Critiq team
- rules
How OWASP and CWE references show up in Critiq rule metadata
Every security rule in @critiq/rules declares metadata.references, OWASP, CWE, and more, so findings stay traceable in the CLI and SARIF output.
How OWASP and CWE references show up in Critiq rule metadata content
When Critiq flags a security issue in your pull request, you should be able to answer a simple question without opening a vendor dashboard: where did this classification come from? The open source rule catalog in @critiq/rules answers that question in the rule file itself. Every security-category rule declares metadata.references, structured citations to CWE, OWASP, CVE, advisory, URL, or internal docs, alongside an explicit metadata.detection.kind: pattern marker that says the rule matches source patterns, not dependency graphs.
This post walks through the references field, why the catalog enforces it, how to read citations in CLI output, and how critiq rules explain surfaces the same metadata before a rule ever runs in CI.
What metadata.references is
Supported reference kinds cover the citations security teams already use in runbooks. CWE entries map findings to MITRE weakness ids. OWASP entries link cheat sheets and project guidance. CVE and advisory entries anchor dependency rules to published identifiers. url and internal kinds cover team docs and stable runbook pages when no public id exists.
Semantic validation enforces shape, not editorial judgment. A CWE id must match the expected pattern (CWE-NNN). URLs must use http or https. The catalog gate goes further for shipped rules: at least one reference must exist, and detection.kind must be pattern for every security.* OSS rule. That combination keeps the pack inspectable without turning rule authoring into a paperwork exercise, one well-chosen CWE plus an OWASP link is enough.
References are transparency metadata. They do not change what the rule matches or how severe the finding is. Semantic validation can warn when a security-category rule omits references entirely, and the @critiq/rules catalog test suite fails CI if any rule whose emit.finding.category starts with security. lacks at least one reference. That gate keeps the public catalog honest: a security finding should always point to a standard or documented rationale you can inspect.
Why transparency fields exist
Static analysis tools have a trust problem when findings feel like opaque scores. Developers merge or ignore alerts they cannot map to something they already know, a CWE entry, an OWASP cheat sheet, a team runbook. Critiq treats references as first-class rule metadata so the same citations travel with the rule from authoring through normalization, check output, and SARIF export.
Transparency fields, references, aliases, rationale, and help-oriented copy, are deliberately excluded from ruleHash. You can improve documentation and add a CWE link without changing finding fingerprints or spamming teams with duplicate alerts. The detection logic and emit block still define behavior; references explain it.
Pattern detection vs vulnerability rules
metadata.detection.kind distinguishes two rule families. pattern (the default for OSS catalog rules) means the rule matches code or adapter facts directly, hardcoded secrets, unsafe HTML, weak TLS settings, and similar SAST-style checks. vulnerability is reserved for Pro dependency and SCA-style rules that require a top-level vulnerability block with package and CVE metadata.
All 240 OSS catalog rules with a security.* finding category now declare metadata.detection.kind: pattern explicitly, even though pattern is the default. That makes the catalog auditable at a glance: nothing in @critiq/rules is pretending to be a CVE package gate. Dependency transparency uses the same references array, but the execution path and DSL shape differ, keep that mental model separate when you read Pro SCA rules later.
Aliases are a sibling transparency field: short external ids like DOK-… for Docker parity rules in the Pro catalog. References and aliases both ship in normalized rule metadata but serve different audiences, aliases for cross-tool lookup, references for standards traceability. OSS security rules today focus on references plus explicit pattern detection; you will not see vulnerability blocks in the public pack.
Example: hardcoded credentials
The shared rule security.no-hardcoded-credentials applies across TypeScript, Go, Java, Python, and other languages. Its metadata block shows the references shape in practice: a CWE id for the weakness and an OWASP cheat sheet for remediation context.
apiVersion: critiq.dev/v1alpha1
kind: Rule
metadata:
id: security.no-hardcoded-credentials
title: Hardcoded API keys or credentials
summary: Source files should not embed credential-like string literals.
rationale: Hardcoded credentials are difficult to rotate and are easily leaked through source control.
detection:
kind: pattern
references:
- kind: cwe
id: CWE-798
title: Use of Hard-coded Credentials
- kind: owasp
title: Secrets Management Cheat Sheet
url: https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html
tags:
- security
- secrets
emit:
finding:
category: security.secrets
severity: criticalThe full rule file lives in the critiq-rules repository under libs/rules/catalog/rules/shared/. You can validate it locally or against any custom rule in your repo with the same schema.
The catalog reference gate
Shipping a rule to @critiq/rules is not only about pattern accuracy. The catalog-rules.spec.ts suite loads every .rule.yaml file, validates it through the core DSL pipeline, and applies an extra policy check on security rules: metadata.references must contain at least one entry, and metadata.detection.kind must be pattern. If you add a new security.secrets or security.injection rule without references, CI fails before the rule reaches npm.
That gate complements semantic validation in critiq-core, which emits a warning when emit.finding.category starts with security. and references are missing. Warnings help custom rule authors; the catalog test makes the warning a hard requirement for the public pack. Premium rules in @critiq-pro follow the same pattern for security-category pattern rules, with additional alias requirements for Docker parity rules.
Reading references in check output
When a rule fires during critiq check, normalized references are copied onto the finding under attributes.references. They appear in JSON output, SARIF help URIs and taxa mappings, and downstream integrations that consume FindingV0. Pretty-printed terminal output focuses on location and message; use JSON or SARIF when you need the citation list programmatically.
critiq check . --format jsonIn the JSON envelope, look for findings whose ruleId matches the rule you care about, then inspect attributes.references. Each object mirrors the YAML: kind, id, title, and url when present. SARIF export maps CWE and CVE ids into taxa and picks a primary helpUri from the first url-bearing reference, or synthesizes one for CVE and CWE ids when no url is declared.
"attributes": {
"ruleHash": "…",
"rationale": "Hardcoded credentials are difficult to rotate…",
"references": [
{
"kind": "cwe",
"id": "CWE-798",
"title": "Use of Hard-coded Credentials"
},
{
"kind": "owasp",
"title": "Secrets Management Cheat Sheet",
"url": "https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html"
}
]
}If you export SARIF for GitHub Advanced Security or an internal ingestion pipeline, references become taxa tags and helpUri values. CWE ids attach to the result; the first url-bearing reference becomes the primary help link. That is the same metadata you saw in YAML, no secondary mapping file maintained by your platform team.
Inspecting a rule with critiq rules explain
Before you rely on a rule in production, inspect what Critiq normalized from the YAML. critiq rules explain runs load, contract validation, semantic validation, and normalization, then prints a readable summary, including a References section listing each citation in terminal-friendly form.
critiq rules explain \
node_modules/@critiq/rules/catalog/rules/shared/security.no-hardcoded-credentials.rule.yaml
critiq rules explain --format json \
.critiq/rules/my-custom-security.rule.yamlPretty output lists references as kind, id, title, and url on separate lines, for example cwe CWE-798 (Use of Hard-coded Credentials) or owasp Secrets Management Cheat Sheet -> https://cheatsheetseries.owasp.org/…. JSON output includes the same data under parsedSummary and normalizedRule.references, which is useful for tooling that generates docs from your rule pack.
Explain also surfaces detection.kind, vulnerability metadata when present, inferred template variables, and semantic diagnostics. If you forget references on a custom security rule, you will see a transparency warning in the Semantic Status section even when contract validation passes. Pair explain with rules validate in CI for custom packs so missing references fail the job before merge.
For catalog rules you do not author yourself, explain is still the fastest way to see which CWE or OWASP page backs a noisy finding. Copy the rule path from JSON output (rulePack plus rule id), run explain on the file under node_modules/@critiq/rules, and read the References section before debating false positive with your team.
Authoring your own rules
When you write a custom rule under .critiq/rules/, treat references as part of the definition of done for any security.* category. Pick at least one authoritative source: the CWE that best describes the weakness, an OWASP guide for the mitigation story, or a stable internal url for team-specific policy. Avoid placeholder urls; semantic validation requires http or https schemes.
- Declare metadata.detection.kind: pattern for source-matching security rules.
- Add metadata.references with at least one cwe, owasp, cve, advisory, url, or internal entry.
- Run critiq rules validate and critiq rules explain on the file before opening a PR.
- Run critiq rules test if you maintain a .spec.yaml fixture alongside the rule.
The Rule DSL reference on docs.critiq.dev documents the full references schema, detection modes, and the vulnerability block shape for future SCA rules. The critiq-rules repository remains the best place to copy vetted patterns from production catalog rules.
What to do next
Run a check on a repo that triggers a catalog security rule, then inspect attributes.references in JSON output. Open the matching .rule.yaml in @critiq/rules and compare the YAML citations to what landed on the finding. If you maintain custom rules, add references before you widen scope, your future self (and your reviewers) will not have to guess which standard you had in mind.
npx critiq check . --format json | jq '.findings[] | select(.ruleId | test("security")) | {ruleId, references: .attributes.references}'
critiq rules explain .critiq/rules/my-rule.rule.yamlMore from the blog

- philosophy
The trust gap in AI-assisted coding, and what inspectable feedback looks like
Developers use AI assistants daily but often distrust review feedback. Inspectable rules, evidence, and local checks close that gap.
Read article
- philosophy
What evidence over vibes means in code review
Review comments should be defensible: tied to a rule, a line, severity, and references, not just confident prose.
Read article
- philosophy
Why we open-sourced the rules engine (and what stays in the catalog)
Critiq ships the rule engine, DSL, and 435+ OSS catalog rules in the open. Here is what you get locally, what Pro adds, and how to inspect rules yourself.
Read article