Custom rule packs
Vulkro ships a large built-in detector set. You can also add your own patterns without waiting on a release. Custom rules are written in YAML and run alongside the built-in detectors on every scan. Use them for organisation-specific concerns: an internal token format, a banned API, a framework convention your team enforces.
Two pattern dialects
Every rule sets kind, which decides how pattern is read:
kind: regex(the default):patternis a Rustregexcrate pattern, applied line by line. It matches raw text, so it is cheap and good for secret formats and simple banned-API checks. The Rustregexcrate has no lookaround and no backreferences.kind: ast:patternis a tree-sitter S-expression query compiled against the language grammar. It matches at the AST level, not the text level, so a comment or a string literal containing the same text does not fire. Anastrule needs exactly ONE entry inlanguages, because node names differ per grammar (JavaScriptcall_expressionversus Pythoncall). To cover several languages, write one rule each.
languages filters which files a rule is applied to, and for ast it also
selects the grammar. There is no file-glob field: scope by language.
Custom rules accept only python (py), javascript (js) and
typescript (ts). Any other value, including go, is a compile error for
that rule: it is skipped with a warning on stderr and contributes nothing.
The scanner itself covers more languages than custom rules can target.
rules:
# regex dialect: an internal token format committed to source.
- id: corp-internal-token
languages: [javascript, typescript, python]
kind: regex
pattern: "intsk_[A-Za-z0-9]{32}"
message: "Internal corp token committed to source"
remediation: "Rotate the token and read it from an environment variable instead."
severity: critical
owasp_category: SecurityMisconfiguration
confidence: high
# ast dialect: a real eval() CALL, not the word "eval" in a comment.
- id: deny-eval-js
languages: [javascript]
kind: ast
pattern: '(call_expression function: (identifier) @fn (#eq? @fn "eval"))'
message: "eval() call: arbitrary code execution sink"
remediation: "Parse the input instead, for example JSON.parse for JSON-shaped data."
severity: high
owasp_category: BrokenObjectPropertyAuth
confidence: medium
Both examples above were run against the scanner before publication and each produces exactly one finding on a matching fixture.
Field reference
| Field | Required | Notes |
|---|---|---|
id | yes | Unique across every rule file a scan loads. Duplicates are a load-time error. |
languages | for ast | List of language names. Filters files; selects the grammar for ast. |
kind | no | regex (default) or ast. |
pattern | yes | A regex string, or a tree-sitter S-expression when kind: ast. A plain string, never a nested block. |
message | yes | Shown on the finding, prefixed with [<id>]. |
remediation | yes | What to do next. |
severity | no | critical, high, medium (default), low, info. |
owasp_category | no | An OwaspCategory variant name, for example BrokenObjectPropertyAuth. Defaults to SecurityMisconfiguration. |
confidence | no | high, medium (default), low. |
cwe | no | A CWE number. |
A rule that fails to parse is reported on stderr and then SKIPPED. A malformed file yields a warning and zero findings rather than an error exit. Check stderr after editing a rule file, and confirm the rule fires on a fixture before trusting a clean result.
Duplicate rule IDs are an error at load time, so keep IDs unique across every file that a scan loads.
Where rules come from
A scan merges rules from these sources:
- A
vulkro-rules.yamlfile at the project root is auto-discovered. No flag is needed. vulkro scan . --rules <PATH>adds an extra rule file or a directory of rule files for that run.- Installed rule packs under
~/.vulkro/rule-packs/<author>/<name>/<version>/participate in every subsequent scan.
Test a rule locally
Point a scan at a small fixture project and iterate until each rule fires where you expect and stays quiet where you do not:
vulkro scan ./test-project --rules ./my-rules.yaml
Keep a directory of positive and negative fixtures next to your rules so you can re-run them as a regression check when you edit a pattern.
Rule packs from the registry
A rule pack is a versioned bundle of rules published under an
<author>/<name> id. Manage packs with vulkro rules:
vulkro rules list # available packs in the registry index
vulkro rules add acme/api-security # fetch, verify signature, install
vulkro rules installed # what is installed locally
vulkro rules verify acme/api-security # re-check the on-disk signature
vulkro rules remove acme/api-security # uninstall every version
vulkro rules update # refresh the registry index
Packs are signed, and add installs a pack only after its signature verifies
against the trust roots the binary ships with. verify re-checks an installed
pack at any time. Pin a version with vulkro rules add acme/[email protected];
the default is the latest published version.
Reuse existing Semgrep rules
If you already maintain Semgrep rules, convert one into Vulkro's format:
vulkro rules import-semgrep my-semgrep-rules.yaml
The importer writes a vulkro-rules.yaml next to the input, which a later
vulkro scan auto-discovers with no extra flag. Translation is best-effort:
simple patterns become regexes, and compound shapes (pattern-either,
pattern-not, patterns:) are skipped with a warning per rule, so review the
output before relying on it. Exit code is 0 on success, 2 on a read or parse
error.
Related
vulkro rules- the full command reference.- Suppressions - silence a finding you have accepted.
- Air-gap: signing and trust roots - the same trust-root model the CVE bundle uses.