@bookedsolid/rea 0.22.0 → 0.23.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 +15 -0
- package/THREAT_MODEL.md +582 -0
- package/dist/audit/append.js +1 -1
- package/dist/cli/doctor.js +11 -12
- package/dist/cli/hook.d.ts +37 -3
- package/dist/cli/hook.js +167 -5
- package/dist/cli/init.js +14 -26
- package/dist/cli/install/canonical.js +18 -3
- package/dist/cli/install/commit-msg.js +1 -2
- package/dist/cli/install/copy.js +4 -13
- package/dist/cli/install/fs-safe.js +5 -16
- package/dist/cli/install/gitignore.js +1 -5
- package/dist/cli/install/pre-push.js +3 -8
- package/dist/cli/install/settings-merge.js +79 -16
- package/dist/cli/upgrade.js +14 -10
- package/dist/gateway/downstream.js +1 -2
- package/dist/gateway/live-state.js +3 -1
- package/dist/gateway/log.js +1 -3
- package/dist/gateway/middleware/audit.js +1 -1
- package/dist/gateway/middleware/injection.js +3 -9
- package/dist/gateway/middleware/policy.js +3 -1
- package/dist/gateway/middleware/redact.js +1 -1
- package/dist/gateway/observability/codex-telemetry.js +1 -2
- package/dist/gateway/reviewers/claude-self.js +10 -6
- package/dist/hooks/bash-scanner/blocked-scan.d.ts +26 -0
- package/dist/hooks/bash-scanner/blocked-scan.js +467 -0
- package/dist/hooks/bash-scanner/index.d.ts +41 -0
- package/dist/hooks/bash-scanner/index.js +62 -0
- package/dist/hooks/bash-scanner/parse-fail-closed.d.ts +31 -0
- package/dist/hooks/bash-scanner/parse-fail-closed.js +27 -0
- package/dist/hooks/bash-scanner/parser.d.ts +42 -0
- package/dist/hooks/bash-scanner/parser.js +92 -0
- package/dist/hooks/bash-scanner/protected-scan.d.ts +76 -0
- package/dist/hooks/bash-scanner/protected-scan.js +815 -0
- package/dist/hooks/bash-scanner/verdict.d.ts +80 -0
- package/dist/hooks/bash-scanner/verdict.js +49 -0
- package/dist/hooks/bash-scanner/walker.d.ts +165 -0
- package/dist/hooks/bash-scanner/walker.js +7954 -0
- package/dist/hooks/push-gate/base.js +2 -6
- package/dist/hooks/push-gate/codex-runner.js +3 -1
- package/dist/hooks/push-gate/index.js +9 -10
- package/dist/policy/loader.js +4 -1
- package/dist/registry/tofu-gate.js +2 -2
- package/hooks/blocked-paths-bash-gate.sh +142 -272
- package/hooks/protected-paths-bash-gate.sh +227 -511
- package/package.json +3 -2
- package/profiles/bst-internal-no-codex.yaml +1 -1
- package/profiles/bst-internal.yaml +1 -1
- package/profiles/client-engagement.yaml +1 -1
- package/profiles/lit-wc.yaml +1 -1
- package/profiles/minimal.yaml +1 -1
- package/profiles/open-source-no-codex.yaml +1 -1
- package/profiles/open-source.yaml +1 -1
- package/scripts/postinstall.mjs +1 -2
- package/scripts/run-vitest.mjs +117 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verdict shape — the contract between the bash-shim hooks and the
|
|
3
|
+
* `rea hook scan-bash` CLI.
|
|
4
|
+
*
|
|
5
|
+
* Stability: this JSON shape is part of the public hook protocol from
|
|
6
|
+
* 0.23.0 onward. The shim hooks under `hooks/protected-paths-bash-gate.sh`
|
|
7
|
+
* and `hooks/blocked-paths-bash-gate.sh` shell out to `rea hook scan-bash`
|
|
8
|
+
* and parse this exact shape with `jq`. Any change here that the bash
|
|
9
|
+
* shims don't survive is a breaking change — bump the major or stage
|
|
10
|
+
* with a fallback shape.
|
|
11
|
+
*
|
|
12
|
+
* Snapshot tests in `__tests__/hooks/bash-scanner/verdict-shape.test.ts`
|
|
13
|
+
* lock the wire format to keep this honest.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* The form of write the walker detected. New shapes can land at the
|
|
17
|
+
* end; the bash shims do not branch on these — they only forward the
|
|
18
|
+
* verdict — so adding a new tag is non-breaking.
|
|
19
|
+
*
|
|
20
|
+
* Naming convention: `<utility>_<role>` for argv-driven detections,
|
|
21
|
+
* `redirect` for shell I/O redirects (covers `>`, `>>`, `>|`, `&>`,
|
|
22
|
+
* fd-prefixed variants), `nested_shell_inner` for unwrapped
|
|
23
|
+
* `bash -c`/`sh -c` payloads.
|
|
24
|
+
*/
|
|
25
|
+
export type DetectedForm = 'redirect' | 'cp_dest' | 'cp_t_flag' | 'mv_dest' | 'mv_t_flag' | 'tee_arg' | 'sed_i' | 'dd_of' | 'truncate_arg' | 'install_dest' | 'ln_dest' | 'awk_inplace' | 'awk_source' | 'ed_target' | 'ex_target' | 'find_exec_inner' | 'find_exec_placeholder_unresolvable' | 'xargs_unresolvable' | 'parallel_stdin_unresolvable' | 'git_filter_branch_inner' | 'git_rebase_exec_inner' | 'git_bisect_run_inner' | 'git_commit_template' | 'git_rm_dest' | 'git_mv_src' | 'archive_extract_dest' | 'archive_extract_unresolvable' | 'archive_member_dest' | 'archive_create_dest' | 'gzip_compress_dest' | 'cmake_e_dest' | 'mkfifo_dest' | 'mknod_dest' | 'node_e_path' | 'python_c_path' | 'ruby_e_path' | 'perl_e_path' | 'php_r_path' | 'process_subst_inner' | 'nested_shell_inner';
|
|
26
|
+
/**
|
|
27
|
+
* Source position for a detected write. 1-indexed (matches the parser's
|
|
28
|
+
* convention) so the operator-facing error message reads naturally.
|
|
29
|
+
*/
|
|
30
|
+
export interface SourcePosition {
|
|
31
|
+
line: number;
|
|
32
|
+
col: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The single verdict the scanner returns to its caller. Allow paths
|
|
36
|
+
* leave reason/hit_pattern/detected_form/source_position unset; block
|
|
37
|
+
* paths set all four where determinable.
|
|
38
|
+
*
|
|
39
|
+
* `parse_failure_reason` is set ONLY when the parser itself rejected
|
|
40
|
+
* the input — distinct from a successfully-parsed but policy-violating
|
|
41
|
+
* command. Lets the operator-facing error tell the difference between
|
|
42
|
+
* "you wrote bad bash" and "you tried to write to .rea/HALT".
|
|
43
|
+
*/
|
|
44
|
+
export interface Verdict {
|
|
45
|
+
verdict: 'allow' | 'block';
|
|
46
|
+
reason?: string;
|
|
47
|
+
hit_pattern?: string;
|
|
48
|
+
detected_form?: DetectedForm;
|
|
49
|
+
source_position?: SourcePosition;
|
|
50
|
+
/**
|
|
51
|
+
* Set on parse-failure blocks only. Format: parser library's raw
|
|
52
|
+
* error message verbatim, prefixed with "parser: ". Operators can
|
|
53
|
+
* paste this into a bug report without further redaction.
|
|
54
|
+
*/
|
|
55
|
+
parse_failure_reason?: string;
|
|
56
|
+
}
|
|
57
|
+
/** Construct a uniform allow verdict. */
|
|
58
|
+
export declare function allowVerdict(): Verdict;
|
|
59
|
+
/**
|
|
60
|
+
* Construct a uniform block verdict for a successful-parse + policy
|
|
61
|
+
* violation. All four explanation fields are required so the operator
|
|
62
|
+
* gets actionable context.
|
|
63
|
+
*/
|
|
64
|
+
export declare function blockVerdict(args: {
|
|
65
|
+
reason: string;
|
|
66
|
+
hitPattern: string;
|
|
67
|
+
detectedForm: DetectedForm;
|
|
68
|
+
sourcePosition?: SourcePosition;
|
|
69
|
+
}): Verdict;
|
|
70
|
+
/**
|
|
71
|
+
* Construct a uniform block verdict for a parse-failure event. We
|
|
72
|
+
* always block — the alternative is "scanner can't tell, assume safe"
|
|
73
|
+
* which is the entire bug class this rewrite exists to close.
|
|
74
|
+
*
|
|
75
|
+
* `parserMessage` flows through to the operator. We DO NOT sanitize
|
|
76
|
+
* it — the parser's messages are static (no user-controlled
|
|
77
|
+
* interpolation in modern mvdan-sh) and including them helps debug
|
|
78
|
+
* malformed payloads in the field.
|
|
79
|
+
*/
|
|
80
|
+
export declare function parseFailureVerdict(parserMessage: string): Verdict;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verdict shape — the contract between the bash-shim hooks and the
|
|
3
|
+
* `rea hook scan-bash` CLI.
|
|
4
|
+
*
|
|
5
|
+
* Stability: this JSON shape is part of the public hook protocol from
|
|
6
|
+
* 0.23.0 onward. The shim hooks under `hooks/protected-paths-bash-gate.sh`
|
|
7
|
+
* and `hooks/blocked-paths-bash-gate.sh` shell out to `rea hook scan-bash`
|
|
8
|
+
* and parse this exact shape with `jq`. Any change here that the bash
|
|
9
|
+
* shims don't survive is a breaking change — bump the major or stage
|
|
10
|
+
* with a fallback shape.
|
|
11
|
+
*
|
|
12
|
+
* Snapshot tests in `__tests__/hooks/bash-scanner/verdict-shape.test.ts`
|
|
13
|
+
* lock the wire format to keep this honest.
|
|
14
|
+
*/
|
|
15
|
+
/** Construct a uniform allow verdict. */
|
|
16
|
+
export function allowVerdict() {
|
|
17
|
+
return { verdict: 'allow' };
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Construct a uniform block verdict for a successful-parse + policy
|
|
21
|
+
* violation. All four explanation fields are required so the operator
|
|
22
|
+
* gets actionable context.
|
|
23
|
+
*/
|
|
24
|
+
export function blockVerdict(args) {
|
|
25
|
+
return {
|
|
26
|
+
verdict: 'block',
|
|
27
|
+
reason: args.reason,
|
|
28
|
+
hit_pattern: args.hitPattern,
|
|
29
|
+
detected_form: args.detectedForm,
|
|
30
|
+
...(args.sourcePosition !== undefined ? { source_position: args.sourcePosition } : {}),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Construct a uniform block verdict for a parse-failure event. We
|
|
35
|
+
* always block — the alternative is "scanner can't tell, assume safe"
|
|
36
|
+
* which is the entire bug class this rewrite exists to close.
|
|
37
|
+
*
|
|
38
|
+
* `parserMessage` flows through to the operator. We DO NOT sanitize
|
|
39
|
+
* it — the parser's messages are static (no user-controlled
|
|
40
|
+
* interpolation in modern mvdan-sh) and including them helps debug
|
|
41
|
+
* malformed payloads in the field.
|
|
42
|
+
*/
|
|
43
|
+
export function parseFailureVerdict(parserMessage) {
|
|
44
|
+
return {
|
|
45
|
+
verdict: 'block',
|
|
46
|
+
reason: 'rea: bash parser failed; refusing on uncertainty',
|
|
47
|
+
parse_failure_reason: `parser: ${parserMessage}`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AST walker — given a parsed `BashFile`, yield every detected write
|
|
3
|
+
* target with its form classification and source position.
|
|
4
|
+
*
|
|
5
|
+
* The walker is the closed surface this scanner is built around. Every
|
|
6
|
+
* regex/heuristic the bash gates used (and there were many; see
|
|
7
|
+
* `hooks/_lib/cmd-segments.sh` and `hooks/_lib/interpreter-scanner.sh`
|
|
8
|
+
* pre-0.23.0) is replaced here by an AST-driven match. The argument
|
|
9
|
+
* grammar is already correct because the parser rebuilt the tree from
|
|
10
|
+
* shell tokenization rules; we never re-tokenize a string.
|
|
11
|
+
*
|
|
12
|
+
* Detection forms covered (matching `verdict.ts::DetectedForm`):
|
|
13
|
+
*
|
|
14
|
+
* - `redirect` — Stmt-level Redirs whose Op is a write
|
|
15
|
+
* - `cp_dest` / `cp_t_flag` — POSIX-cp tail destination, plus -t / --target-directory
|
|
16
|
+
* - `mv_dest` / `mv_t_flag` — same for mv
|
|
17
|
+
* - `tee_arg` — every non-flag arg to tee
|
|
18
|
+
* - `sed_i` — sed -i / -i'' /-iEXT trailing target
|
|
19
|
+
* - `dd_of` — `of=` named arg
|
|
20
|
+
* - `truncate_arg` — first non-flag arg
|
|
21
|
+
* - `install_dest` / `ln_dest` — last positional
|
|
22
|
+
* - `awk_inplace` — awk/gawk -i inplace target
|
|
23
|
+
* - `ed_target` / `ex_target` — first non-flag positional
|
|
24
|
+
* - `find_exec_inner` — recurse the inner -exec / -execdir / -ok cmd
|
|
25
|
+
* - `xargs_unresolvable` — xargs is destination-via-stdin; refuse
|
|
26
|
+
* - `node_e_path` — node -e payload string-scanned for fs.write*
|
|
27
|
+
* - `python_c_path` — python -c payload scanned for open(...,'w'/'a')
|
|
28
|
+
* - `ruby_e_path` — ruby -e File.write/.open(...'w')
|
|
29
|
+
* - `perl_e_path` — perl -e open(FH,'>FILE')
|
|
30
|
+
* - `process_subst_inner` — recurse `>(...)` / `<(...)` inner stmts
|
|
31
|
+
* - `nested_shell_inner` — recurse bash -c / sh -c / zsh -c payloads
|
|
32
|
+
*
|
|
33
|
+
* Dynamic targets (containing $VAR, `cmd`, $(cmd), arithmetic, etc.)
|
|
34
|
+
* are emitted with `dynamic: true`. The compositor refuses on dynamic
|
|
35
|
+
* by default — fail-closed parity with the 0.21.2/0.22.0 sentinel
|
|
36
|
+
* `__rea_unresolved_expansion__`. Process substitutions and nested
|
|
37
|
+
* shells are NOT considered dynamic targets themselves; they're
|
|
38
|
+
* recursed.
|
|
39
|
+
*/
|
|
40
|
+
import type { BashFile } from 'mvdan-sh';
|
|
41
|
+
import type { DetectedForm, SourcePosition } from './verdict.js';
|
|
42
|
+
/**
|
|
43
|
+
* One detected write. The compositor pairs `path` against the policy
|
|
44
|
+
* (protected or blocked) to decide allow/block.
|
|
45
|
+
*
|
|
46
|
+
* `dynamic: true` means the target's value depends on shell expansion
|
|
47
|
+
* (`$VAR`, `$(cmd)`, backticks, arithmetic, brace expansion) we did not
|
|
48
|
+
* fully resolve. Fail-closed semantics: the compositor always BLOCKS
|
|
49
|
+
* dynamic targets. The detected_form on a dynamic emit is whatever
|
|
50
|
+
* shape was in argv position.
|
|
51
|
+
*
|
|
52
|
+
* `isDirTarget: true` means the target is semantically a directory —
|
|
53
|
+
* `cp -t DIR ...` / `cp --target-directory=DIR ...` / `install -t DIR
|
|
54
|
+
* ...` / `mv -t DIR ...` / `ln -t DIR ...`. The matcher treats dir-
|
|
55
|
+
* targets as `<DIR>/`-shaped: writes INTO that directory may hit any
|
|
56
|
+
* file under it, so a protected file inside the dir matches even when
|
|
57
|
+
* the input lacks a trailing slash. Codex round 1 F-7.
|
|
58
|
+
*
|
|
59
|
+
* `originSrc` is the bash source-substring for the offending node —
|
|
60
|
+
* useful when the operator-facing error message wants to show
|
|
61
|
+
* "Segment: ..." like the bash gates did. It is NOT guaranteed
|
|
62
|
+
* verbatim from the input (the parser may normalize whitespace);
|
|
63
|
+
* treat it as best-effort.
|
|
64
|
+
*/
|
|
65
|
+
export interface DetectedWrite {
|
|
66
|
+
path: string;
|
|
67
|
+
form: DetectedForm;
|
|
68
|
+
position: SourcePosition;
|
|
69
|
+
dynamic: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* The target is a directory (the write semantics are "into this
|
|
72
|
+
* directory"). Set on `cp -t`, `mv -t`, `install -t`, `ln -t`,
|
|
73
|
+
* `--target-directory=`. False for ordinary file destinations.
|
|
74
|
+
*/
|
|
75
|
+
isDirTarget?: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* The detection is a destructive operation (recursive removal,
|
|
78
|
+
* unlink, rmdir, find -delete, FileUtils.rm_rf, shutil.rmtree, etc).
|
|
79
|
+
* Codex round 4 Finding 1: when set, a target that is an ANCESTOR
|
|
80
|
+
* directory of any protected file matches via protected-ancestry —
|
|
81
|
+
* `rm -rf .rea` is treated as a write to every file under .rea/.
|
|
82
|
+
* Without this flag, the scanner only matches exact patterns or
|
|
83
|
+
* dir-shape inputs (-t flags, trailing slash); plain `.rea` argv
|
|
84
|
+
* positionals walked unchecked because they are neither.
|
|
85
|
+
*/
|
|
86
|
+
isDestructive?: boolean;
|
|
87
|
+
originSrc?: string;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Walk the AST and return every detected write.
|
|
91
|
+
*
|
|
92
|
+
* 0.23.0 round-6 architectural refactor: deny-by-default generic
|
|
93
|
+
* `syntax.Walk()` traversal. Pre-refactor the walker dispatched on
|
|
94
|
+
* specific Cmd kinds (`case 'WhileClause':`, `case 'ForClause':`,
|
|
95
|
+
* `case 'DeclClause':`, etc.) and manually traversed each kind's
|
|
96
|
+
* specific fields. Any field NOT enumerated in the case branch was
|
|
97
|
+
* silently dropped — that pattern produced six rounds of P0 bypasses
|
|
98
|
+
* (DeclClause.Args round 5, CaseClause.Word round 5, WhileClause.Cond
|
|
99
|
+
* round 6, ForClause.CStyleLoop.Init/Cond/Post round 6, etc).
|
|
100
|
+
*
|
|
101
|
+
* The round-6 design closes OUR-DISPATCH field-omission structurally.
|
|
102
|
+
* We use `mvdan-sh`'s built-in `syntax.Walk(node, visit)` for
|
|
103
|
+
* traversal — every Cmd kind's inner Stmts / CallExprs / BinaryCmds
|
|
104
|
+
* reach our dispatcher when Walk descends into them. Our dispatch is
|
|
105
|
+
* preserved (per-utility cp/mv/sed/find/etc.), but the TRAVERSAL is
|
|
106
|
+
* no longer a denylist of OUR shapes. A new mvdan-sh Cmd type, or a
|
|
107
|
+
* new field on an existing Cmd, automatically gets visited from our
|
|
108
|
+
* side.
|
|
109
|
+
*
|
|
110
|
+
* 0.23.0 round-7 P0 closure: the round-6 framing "Walk visits every
|
|
111
|
+
* field" was OVERCLAIM. mvdan-sh@0.10.1's `syntax.Walk` itself has
|
|
112
|
+
* field gaps. Empirically verified (see `walk-probe-slice.mjs`):
|
|
113
|
+
* `ParamExp.Slice.Offset` and `ParamExp.Slice.Length` (Word nodes)
|
|
114
|
+
* are NOT recursed into by Walk. Pre-fix every `${X:$(...)}` /
|
|
115
|
+
* `${X:0:$(...)}` / `${arr[@]:$(...)}` / `${@:$(...)}` form bypassed
|
|
116
|
+
* every detector — a regression vs 0.22.0's bash regex.
|
|
117
|
+
*
|
|
118
|
+
* Round-7 fix: this function declares its visitor up front and
|
|
119
|
+
* manually re-enters `syntax.Walk` on the missed Slice subtrees via
|
|
120
|
+
* `recurseParamExpSlice` whenever the visitor sees a `ParamExp`. The
|
|
121
|
+
* re-entry uses the SAME visitor, so nested forms (e.g.
|
|
122
|
+
* `${X:${Y:$(rm)}}`) recurse to fixed point.
|
|
123
|
+
*
|
|
124
|
+
* Round-7 structural pin: the Class O exhaustiveness contract test
|
|
125
|
+
* (`__tests__/hooks/bash-scanner/walker-exhaustiveness.contract.test.ts`)
|
|
126
|
+
* names every (node-type, field) Word-bearing position mvdan-sh
|
|
127
|
+
* populates and asserts the walker reaches each one. If
|
|
128
|
+
* mvdan-sh@0.11.0+ adds a new node type or field that Walk skips,
|
|
129
|
+
* that test fails CI before any runtime regression — the fix is
|
|
130
|
+
* always a one-line manual recursion in the visit callback below.
|
|
131
|
+
*
|
|
132
|
+
* What this closes by construction:
|
|
133
|
+
* - WhileClause / UntilClause `.Cond` → visited (round 6)
|
|
134
|
+
* - ForClause `.CStyleLoop.{Init,Cond,Post}` → visited (round 6)
|
|
135
|
+
* - DeclClause `.Args[*].Value` → visited (round 5 redux)
|
|
136
|
+
* - CaseClause `.Word` and `.Items[*].Patterns` → visited (round 5 redux)
|
|
137
|
+
* - TestClause arbitrary nesting → visited
|
|
138
|
+
* - ArithmCmd / LetClause / SelectClause → visited
|
|
139
|
+
* - Stmt.Redirs[*].Word with embedded CmdSubst → visited
|
|
140
|
+
* - Function bodies, if/else branches, subshells → visited
|
|
141
|
+
* - Anything we add in mvdan-sh@0.11.0+ → reaches our dispatcher
|
|
142
|
+
* IF Walk visits it; if Walk skips it, Class O fails CI first
|
|
143
|
+
*
|
|
144
|
+
* What we still maintain explicitly (because they require argv-level
|
|
145
|
+
* inspection or string-level re-parse):
|
|
146
|
+
* - CallExpr → detector dispatch on the command head
|
|
147
|
+
* - Stmt-level redirects (`>`, `>>`, etc.) → emit per redirect Op
|
|
148
|
+
* - BinaryCmd pipe-into-bare-shell detection
|
|
149
|
+
* - heredoc-into-shell payload re-parse
|
|
150
|
+
* - eval / trap / nested-shell payload re-parse
|
|
151
|
+
* - ParamExp.Slice.{Offset,Length} → manual Walk re-entry
|
|
152
|
+
* (round 7 P0 — Walk's own gap, NOT our dispatch gap)
|
|
153
|
+
*
|
|
154
|
+
* The walker NEVER throws on shape oddities — it gracefully ignores
|
|
155
|
+
* nodes whose shape doesn't match what we expect. Parse-level failures
|
|
156
|
+
* are upstream (the parser wrapper). Walker-level failures would be a
|
|
157
|
+
* bug in this file; defensively continue.
|
|
158
|
+
*
|
|
159
|
+
* Performance: O(N) over AST nodes; allocation-light. `syntax.Walk`
|
|
160
|
+
* is a pure-traversal pass that calls our visitor once per node.
|
|
161
|
+
* Visited-node-set tracking is unnecessary because mvdan-sh's
|
|
162
|
+
* tree is acyclic by construction (re-parsed payloads are separate
|
|
163
|
+
* BashFile trees walked via fresh `walkForWrites` invocations).
|
|
164
|
+
*/
|
|
165
|
+
export declare function walkForWrites(file: BashFile): DetectedWrite[];
|