fallow 3.35.0 → 3.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/capabilities.json +25 -9
- package/issue-registry.json +8 -4
- package/package.json +14 -11
- package/rule-pack-schema.json +264 -0
- package/schema.json +43 -17
- package/skills/fallow/SKILL.md +2 -2
- package/skills/fallow/references/cli-reference.md +13 -12
- package/skills/fallow/references/gotchas.md +3 -1
- package/skills/fallow/references/patterns.md +2 -2
- package/types/output-contract.d.ts +176 -59
- package/scripts/lazy-verify.test.js +0 -564
- package/scripts/platform-package.test.js +0 -26
- package/scripts/run-binary.test.js +0 -293
- package/scripts/sentinel-path.test.js +0 -240
- package/scripts/verify-binary.test.js +0 -809
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ npx fallow fix --dry-run # Preview automatic cleanup
|
|
|
28
28
|
|
|
29
29
|
## Output and exit codes
|
|
30
30
|
|
|
31
|
-
Add `--format json --quiet` to any command for one typed JSON document on stdout. Exit code 1 means findings, not failure; 0
|
|
31
|
+
Add `--format json --quiet` to any command for one typed JSON document on stdout. Exit code 1 means findings, not failure; 0 means no enforced gate failed (or an audit pass or warn verdict). Bare `fallow` in a machine format exits 0 on findings unless you pass `--fail-on-issues`; 2 is a validation or runtime error, reported as a JSON error envelope rather than a stack trace. License, coverage setup, network, and security-gate workflows use additional documented codes; read `fallow schema.exit_codes` instead of suppressing the process status.
|
|
32
32
|
|
|
33
33
|
Parsing the output in TypeScript? Import the typed shapes, version-pinned to the CLI you install:
|
|
34
34
|
|
package/capabilities.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fallow",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.37.0",
|
|
4
4
|
"manifest_version": "1",
|
|
5
5
|
"description": "Codebase analyzer for TypeScript/JavaScript: unused code, circular dependencies, code duplication, complexity hotspots, and architecture boundary violations",
|
|
6
6
|
"global_flags": [
|
|
@@ -301,7 +301,7 @@
|
|
|
301
301
|
"name": "--fail-on-issues",
|
|
302
302
|
"type": "bool",
|
|
303
303
|
"required": false,
|
|
304
|
-
"description": "Exit
|
|
304
|
+
"description": "Exit 1 on issues, also where the default is 0: bare json, sarif, codeclimate and dupes",
|
|
305
305
|
"possible_values": [
|
|
306
306
|
"true",
|
|
307
307
|
"false"
|
|
@@ -1289,6 +1289,16 @@
|
|
|
1289
1289
|
"false"
|
|
1290
1290
|
]
|
|
1291
1291
|
},
|
|
1292
|
+
{
|
|
1293
|
+
"name": "--resume",
|
|
1294
|
+
"type": "bool",
|
|
1295
|
+
"required": false,
|
|
1296
|
+
"description": "Continue an interrupted or partial run: reuse the killed and confirmed survived mutants in its journal and run only the others. A journal of other inputs exits 2",
|
|
1297
|
+
"possible_values": [
|
|
1298
|
+
"true",
|
|
1299
|
+
"false"
|
|
1300
|
+
]
|
|
1301
|
+
},
|
|
1292
1302
|
{
|
|
1293
1303
|
"name": "paths",
|
|
1294
1304
|
"type": "string",
|
|
@@ -4536,9 +4546,11 @@
|
|
|
4536
4546
|
"sarif_rule_ids": [
|
|
4537
4547
|
"fallow/thin-wrapper"
|
|
4538
4548
|
],
|
|
4539
|
-
"codeclimate_check_names":
|
|
4549
|
+
"codeclimate_check_names": [
|
|
4550
|
+
"fallow/thin-wrapper"
|
|
4551
|
+
],
|
|
4540
4552
|
"ts_alias": null,
|
|
4541
|
-
"counts_in_total":
|
|
4553
|
+
"counts_in_total": true,
|
|
4542
4554
|
"fixable": false,
|
|
4543
4555
|
"suppressible": true,
|
|
4544
4556
|
"suppress_comment": "// fallow-ignore-next-line thin-wrapper",
|
|
@@ -4570,9 +4582,11 @@
|
|
|
4570
4582
|
"sarif_rule_ids": [
|
|
4571
4583
|
"fallow/duplicate-prop-shape"
|
|
4572
4584
|
],
|
|
4573
|
-
"codeclimate_check_names":
|
|
4585
|
+
"codeclimate_check_names": [
|
|
4586
|
+
"fallow/duplicate-prop-shape"
|
|
4587
|
+
],
|
|
4574
4588
|
"ts_alias": null,
|
|
4575
|
-
"counts_in_total":
|
|
4589
|
+
"counts_in_total": true,
|
|
4576
4590
|
"fixable": false,
|
|
4577
4591
|
"suppressible": true,
|
|
4578
4592
|
"suppress_comment": "// fallow-ignore-next-line duplicate-prop-shape",
|
|
@@ -6995,8 +7009,8 @@
|
|
|
6995
7009
|
"github-summary"
|
|
6996
7010
|
],
|
|
6997
7011
|
"exit_codes": {
|
|
6998
|
-
"0": "Success
|
|
6999
|
-
"1": "
|
|
7012
|
+
"0": "Success: no enforced gate failed. Bare fallow in a machine format (json, sarif, codeclimate, the GitHub formats, the comment and review formats) does not enforce findings without --fail-on-issues, so it exits 0 on findings. Read gate_outcomes: status \"fail\" with enforced false is a failing gate that did not set the exit code",
|
|
7013
|
+
"1": "An enforced gate failed, for example error-severity findings (per rules config, or --fail-on-issues promotes warn to error). This is an analysis result, not a tool failure",
|
|
7000
7014
|
"2": "Error (invalid config, invalid input, etc.). When --format json is active, errors are emitted as structured JSON on stdout: {\"error\": true, \"message\": \"...\", \"exit_code\": 2}",
|
|
7001
7015
|
"3": "Requested resource unavailable: config --path found no config, or a license is missing, invalid, or beyond its offline hard-fail window",
|
|
7002
7016
|
"4": "Runtime coverage sidecar is unavailable, unverifiable, protocol-incompatible, or terminated unexpectedly",
|
|
@@ -7034,6 +7048,7 @@
|
|
|
7034
7048
|
"FALLOW_MAX_FILE_SIZE": "Per-file size ceiling in megabytes for source discovery (default 5; 0 = no limit). CLI --max-file-size flag overrides this.",
|
|
7035
7049
|
"FALLOW_TYPE_AWARE": "Enable or disable TypeScript semantic (type-aware) analysis for the run (true/false/1/0/yes/no/on/off). Precedence: --type-aware/--no-type-aware CLI flags, then FALLOW_TYPE_AWARE, then the audit.typeAware config field, then typeAware.enabled.",
|
|
7036
7050
|
"FALLOW_TYPE_AWARE_TIMEOUT_SECS": "Wall clock in seconds allowed for one TypeScript semantic sidecar request (default 120; unset, 0, or a non-numeric value keeps the default). Raise it for very large TypeScript programs, where a semantic query scans the whole program.",
|
|
7051
|
+
"FALLOW_TYPE_AWARE_BACKEND": "Experimental. Type-checker backend for the TypeScript semantic sidecar: typescript-go (default) or tsrs. The tsrs backend needs the exact pinned tsrs npm package where fallow-type-aware can resolve it. An unknown value makes the type-aware pass unavailable.",
|
|
7037
7052
|
"FALLOW_TYPE_AWARE_BIN": "Trusted executable override for the TypeScript semantic refinement sidecar used by dead-code --type-aware. Relative paths resolve from the caller's working directory before --root is applied; project node_modules and PATH are intentionally not searched. Default: sibling of the active Fallow executable.",
|
|
7038
7053
|
"FALLOW_AUDIT_BASE": "Pins the fallow audit comparison base ref when no --base/--changed-since is passed (e.g. upstream/main).",
|
|
7039
7054
|
"FALLOW_AUDIT_CACHE_MAX_AGE_DAYS": "GC threshold in days for reusable audit base-snapshot caches (default 30; 0 disables the sweep).",
|
|
@@ -8280,7 +8295,7 @@
|
|
|
8280
8295
|
]
|
|
8281
8296
|
},
|
|
8282
8297
|
"plugins": {
|
|
8283
|
-
"count":
|
|
8298
|
+
"count": 149,
|
|
8284
8299
|
"note": "Built-in framework plugins, auto-activated when their enabler dependency is present; run fallow list --plugins for the set active in a specific project",
|
|
8285
8300
|
"names": [
|
|
8286
8301
|
"nextjs",
|
|
@@ -8421,6 +8436,7 @@
|
|
|
8421
8436
|
"nodemon",
|
|
8422
8437
|
"pm2",
|
|
8423
8438
|
"dependency-cruiser",
|
|
8439
|
+
"dotenv",
|
|
8424
8440
|
"wuchale",
|
|
8425
8441
|
"varlock",
|
|
8426
8442
|
"pnpm",
|
package/issue-registry.json
CHANGED
|
@@ -1939,9 +1939,11 @@
|
|
|
1939
1939
|
"sarif_rule_ids": [
|
|
1940
1940
|
"fallow/thin-wrapper"
|
|
1941
1941
|
],
|
|
1942
|
-
"codeclimate_check_names":
|
|
1942
|
+
"codeclimate_check_names": [
|
|
1943
|
+
"fallow/thin-wrapper"
|
|
1944
|
+
],
|
|
1943
1945
|
"ts_alias": null,
|
|
1944
|
-
"counts_in_total":
|
|
1946
|
+
"counts_in_total": true,
|
|
1945
1947
|
"fixable": false,
|
|
1946
1948
|
"suppressible": true,
|
|
1947
1949
|
"suppress_comment": "// fallow-ignore-next-line thin-wrapper",
|
|
@@ -1973,9 +1975,11 @@
|
|
|
1973
1975
|
"sarif_rule_ids": [
|
|
1974
1976
|
"fallow/duplicate-prop-shape"
|
|
1975
1977
|
],
|
|
1976
|
-
"codeclimate_check_names":
|
|
1978
|
+
"codeclimate_check_names": [
|
|
1979
|
+
"fallow/duplicate-prop-shape"
|
|
1980
|
+
],
|
|
1977
1981
|
"ts_alias": null,
|
|
1978
|
-
"counts_in_total":
|
|
1982
|
+
"counts_in_total": true,
|
|
1979
1983
|
"fixable": false,
|
|
1980
1984
|
"suppressible": true,
|
|
1981
1985
|
"suppress_comment": "// fallow-ignore-next-line duplicate-prop-shape",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fallow",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.37.0",
|
|
4
4
|
"mcpName": "io.github.fallow-rs/fallow",
|
|
5
5
|
"description": "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.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"./scripts/*": "./scripts/*",
|
|
55
55
|
"./skills/*": "./skills/*",
|
|
56
56
|
"./schema.json": "./schema.json",
|
|
57
|
+
"./rule-pack-schema.json": "./rule-pack-schema.json",
|
|
57
58
|
"./capabilities.json": "./capabilities.json",
|
|
58
59
|
"./issue-registry.json": "./issue-registry.json"
|
|
59
60
|
},
|
|
@@ -67,11 +68,13 @@
|
|
|
67
68
|
"files": [
|
|
68
69
|
"bin",
|
|
69
70
|
"scripts",
|
|
71
|
+
"!scripts/*.test.js",
|
|
70
72
|
"skills",
|
|
71
73
|
"!skills/_artifacts",
|
|
72
74
|
"types",
|
|
73
75
|
"README.md",
|
|
74
76
|
"schema.json",
|
|
77
|
+
"rule-pack-schema.json",
|
|
75
78
|
"capabilities.json",
|
|
76
79
|
"issue-registry.json"
|
|
77
80
|
],
|
|
@@ -86,17 +89,17 @@
|
|
|
86
89
|
"detect-libc": "2.1.2"
|
|
87
90
|
},
|
|
88
91
|
"devDependencies": {
|
|
89
|
-
"@tanstack/intent": "0.5.
|
|
92
|
+
"@tanstack/intent": "0.5.6"
|
|
90
93
|
},
|
|
91
94
|
"optionalDependencies": {
|
|
92
|
-
"@fallow-cli/darwin-arm64": "3.
|
|
93
|
-
"@fallow-cli/darwin-x64": "3.
|
|
94
|
-
"@fallow-cli/linux-x64-gnu": "3.
|
|
95
|
-
"@fallow-cli/linux-arm64-gnu": "3.
|
|
96
|
-
"@fallow-cli/linux-x64-musl": "3.
|
|
97
|
-
"@fallow-cli/linux-arm64-musl": "3.
|
|
98
|
-
"@fallow-cli/win32-arm64-msvc": "3.
|
|
99
|
-
"@fallow-cli/win32-x64-msvc": "3.
|
|
100
|
-
"fallow-type-aware": "3.
|
|
95
|
+
"@fallow-cli/darwin-arm64": "3.37.0",
|
|
96
|
+
"@fallow-cli/darwin-x64": "3.37.0",
|
|
97
|
+
"@fallow-cli/linux-x64-gnu": "3.37.0",
|
|
98
|
+
"@fallow-cli/linux-arm64-gnu": "3.37.0",
|
|
99
|
+
"@fallow-cli/linux-x64-musl": "3.37.0",
|
|
100
|
+
"@fallow-cli/linux-arm64-musl": "3.37.0",
|
|
101
|
+
"@fallow-cli/win32-arm64-msvc": "3.37.0",
|
|
102
|
+
"@fallow-cli/win32-x64-msvc": "3.37.0",
|
|
103
|
+
"fallow-type-aware": "3.37.0"
|
|
101
104
|
}
|
|
102
105
|
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "RulePackDef",
|
|
4
|
+
"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```",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"version": {
|
|
8
|
+
"description": "Pack format version. Must be `1`; the field exists so future rule\nkinds can be added without breaking older fallow builds silently.",
|
|
9
|
+
"type": "integer",
|
|
10
|
+
"format": "uint32",
|
|
11
|
+
"minimum": 0
|
|
12
|
+
},
|
|
13
|
+
"name": {
|
|
14
|
+
"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.",
|
|
15
|
+
"type": "string"
|
|
16
|
+
},
|
|
17
|
+
"description": {
|
|
18
|
+
"description": "Optional human description of the pack's intent.",
|
|
19
|
+
"type": [
|
|
20
|
+
"string",
|
|
21
|
+
"null"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"rules": {
|
|
25
|
+
"description": "The policy rules this pack enforces. Must be non-empty: an empty pack\nwould silently enforce nothing.",
|
|
26
|
+
"type": "array",
|
|
27
|
+
"items": {
|
|
28
|
+
"$ref": "#/$defs/RulePackRule"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"additionalProperties": false,
|
|
33
|
+
"required": [
|
|
34
|
+
"version",
|
|
35
|
+
"name",
|
|
36
|
+
"rules"
|
|
37
|
+
],
|
|
38
|
+
"$defs": {
|
|
39
|
+
"RulePackRule": {
|
|
40
|
+
"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).",
|
|
41
|
+
"type": "object",
|
|
42
|
+
"properties": {
|
|
43
|
+
"id": {
|
|
44
|
+
"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.",
|
|
45
|
+
"type": "string"
|
|
46
|
+
},
|
|
47
|
+
"kind": {
|
|
48
|
+
"description": "Which check this rule performs.",
|
|
49
|
+
"$ref": "#/$defs/RulePackRuleKind"
|
|
50
|
+
},
|
|
51
|
+
"callees": {
|
|
52
|
+
"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.",
|
|
53
|
+
"type": "array",
|
|
54
|
+
"items": {
|
|
55
|
+
"type": "string"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"specifiers": {
|
|
59
|
+
"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.",
|
|
60
|
+
"type": "array",
|
|
61
|
+
"items": {
|
|
62
|
+
"type": "string"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"effects": {
|
|
66
|
+
"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.",
|
|
67
|
+
"type": "array",
|
|
68
|
+
"items": {
|
|
69
|
+
"$ref": "#/$defs/EffectKind"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"exports": {
|
|
73
|
+
"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.",
|
|
74
|
+
"type": "array",
|
|
75
|
+
"items": {
|
|
76
|
+
"type": "string"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"allowedFiles": {
|
|
80
|
+
"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.",
|
|
81
|
+
"type": "array",
|
|
82
|
+
"items": {
|
|
83
|
+
"type": "string"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"proofKinds": {
|
|
87
|
+
"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.",
|
|
88
|
+
"type": "array",
|
|
89
|
+
"items": {
|
|
90
|
+
"type": "string"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"ignoreTypeOnly": {
|
|
94
|
+
"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.",
|
|
95
|
+
"type": "boolean"
|
|
96
|
+
},
|
|
97
|
+
"files": {
|
|
98
|
+
"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.",
|
|
99
|
+
"type": "array",
|
|
100
|
+
"items": {
|
|
101
|
+
"type": "string"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"exclude": {
|
|
105
|
+
"description": "Optional exclude globs (project-root-relative), applied after `files`.\nA single `*` stays within one path segment; use `**` to match across directories.",
|
|
106
|
+
"type": "array",
|
|
107
|
+
"items": {
|
|
108
|
+
"type": "string"
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"zones": {
|
|
112
|
+
"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.",
|
|
113
|
+
"type": "array",
|
|
114
|
+
"items": {
|
|
115
|
+
"type": "string"
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"message": {
|
|
119
|
+
"description": "Author-provided message naming the sanctioned alternative. Rendered\nnext to each finding.",
|
|
120
|
+
"type": [
|
|
121
|
+
"string",
|
|
122
|
+
"null"
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
"severity": {
|
|
126
|
+
"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.",
|
|
127
|
+
"anyOf": [
|
|
128
|
+
{
|
|
129
|
+
"$ref": "#/$defs/Severity"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"type": "null"
|
|
133
|
+
}
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"additionalProperties": false,
|
|
138
|
+
"required": [
|
|
139
|
+
"id",
|
|
140
|
+
"kind"
|
|
141
|
+
]
|
|
142
|
+
},
|
|
143
|
+
"RulePackRuleKind": {
|
|
144
|
+
"description": "Which check a rule-pack rule performs.",
|
|
145
|
+
"oneOf": [
|
|
146
|
+
{
|
|
147
|
+
"description": "Ban call sites whose callee path matches one of `callees`.",
|
|
148
|
+
"type": "string",
|
|
149
|
+
"const": "banned-call"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"description": "Ban imports and re-exports whose raw specifier matches one of\n`specifiers`.",
|
|
153
|
+
"type": "string",
|
|
154
|
+
"const": "banned-import"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"description": "Ban call sites whose catalogue-derived effect matches one of `effects`.",
|
|
158
|
+
"type": "string",
|
|
159
|
+
"const": "banned-effect"
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"description": "Ban exported names that match one of `exports`.",
|
|
163
|
+
"type": "string",
|
|
164
|
+
"const": "banned-export"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"description": "Restrict gdp-ts proof factory calls to explicitly allowed modules.",
|
|
168
|
+
"type": "string",
|
|
169
|
+
"const": "gdp-proof-producer"
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
},
|
|
173
|
+
"EffectKind": {
|
|
174
|
+
"description": "Internal side-effect taxonomy derived from security catalogue rows.",
|
|
175
|
+
"oneOf": [
|
|
176
|
+
{
|
|
177
|
+
"description": "No observable side effect.",
|
|
178
|
+
"type": "string",
|
|
179
|
+
"const": "pure"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"description": "Structured-data read/query evaluation (XML external entities, XPath).",
|
|
183
|
+
"type": "string",
|
|
184
|
+
"const": "read"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"description": "In-memory state mutation (mass assignment, prototype pollution).",
|
|
188
|
+
"type": "string",
|
|
189
|
+
"const": "write"
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"description": "Network I/O (HTTP clients, request-forgery sinks).",
|
|
193
|
+
"type": "string",
|
|
194
|
+
"const": "network"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"description": "Filesystem access (path traversal, file permission changes).",
|
|
198
|
+
"type": "string",
|
|
199
|
+
"const": "storage"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"description": "In-process code evaluation (`eval`, `Function`, string timers).",
|
|
203
|
+
"type": "string",
|
|
204
|
+
"const": "process"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"description": "OS shell command execution.",
|
|
208
|
+
"type": "string",
|
|
209
|
+
"const": "shell"
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"description": "Cryptographic primitives (weak algorithm or key usage).",
|
|
213
|
+
"type": "string",
|
|
214
|
+
"const": "crypto"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"description": "Random-number generation in security-sensitive contexts.",
|
|
218
|
+
"type": "string",
|
|
219
|
+
"const": "randomness"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"description": "DOM injection (`innerHTML`-style XSS sinks).",
|
|
223
|
+
"type": "string",
|
|
224
|
+
"const": "dom"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"description": "Database query execution (SQL/NoSQL injection sinks).",
|
|
228
|
+
"type": "string",
|
|
229
|
+
"const": "database"
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"description": "Callback invoked by a framework rather than a direct effect.",
|
|
233
|
+
"type": "string",
|
|
234
|
+
"const": "framework-callback"
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"description": "Effect the catalogue cannot classify.",
|
|
238
|
+
"type": "string",
|
|
239
|
+
"const": "unknown"
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
},
|
|
243
|
+
"Severity": {
|
|
244
|
+
"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`.",
|
|
245
|
+
"oneOf": [
|
|
246
|
+
{
|
|
247
|
+
"description": "Report and set a non-zero exit code.",
|
|
248
|
+
"type": "string",
|
|
249
|
+
"const": "error"
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
"description": "Report without an effect on the exit code.",
|
|
253
|
+
"type": "string",
|
|
254
|
+
"const": "warn"
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"description": "Don't detect or report.",
|
|
258
|
+
"type": "string",
|
|
259
|
+
"const": "off"
|
|
260
|
+
}
|
|
261
|
+
]
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
package/schema.json
CHANGED
|
@@ -357,6 +357,13 @@
|
|
|
357
357
|
},
|
|
358
358
|
"default": []
|
|
359
359
|
},
|
|
360
|
+
"markupExtensions": {
|
|
361
|
+
"description": "An array of file extensions, without the leading dot, for server-side template files whose text counts as CSS class references for the `unreferenced-css-class` finding of `fallow health --css`. These files are read as text only: they do not join source discovery or the module graph, so they never become unused files and their imports are not followed. Templates are more lenient than Markdown: the whole text of a template file counts as a dynamic class reference, so any class-like word in it counts as used, also a common word such as `hidden` or `title` in prose or a template tag. A Markdown file joins the reference text only when it has dynamic class syntax. The extensions `liquid`, `njk`, `hbs`, `handlebars`, `twig`, and `blade.php` are always read; set this (e.g. `[\"tpl\", \"erb\"]`) for other template languages. An extension matches the end of a file name, ignoring case, so `blade.php` matches `welcome.blade.php`. Values may contain only letters, digits, `.`, `-`, and `_`. A value whose last segment is a script, stylesheet, data or asset extension (`js`, `ts`, `css`, `json`, `svg`, `xml`, `map`, and similar, so also `module.css` or `d.ts`), or a compound value that ends in a markup extension that fallow already reads (`page.html`), is a config error. The walk uses the ignore files of source discovery (`.gitignore`, `.ignore`), skips `ignorePatterns`, `health.ignore`, `node_modules`, `vendor`, and build output directories, reads only regular files, and skips files over the `--max-file-size` limit. In `extends`, a child list replaces the parent list.",
|
|
362
|
+
"type": "array",
|
|
363
|
+
"items": {
|
|
364
|
+
"type": "string"
|
|
365
|
+
}
|
|
366
|
+
},
|
|
360
367
|
"overrides": {
|
|
361
368
|
"description": "An ordered list of per-file rule-severity overrides: each entry re-severities specific analysis rules for files its globs match, layered on top of the top-level `rules` defaults. Set to relax or tighten rules for a subset of paths (e.g. downgrade unused-exports to warn under a generated directory); each entry has `files` (glob-pattern array) and `rules` (a partial per-rule severity map of error/warn/off). Entries apply in list order and a file matched by several entries takes every matching entry's overrides (later entries win on conflict); inter-file rules (duplicate-exports, circular-dependencies, re-export-cycle) have no effect in an override (fallow warns during analysis and points to the right mechanism: top-level `ignoreExports` for duplicate-exports, a file-level `// fallow-ignore-file` comment for the others). A single `*` stays within one path segment; use `**` to match across directories.",
|
|
362
369
|
"type": "array",
|
|
@@ -416,7 +423,7 @@
|
|
|
416
423
|
"default": false
|
|
417
424
|
},
|
|
418
425
|
"failOnParseError": {
|
|
419
|
-
"description": "When true, the run fails with exit code 1 if fallow could not parse a source file cleanly (a `source-parse-degraded` entry in `workspace_diagnostics[]`), and the `parse-error` entry of `gate_outcomes` names each such file. Boolean, defaults to false, so a file the parser rejects only warns. Applies to `fallow dead-code`, `fallow health`, `fallow audit` and the bare `fallow` run; the CLI flag `--fail-on-parse-error` arms the same gate for one run. Older fallow versions reject this key as unknown, so pair it with `minimumVersion` set to the release that added it.",
|
|
426
|
+
"description": "When true, the run fails with exit code 1 if fallow could not parse a source file cleanly (a `source-parse-degraded` or `source-nesting-too-deep` entry in `workspace_diagnostics[]`), and the `parse-error` entry of `gate_outcomes` names each such file. Boolean, defaults to false, so a file the parser rejects only warns. Applies to `fallow dead-code`, `fallow health`, `fallow audit` and the bare `fallow` run; the CLI flag `--fail-on-parse-error` arms the same gate for one run. Older fallow versions reject this key as unknown, so pair it with `minimumVersion` set to the release that added it.",
|
|
420
427
|
"type": "boolean",
|
|
421
428
|
"default": false
|
|
422
429
|
},
|
|
@@ -538,13 +545,13 @@
|
|
|
538
545
|
"description": "Plugin detected if this package is in dependencies.",
|
|
539
546
|
"type": "object",
|
|
540
547
|
"properties": {
|
|
541
|
-
"package": {
|
|
542
|
-
"description": "Exact package name looked up in the project's declared dependencies.",
|
|
543
|
-
"type": "string"
|
|
544
|
-
},
|
|
545
548
|
"type": {
|
|
546
549
|
"type": "string",
|
|
547
550
|
"const": "dependency"
|
|
551
|
+
},
|
|
552
|
+
"package": {
|
|
553
|
+
"description": "Exact package name looked up in the project's declared dependencies.",
|
|
554
|
+
"type": "string"
|
|
548
555
|
}
|
|
549
556
|
},
|
|
550
557
|
"required": [
|
|
@@ -556,13 +563,13 @@
|
|
|
556
563
|
"description": "Plugin detected if this file pattern matches.",
|
|
557
564
|
"type": "object",
|
|
558
565
|
"properties": {
|
|
559
|
-
"pattern": {
|
|
560
|
-
"description": "Project-root-relative glob; the plugin activates when any\ndiscovered file matches it. Validated at load like other user\nglobs (no absolute paths or `..` segments).\nA single `*` stays within one path segment; use `**` to match across directories.",
|
|
561
|
-
"type": "string"
|
|
562
|
-
},
|
|
563
566
|
"type": {
|
|
564
567
|
"type": "string",
|
|
565
568
|
"const": "fileExists"
|
|
569
|
+
},
|
|
570
|
+
"pattern": {
|
|
571
|
+
"description": "Project-root-relative glob; the plugin activates when any\ndiscovered file matches it. Validated at load like other user\nglobs (no absolute paths or `..` segments).\nA single `*` stays within one path segment; use `**` to match across directories.",
|
|
572
|
+
"type": "string"
|
|
566
573
|
}
|
|
567
574
|
},
|
|
568
575
|
"required": [
|
|
@@ -574,16 +581,16 @@
|
|
|
574
581
|
"description": "All conditions must be true.",
|
|
575
582
|
"type": "object",
|
|
576
583
|
"properties": {
|
|
584
|
+
"type": {
|
|
585
|
+
"type": "string",
|
|
586
|
+
"const": "all"
|
|
587
|
+
},
|
|
577
588
|
"conditions": {
|
|
578
589
|
"description": "Sub-conditions combined with logical AND.",
|
|
579
590
|
"type": "array",
|
|
580
591
|
"items": {
|
|
581
592
|
"$ref": "#/$defs/PluginDetection"
|
|
582
593
|
}
|
|
583
|
-
},
|
|
584
|
-
"type": {
|
|
585
|
-
"type": "string",
|
|
586
|
-
"const": "all"
|
|
587
594
|
}
|
|
588
595
|
},
|
|
589
596
|
"required": [
|
|
@@ -595,16 +602,16 @@
|
|
|
595
602
|
"description": "Any condition must be true.",
|
|
596
603
|
"type": "object",
|
|
597
604
|
"properties": {
|
|
605
|
+
"type": {
|
|
606
|
+
"type": "string",
|
|
607
|
+
"const": "any"
|
|
608
|
+
},
|
|
598
609
|
"conditions": {
|
|
599
610
|
"description": "Sub-conditions combined with logical OR.",
|
|
600
611
|
"type": "array",
|
|
601
612
|
"items": {
|
|
602
613
|
"$ref": "#/$defs/PluginDetection"
|
|
603
614
|
}
|
|
604
|
-
},
|
|
605
|
-
"type": {
|
|
606
|
-
"type": "string",
|
|
607
|
-
"const": "any"
|
|
608
615
|
}
|
|
609
616
|
},
|
|
610
617
|
"required": [
|
|
@@ -1269,6 +1276,10 @@
|
|
|
1269
1276
|
"maximum": 65535,
|
|
1270
1277
|
"default": 20
|
|
1271
1278
|
},
|
|
1279
|
+
"cyclomaticPolicy": {
|
|
1280
|
+
"description": "How the cyclomatic gate counts complexity (default: `standard`).\n`literal-membership` counts a chain of strict equality checks of one\nidentifier against primitive literals (`kind === \"a\" || kind === \"b\"`)\nas one predicate. The policy applies to the `maxCyclomatic` gate and\nto every CRAP score, so the `maxCrap` gate reads the same value.\nFindings then report the standard value as `standard_cyclomatic`. The\nhealth score, complexity density, and hotspots keep the standard\ncount. Older fallow versions reject this key as unknown, so pair it\nwith `minimumVersion`.",
|
|
1281
|
+
"$ref": "#/$defs/CyclomaticPolicy"
|
|
1282
|
+
},
|
|
1272
1283
|
"maxCognitive": {
|
|
1273
1284
|
"description": "Maximum allowed cognitive complexity per function (default: 15).\nFunctions exceeding this threshold are reported. Governs findings\nonly, never the health score.",
|
|
1274
1285
|
"type": "integer",
|
|
@@ -1362,6 +1373,21 @@
|
|
|
1362
1373
|
},
|
|
1363
1374
|
"additionalProperties": false
|
|
1364
1375
|
},
|
|
1376
|
+
"CyclomaticPolicy": {
|
|
1377
|
+
"description": "How `fallow health` counts cyclomatic complexity for its gates.\n\nThe policy changes the cyclomatic value that the `maxCyclomatic` gate reads\nand the cyclomatic value in every CRAP score. It does not change the health\nscore, the complexity density, or the hotspot ranking: those keep the\nstandard count so that grades stay comparable across projects.",
|
|
1378
|
+
"oneOf": [
|
|
1379
|
+
{
|
|
1380
|
+
"description": "Count every decision point, `&&`, `||`, and `??` operator (`McCabe`).",
|
|
1381
|
+
"type": "string",
|
|
1382
|
+
"const": "standard"
|
|
1383
|
+
},
|
|
1384
|
+
{
|
|
1385
|
+
"description": "Count a literal membership chain as one predicate. A chain such as\n`kind === \"a\" || kind === \"b\" || kind === \"c\"` adds no cyclomatic\nincrements for its `||` operators. Only strict equality checks of one\nidentifier against string, number, bigint, boolean, or `null` literals\nqualify. Member access, calls, mixed operators, and different operands\nkeep the standard count.",
|
|
1386
|
+
"type": "string",
|
|
1387
|
+
"const": "literal-membership"
|
|
1388
|
+
}
|
|
1389
|
+
]
|
|
1390
|
+
},
|
|
1365
1391
|
"HealthThresholdOverride": {
|
|
1366
1392
|
"description": "Per-file or per-function health threshold override.",
|
|
1367
1393
|
"type": "object",
|
package/skills/fallow/SKILL.md
CHANGED
|
@@ -42,7 +42,7 @@ cargo install fallow-cli # build from source
|
|
|
42
42
|
|
|
43
43
|
## Agent Rules
|
|
44
44
|
1. **Always use `--format json --quiet`** for machine-readable output and parse stdout as JSON. Compact JSON is the default; never depend on whitespace or add `--pretty` in agent pipelines. Keep stderr separate so diagnostics remain visible; never merge it into the JSON stream with `2>&1`.
|
|
45
|
-
2. **Preserve and interpret the exit status.** Codes 0 and 1 are successful analysis outcomes
|
|
45
|
+
2. **Preserve and interpret the exit status.** Codes 0 and 1 are successful analysis outcomes. 0 means no enforced gate failed, and 1 means an enforced gate failed: findings, not a tool failure. Bare `fallow` in a machine format exits 0 on findings unless you pass `--fail-on-issues`. To gate, pass `--fail-on-issues`, run `fallow dead-code` or `fallow audit`, or read `gate_outcomes`: `status: "fail"` with `enforced: false` is a failing gate that did not set the exit code. Treat every other code according to `fallow schema.exit_codes`. Do not force a successful status, because that hides validation, license, setup, network, and security-gate outcomes.
|
|
46
46
|
3. **Use `--explain`** to include a `_meta` object in JSON output with metric definitions, ranges, and interpretation hints. In human format, `--explain` prints a `Description:` line under each section header.
|
|
47
47
|
4. **Use the root `kind` field** to identify typed JSON envelopes (`dead-code`, `dead-code-grouped`, `architecture`, `architecture-grouped`, `health`, `dupes`, `combined`, `audit`, etc.).
|
|
48
48
|
5. **Use issue type filters** (`--unused-exports`, `--unused-files`, etc.) to limit output scope
|
|
@@ -375,7 +375,7 @@ fallow hooks install --target git # pre-commit gate; --branch <ref> sets the f
|
|
|
375
375
|
|
|
376
376
|
## Exit Codes
|
|
377
377
|
|
|
378
|
-
Codes 0 and 1 are successful analysis outcomes
|
|
378
|
+
Codes 0 and 1 are successful analysis outcomes. 0 means no enforced gate failed, and 1 means an enforced gate failed: findings, not a tool failure. Bare `fallow` in a machine format exits 0 on findings unless you pass `--fail-on-issues`. To gate, pass `--fail-on-issues`, run `fallow dead-code` or `fallow audit`, or read `gate_outcomes`: `status: "fail"` with `enforced: false` is a failing gate that did not set the exit code. Read `fallow schema.exit_codes` for validation, resource, runtime, network, security-gate, and upload failures instead of maintaining another copied table.
|
|
379
379
|
|
|
380
380
|
When `--format json` is active and exit code is 2, errors are emitted as JSON on stdout:
|
|
381
381
|
```json
|