UNPKG

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
{ "$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" } ] } } }