fallow
Version:
Codebase intelligence for TypeScript and JavaScript: health, complexity, duplication, architecture, styling drift, and unused code from one graph. CLI, LSP, and MCP server. Zero config for over 100 frameworks.
265 lines (264 loc) • 12 kB
JSON
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "RulePackDef",
"description": "A declarative rule pack loaded from a standalone JSON or JSONC file listed\nin the `rulePacks` config key.\n\nRule packs are pure data: loading a pack never executes project code. They\nencode project-specific policy (banned calls, banned imports, and\ncatalogue-backed banned effects) evaluated over fallow's static extraction\ndata, reporting as `policy-violation`\nfindings.\n\n```jsonc\n{\n \"$schema\": \"https://raw.githubusercontent.com/fallow-rs/fallow/main/rule-pack-schema.json\",\n \"version\": 1,\n \"name\": \"team-policy\",\n \"description\": \"House rules for the platform team\",\n \"rules\": [\n {\n \"id\": \"no-child-process\",\n \"kind\": \"banned-call\",\n \"callees\": [\"child_process.*\"],\n \"message\": \"Use the sandboxed runner instead.\",\n \"severity\": \"error\"\n },\n {\n \"id\": \"no-network\",\n \"kind\": \"banned-effect\",\n \"effects\": [\"network\"],\n \"message\": \"Keep this package side-effect free.\"\n },\n {\n \"id\": \"no-moment\",\n \"kind\": \"banned-import\",\n \"specifiers\": [\"moment\"],\n \"message\": \"Use date-fns.\"\n }\n ]\n}\n```",
"type": "object",
"properties": {
"version": {
"description": "Pack format version. Must be `1`; the field exists so future rule\nkinds can be added without breaking older fallow builds silently.",
"type": "integer",
"format": "uint32",
"minimum": 0
},
"name": {
"description": "Pack name, unique across all loaded packs. Must use only ASCII\nletters, digits, `.`, `_`, and `-` so `\"<pack>/<id>\"` is unambiguous in\noutput, baselines, and scoped suppression comments.",
"type": "string"
},
"description": {
"description": "Optional human description of the pack's intent.",
"type": [
"string",
"null"
]
},
"rules": {
"description": "The policy rules this pack enforces. Must be non-empty: an empty pack\nwould silently enforce nothing.",
"type": "array",
"items": {
"$ref": "#/$defs/RulePackRule"
}
}
},
"additionalProperties": false,
"required": [
"version",
"name",
"rules"
],
"$defs": {
"RulePackRule": {
"description": "One declarative policy rule inside a rule pack.\n\n`callees` applies only to `banned-call` rules; `specifiers` and\n`ignoreTypeOnly` apply only to `banned-import` rules; `effects` applies\nonly to `banned-effect` rules; `exports` applies only to `banned-export`\nrules. `allowedFiles` and `proofKinds` apply only to `gdp-proof-producer`\nrules. `zones` can scope any rule kind to files classified into one of the\nnamed boundary zones. Setting a field on the wrong kind is a load error\n(fail loud, never silently ignore policy).",
"type": "object",
"properties": {
"id": {
"description": "Rule id, unique within the pack. Must use only ASCII letters, digits,\n`.`, `_`, and `-` so `\"<pack>/<id>\"` is unambiguous in output,\nbaselines, and scoped suppression comments.",
"type": "string"
},
"kind": {
"description": "Which check this rule performs.",
"$ref": "#/$defs/RulePackRuleKind"
},
"callees": {
"description": "Callee patterns to ban (`banned-call` only). Matching is segment-aware\nand import-resolved, identical to `boundaries.calls.forbidden`:\n`child_process.*` covers `import { exec } from \"node:child_process\"`,\nthe bare specifier, and namespace/default imports; `fetch` matches only\nthe global `fetch`; a leading `*.member` matches any object.",
"type": "array",
"items": {
"type": "string"
}
},
"specifiers": {
"description": "Import specifiers to ban (`banned-import` only). Matched segment-aware\nagainst the RAW specifier: `moment` covers `moment` and\n`moment/locale/nl` but not `moment-timezone`. A trailing `/*` form,\nsuch as `@org/ui/*`, matches subpaths only (`@org/ui/internal`) and\nnot the package root (`@org/ui`). Aliased or rewritten specifiers\n(e.g. `npm:moment`) are not matched.",
"type": "array",
"items": {
"type": "string"
}
},
"effects": {
"description": "Effect classes to ban (`banned-effect` only). Effects are derived from\n`security_matchers.toml` catalogue rows and matched against captured\ncall sites after import-resolution canonicalization.",
"type": "array",
"items": {
"$ref": "#/$defs/EffectKind"
}
},
"exports": {
"description": "Export names to ban (`banned-export` only). `\"default\"` matches the\ndefault export; any other entry matches an exported name exactly; a\nsingle trailing `*` makes it a prefix match (`internal*`). No other\nglob syntax is supported. Re-exports are out of scope for this rule.",
"type": "array",
"items": {
"type": "string"
}
},
"allowedFiles": {
"description": "Project-root-relative globs allowed to call `@gdp-ts/core.defineProof`\n(`gdp-proof-producer` only). At least one pattern is required.\nA single `*` stays within one path segment; use `**` to match across directories.",
"type": "array",
"items": {
"type": "string"
}
},
"proofKinds": {
"description": "Exact literal proof labels checked by a `gdp-proof-producer` rule.\nEmpty or absent checks every recognized factory call, including calls\nwith dynamic labels. A nonempty list checks only static string labels.",
"type": "array",
"items": {
"type": "string"
}
},
"ignoreTypeOnly": {
"description": "When `true`, type-only imports (`import type ...` and type-only\nre-exports) are ignored by `banned-import`; type-only exports are\nignored by `banned-export`. Defaults to `false`: type-only sites are\nflagged too.",
"type": "boolean"
},
"files": {
"description": "Optional include globs (project-root-relative). Empty or absent means\nthe rule applies to every analyzed file.\nA single `*` stays within one path segment; use `**` to match across directories.",
"type": "array",
"items": {
"type": "string"
}
},
"exclude": {
"description": "Optional exclude globs (project-root-relative), applied after `files`.\nA single `*` stays within one path segment; use `**` to match across directories.",
"type": "array",
"items": {
"type": "string"
}
},
"zones": {
"description": "Optional boundary zones this rule applies to. Empty or absent means the\nrule applies regardless of zone; non-empty values require matching\nconfigured boundaries and combine with `files`/`exclude` as AND.",
"type": "array",
"items": {
"type": "string"
}
},
"message": {
"description": "Author-provided message naming the sanctioned alternative. Rendered\nnext to each finding.",
"type": [
"string",
"null"
]
},
"severity": {
"description": "Per-rule severity overriding the `rules.\"policy-violation\"` master.\n`off` disables this rule. When the master itself is `off`, the whole\nevaluator is disabled and per-rule severity cannot resurrect it.",
"anyOf": [
{
"$ref": "#/$defs/Severity"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false,
"required": [
"id",
"kind"
]
},
"RulePackRuleKind": {
"description": "Which check a rule-pack rule performs.",
"oneOf": [
{
"description": "Ban call sites whose callee path matches one of `callees`.",
"type": "string",
"const": "banned-call"
},
{
"description": "Ban imports and re-exports whose raw specifier matches one of\n`specifiers`.",
"type": "string",
"const": "banned-import"
},
{
"description": "Ban call sites whose catalogue-derived effect matches one of `effects`.",
"type": "string",
"const": "banned-effect"
},
{
"description": "Ban exported names that match one of `exports`.",
"type": "string",
"const": "banned-export"
},
{
"description": "Restrict gdp-ts proof factory calls to explicitly allowed modules.",
"type": "string",
"const": "gdp-proof-producer"
}
]
},
"EffectKind": {
"description": "Internal side-effect taxonomy derived from security catalogue rows.",
"oneOf": [
{
"description": "No observable side effect.",
"type": "string",
"const": "pure"
},
{
"description": "Structured-data read/query evaluation (XML external entities, XPath).",
"type": "string",
"const": "read"
},
{
"description": "In-memory state mutation (mass assignment, prototype pollution).",
"type": "string",
"const": "write"
},
{
"description": "Network I/O (HTTP clients, request-forgery sinks).",
"type": "string",
"const": "network"
},
{
"description": "Filesystem access (path traversal, file permission changes).",
"type": "string",
"const": "storage"
},
{
"description": "In-process code evaluation (`eval`, `Function`, string timers).",
"type": "string",
"const": "process"
},
{
"description": "OS shell command execution.",
"type": "string",
"const": "shell"
},
{
"description": "Cryptographic primitives (weak algorithm or key usage).",
"type": "string",
"const": "crypto"
},
{
"description": "Random-number generation in security-sensitive contexts.",
"type": "string",
"const": "randomness"
},
{
"description": "DOM injection (`innerHTML`-style XSS sinks).",
"type": "string",
"const": "dom"
},
{
"description": "Database query execution (SQL/NoSQL injection sinks).",
"type": "string",
"const": "database"
},
{
"description": "Callback invoked by a framework rather than a direct effect.",
"type": "string",
"const": "framework-callback"
},
{
"description": "Effect the catalogue cannot classify.",
"type": "string",
"const": "unknown"
}
]
},
"Severity": {
"description": "Severity level for rules.\n\nControls whether an issue type sets a non-zero exit code (`error`), is\nreported without an effect on the exit code (`warn`), or is suppressed\nentirely (`off`). `--fail-on-issues` promotes `warn` to `error`. The\ndead-code count gate of the GitHub Action counts `warn` findings unless its\n`fail-on-issues` input is `false`.",
"oneOf": [
{
"description": "Report and set a non-zero exit code.",
"type": "string",
"const": "error"
},
{
"description": "Report without an effect on the exit code.",
"type": "string",
"const": "warn"
},
{
"description": "Don't detect or report.",
"type": "string",
"const": "off"
}
]
}
}
}