@intutic/sync-daemon 1.9.0 → 1.10.1
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 +2 -2
- package/dist/agentReporter.d.ts +205 -0
- package/dist/agentReporter.d.ts.map +1 -0
- package/dist/agentReporter.js +394 -0
- package/dist/agentReporter.js.map +1 -0
- package/dist/configReader.d.ts +39 -2
- package/dist/configReader.d.ts.map +1 -1
- package/dist/configReader.js +56 -2
- package/dist/configReader.js.map +1 -1
- package/dist/configWriter.d.ts +51 -1
- package/dist/configWriter.d.ts.map +1 -1
- package/dist/configWriter.js +490 -63
- package/dist/configWriter.js.map +1 -1
- package/dist/harness/antigravityHooks.d.ts.map +1 -1
- package/dist/harness/antigravityHooks.js +33 -55
- package/dist/harness/antigravityHooks.js.map +1 -1
- package/dist/harness/claudeCodeHooks.d.ts +47 -13
- package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
- package/dist/harness/claudeCodeHooks.js +323 -55
- package/dist/harness/claudeCodeHooks.js.map +1 -1
- package/dist/harness/claudeDesktopHooks.d.ts.map +1 -1
- package/dist/harness/claudeDesktopHooks.js +35 -48
- package/dist/harness/claudeDesktopHooks.js.map +1 -1
- package/dist/harness/clineHooks.d.ts.map +1 -1
- package/dist/harness/clineHooks.js +42 -34
- package/dist/harness/clineHooks.js.map +1 -1
- package/dist/harness/codexHooks.d.ts +36 -0
- package/dist/harness/codexHooks.d.ts.map +1 -0
- package/dist/harness/codexHooks.js +227 -0
- package/dist/harness/codexHooks.js.map +1 -0
- package/dist/harness/continueHooks.d.ts +45 -5
- package/dist/harness/continueHooks.d.ts.map +1 -1
- package/dist/harness/continueHooks.js +225 -5
- package/dist/harness/continueHooks.js.map +1 -1
- package/dist/harness/cursorHooks.d.ts +31 -0
- package/dist/harness/cursorHooks.d.ts.map +1 -1
- package/dist/harness/cursorHooks.js +78 -49
- package/dist/harness/cursorHooks.js.map +1 -1
- package/dist/harness/dshHooks.d.ts +198 -0
- package/dist/harness/dshHooks.d.ts.map +1 -0
- package/dist/harness/dshHooks.js +569 -0
- package/dist/harness/dshHooks.js.map +1 -0
- package/dist/harness/gateBody.d.ts +422 -0
- package/dist/harness/gateBody.d.ts.map +1 -0
- package/dist/harness/gateBody.js +1816 -0
- package/dist/harness/gateBody.js.map +1 -0
- package/dist/harness/gateKind.d.ts +164 -0
- package/dist/harness/gateKind.d.ts.map +1 -0
- package/dist/harness/gateKind.js +143 -0
- package/dist/harness/gateKind.js.map +1 -0
- package/dist/harness/githubCopilotHooks.d.ts +54 -0
- package/dist/harness/githubCopilotHooks.d.ts.map +1 -0
- package/dist/harness/githubCopilotHooks.js +218 -0
- package/dist/harness/githubCopilotHooks.js.map +1 -0
- package/dist/harness/gooseHooks.d.ts.map +1 -1
- package/dist/harness/gooseHooks.js +67 -37
- package/dist/harness/gooseHooks.js.map +1 -1
- package/dist/harness/grokHooks.d.ts +77 -0
- package/dist/harness/grokHooks.d.ts.map +1 -0
- package/dist/harness/grokHooks.js +350 -0
- package/dist/harness/grokHooks.js.map +1 -0
- package/dist/harness/hermesHooks.d.ts.map +1 -1
- package/dist/harness/hermesHooks.js +26 -55
- package/dist/harness/hermesHooks.js.map +1 -1
- package/dist/harness/holdRedaction.d.ts +69 -0
- package/dist/harness/holdRedaction.d.ts.map +1 -0
- package/dist/harness/holdRedaction.js +211 -0
- package/dist/harness/holdRedaction.js.map +1 -0
- package/dist/harness/jetbrainsXmlConfig.d.ts +51 -0
- package/dist/harness/jetbrainsXmlConfig.d.ts.map +1 -0
- package/dist/harness/jetbrainsXmlConfig.js +151 -0
- package/dist/harness/jetbrainsXmlConfig.js.map +1 -0
- package/dist/harness/mcpAutoWrite.d.ts +100 -3
- package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
- package/dist/harness/mcpAutoWrite.js +712 -90
- package/dist/harness/mcpAutoWrite.js.map +1 -1
- package/dist/harness/museHooks.d.ts +53 -0
- package/dist/harness/museHooks.d.ts.map +1 -0
- package/dist/harness/museHooks.js +273 -0
- package/dist/harness/museHooks.js.map +1 -0
- package/dist/harness/n8nHooks.d.ts +25 -9
- package/dist/harness/n8nHooks.d.ts.map +1 -1
- package/dist/harness/n8nHooks.js +174 -9
- package/dist/harness/n8nHooks.js.map +1 -1
- package/dist/harness/openCodeHooks.d.ts +93 -0
- package/dist/harness/openCodeHooks.d.ts.map +1 -0
- package/dist/harness/openCodeHooks.js +248 -0
- package/dist/harness/openCodeHooks.js.map +1 -0
- package/dist/harness/openWebuiHooks.d.ts.map +1 -1
- package/dist/harness/openWebuiHooks.js +56 -11
- package/dist/harness/openWebuiHooks.js.map +1 -1
- package/dist/harness/openclawHooks.d.ts.map +1 -1
- package/dist/harness/openclawHooks.js +30 -54
- package/dist/harness/openclawHooks.js.map +1 -1
- package/dist/harness/openhandsHooks.d.ts.map +1 -1
- package/dist/harness/openhandsHooks.js +35 -41
- package/dist/harness/openhandsHooks.js.map +1 -1
- package/dist/harness/piHooks.d.ts.map +1 -1
- package/dist/harness/piHooks.js +31 -58
- package/dist/harness/piHooks.js.map +1 -1
- package/dist/harness/protectedPaths.d.ts +353 -0
- package/dist/harness/protectedPaths.d.ts.map +1 -0
- package/dist/harness/protectedPaths.js +805 -0
- package/dist/harness/protectedPaths.js.map +1 -0
- package/dist/harness/rooCodeHooks.d.ts.map +1 -1
- package/dist/harness/rooCodeHooks.js +34 -48
- package/dist/harness/rooCodeHooks.js.map +1 -1
- package/dist/harness/windsurfHooks.d.ts +72 -0
- package/dist/harness/windsurfHooks.d.ts.map +1 -1
- package/dist/harness/windsurfHooks.js +172 -58
- package/dist/harness/windsurfHooks.js.map +1 -1
- package/dist/harness/windsurfJetBrainsProxy.d.ts +78 -0
- package/dist/harness/windsurfJetBrainsProxy.d.ts.map +1 -0
- package/dist/harness/windsurfJetBrainsProxy.js +159 -0
- package/dist/harness/windsurfJetBrainsProxy.js.map +1 -0
- package/dist/hashReporter.js +1 -1
- package/dist/hashReporter.js.map +1 -1
- package/dist/index.d.ts +12 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -6
- package/dist/index.js.map +1 -1
- package/dist/integrityStore.d.ts +0 -8
- package/dist/integrityStore.d.ts.map +1 -1
- package/dist/integrityStore.js +6 -29
- package/dist/integrityStore.js.map +1 -1
- package/dist/lib/approvedBypasses.d.ts +76 -0
- package/dist/lib/approvedBypasses.d.ts.map +1 -0
- package/dist/lib/approvedBypasses.js +140 -0
- package/dist/lib/approvedBypasses.js.map +1 -0
- package/dist/lib/complianceProbes.d.ts.map +1 -1
- package/dist/lib/complianceProbes.js +5 -0
- package/dist/lib/complianceProbes.js.map +1 -1
- package/dist/lib/decisionsDigest.d.ts +99 -0
- package/dist/lib/decisionsDigest.d.ts.map +1 -0
- package/dist/lib/decisionsDigest.js +199 -0
- package/dist/lib/decisionsDigest.js.map +1 -0
- package/dist/lib/egressPolicy.d.ts +69 -0
- package/dist/lib/egressPolicy.d.ts.map +1 -0
- package/dist/lib/egressPolicy.js +126 -0
- package/dist/lib/egressPolicy.js.map +1 -0
- package/dist/lib/gitWorktrees.d.ts +140 -0
- package/dist/lib/gitWorktrees.d.ts.map +1 -0
- package/dist/lib/gitWorktrees.js +191 -0
- package/dist/lib/gitWorktrees.js.map +1 -0
- package/dist/lib/policySnapshot.d.ts +224 -0
- package/dist/lib/policySnapshot.d.ts.map +1 -0
- package/dist/lib/policySnapshot.js +637 -0
- package/dist/lib/policySnapshot.js.map +1 -0
- package/dist/lib/processPoller.d.ts +44 -0
- package/dist/lib/processPoller.d.ts.map +1 -1
- package/dist/lib/processPoller.js +176 -1
- package/dist/lib/processPoller.js.map +1 -1
- package/dist/lib/runtimeEnv.d.ts +8 -0
- package/dist/lib/runtimeEnv.d.ts.map +1 -1
- package/dist/lib/runtimeEnv.js +3 -0
- package/dist/lib/runtimeEnv.js.map +1 -1
- package/dist/localTrajectoryHeuristics.d.ts +51 -0
- package/dist/localTrajectoryHeuristics.d.ts.map +1 -0
- package/dist/localTrajectoryHeuristics.js +90 -0
- package/dist/localTrajectoryHeuristics.js.map +1 -0
- package/dist/sessionReporter.d.ts +70 -0
- package/dist/sessionReporter.d.ts.map +1 -0
- package/dist/sessionReporter.js +140 -0
- package/dist/sessionReporter.js.map +1 -0
- package/dist/skillWriter.d.ts +1 -1
- package/dist/skillWriter.d.ts.map +1 -1
- package/dist/skillWriter.js +6 -3
- package/dist/skillWriter.js.map +1 -1
- package/dist/syncLoop.d.ts +105 -3
- package/dist/syncLoop.d.ts.map +1 -1
- package/dist/syncLoop.js +397 -65
- package/dist/syncLoop.js.map +1 -1
- package/dist/trajectoryMonitor.d.ts +2 -0
- package/dist/trajectoryMonitor.d.ts.map +1 -1
- package/dist/trajectoryMonitor.js +57 -0
- package/dist/trajectoryMonitor.js.map +1 -1
- package/dist/watcher/driftWatcher.d.ts.map +1 -1
- package/dist/watcher/driftWatcher.js +12 -3
- package/dist/watcher/driftWatcher.js.map +1 -1
- package/dist/watcher/settingsGuard.d.ts +23 -0
- package/dist/watcher/settingsGuard.d.ts.map +1 -1
- package/dist/watcher/settingsGuard.js +202 -17
- package/dist/watcher/settingsGuard.js.map +1 -1
- package/package.json +37 -6
- package/dist/brainIndexer.d.ts +0 -51
- package/dist/brainIndexer.d.ts.map +0 -1
- package/dist/brainIndexer.js +0 -148
- package/dist/brainIndexer.js.map +0 -1
- package/dist/harness/cursorHooksJson.d.ts +0 -29
- package/dist/harness/cursorHooksJson.d.ts.map +0 -1
- package/dist/harness/cursorHooksJson.js +0 -130
- package/dist/harness/cursorHooksJson.js.map +0 -1
- package/dist/harness/gooseHooksWriter.d.ts +0 -27
- package/dist/harness/gooseHooksWriter.d.ts.map +0 -1
- package/dist/harness/gooseHooksWriter.js +0 -147
- package/dist/harness/gooseHooksWriter.js.map +0 -1
- package/dist/harness/openhandsHooksWriter.d.ts +0 -23
- package/dist/harness/openhandsHooksWriter.d.ts.map +0 -1
- package/dist/harness/openhandsHooksWriter.js +0 -146
- package/dist/harness/openhandsHooksWriter.js.map +0 -1
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The config files an agent must never write, and the command shapes it must
|
|
3
|
+
* never run — whichever harness it runs under.
|
|
4
|
+
*
|
|
5
|
+
* # Why this is one list
|
|
6
|
+
*
|
|
7
|
+
* It was eleven lists, and no two agreed. Measured on 2026-08-04:
|
|
8
|
+
*
|
|
9
|
+
* | path | harnesses protecting it |
|
|
10
|
+
* |---|---|
|
|
11
|
+
* | `.claude/settings.local.json` | 3 of 11 |
|
|
12
|
+
* | `.gemini/settings.json` | 5 of 11 |
|
|
13
|
+
* | `.intutic/events` | 5 of 11 |
|
|
14
|
+
* | `.codeium/windsurf/hooks.json` | 8 of 11 |
|
|
15
|
+
* | `.cursor/hooks.json` | 8 of 11 |
|
|
16
|
+
*
|
|
17
|
+
* That is not eleven partial views of one intent, it is a **cross-harness
|
|
18
|
+
* tamper matrix**. The Claude-family lists carried only Claude paths, so an
|
|
19
|
+
* agent under Claude Code could rewrite `.cursor/hooks.json`,
|
|
20
|
+
* `.openhands/hooks.json`, `.gemini/settings.json` or the governance plugin
|
|
21
|
+
* directory unopposed; the other nine could write `.claude/settings.local.json`,
|
|
22
|
+
* which Claude Code loads at *higher* precedence than the `settings.json` they
|
|
23
|
+
* all did protect. Whichever harness the agent was running under, it could
|
|
24
|
+
* disarm a different one.
|
|
25
|
+
*
|
|
26
|
+
* A protected-path list is a security control. Eleven copies of a security
|
|
27
|
+
* control is eleven chances for one to be wrong, and every one of them was.
|
|
28
|
+
*
|
|
29
|
+
* # The same was true of the command guards
|
|
30
|
+
*
|
|
31
|
+
* Nine writers hand-maintained a `BYPASS_PATTERNS` list. Measured on 2026-08-05:
|
|
32
|
+
*
|
|
33
|
+
* | pattern | writers carrying it |
|
|
34
|
+
* |---|---|
|
|
35
|
+
* | `chflags nouchg` / `chattr -i` | 9 of 9 |
|
|
36
|
+
* | `rm -rf .intutic` | 7 of 9 — **`cursorHooks` and `windsurfHooks` had no protected-path command guard at all** |
|
|
37
|
+
* | `rm -rf .gemini/settings` | 4 of 9 |
|
|
38
|
+
* | the harness's own env kill-switch | 1 each |
|
|
39
|
+
*
|
|
40
|
+
* That last row is the load-bearing one. `claudeCodeHooks` guarded
|
|
41
|
+
* `CLAUDE_CODE_HOOKS=` and nothing else; `piHooks` guarded `PI_PRE_TOOL_HOOK=`
|
|
42
|
+
* and nothing else. So under any harness, the *other eight* kill-switches were
|
|
43
|
+
* free to set — the identical cross-harness hole the path list already had.
|
|
44
|
+
* `GOVERNANCE_BYPASS_PATTERNS` closes it with one generic pattern rather than
|
|
45
|
+
* nine specific ones, so a harness added tomorrow is covered on the day it
|
|
46
|
+
* lands.
|
|
47
|
+
*
|
|
48
|
+
* Two writers also used `/chflags\s+nouchg/` where the other seven used a
|
|
49
|
+
* literal substring. `\s` does not exist in POSIX ERE — see
|
|
50
|
+
* {@link assertPortableEre} for why that class of divergence is now a load
|
|
51
|
+
* failure rather than a silent one.
|
|
52
|
+
*
|
|
53
|
+
* # Scope
|
|
54
|
+
*
|
|
55
|
+
* Universal by design. A Cursor agent has no legitimate reason to edit Claude
|
|
56
|
+
* Code's settings, and the reverse holds for every pair — so the union is the
|
|
57
|
+
* correct list for all of them, not a Cursor list plus a Claude list.
|
|
58
|
+
* Harness-specific additions spread this and append; `harnessProtectedPaths.test.ts`
|
|
59
|
+
* asserts each is a superset.
|
|
60
|
+
*
|
|
61
|
+
* @module
|
|
62
|
+
*/
|
|
63
|
+
/** Governance config that must hold still, for every harness. */
|
|
64
|
+
export declare const UNIVERSAL_PROTECTED_PATHS: readonly string[];
|
|
65
|
+
/**
|
|
66
|
+
* One command-guard rule, in a form every gate can evaluate identically.
|
|
67
|
+
*
|
|
68
|
+
* `source` is matched against the **normalised, padded** command — see
|
|
69
|
+
* {@link NORMALISE_CONTRACT}. It is deliberately restricted to the intersection
|
|
70
|
+
* of POSIX ERE and JavaScript `RegExp`; {@link assertPortableEre} enforces that
|
|
71
|
+
* at module load.
|
|
72
|
+
*/
|
|
73
|
+
/**
|
|
74
|
+
* Which part of the tool call a rule is matched against.
|
|
75
|
+
*
|
|
76
|
+
* Not cosmetic. A `BLOCK:` SOP compiles to a **tool-name** pattern (`Bash`,
|
|
77
|
+
* `Write`), while every pattern in this file matches **command or path text**.
|
|
78
|
+
* Evaluating one against the other silently matches nothing — the SOP rules
|
|
79
|
+
* would have loaded, appeared in the audit trail as active, and never fired.
|
|
80
|
+
* Making the subject an explicit column means a rule cannot be pointed at the
|
|
81
|
+
* wrong string by omission.
|
|
82
|
+
*/
|
|
83
|
+
/** `action` is the space-padded action-token string the gate classifies a
|
|
84
|
+
* shell command to (`" action:deploy "`, from `ACTION_NEEDLES`) — the subject
|
|
85
|
+
* a `review_before: action:deploy` hold rule matches. */
|
|
86
|
+
export type GuardSubject = 'tool' | 'command' | 'target' | 'content' | 'action' | 'any';
|
|
87
|
+
export interface GuardPattern {
|
|
88
|
+
/** Stable id. Appears in the block message and the audit line, so it is the
|
|
89
|
+
* thing a support conversation is conducted in. Never renumber one. */
|
|
90
|
+
id: string;
|
|
91
|
+
/** Portable ERE ∩ JS RegExp source. */
|
|
92
|
+
source: string;
|
|
93
|
+
/** What {@link source} is matched against. Defaults to `any` — command and
|
|
94
|
+
* target, but never the tool name. `content` is the serialized tool input
|
|
95
|
+
* (`JSON.stringify(tool_input)` / `$TOOL_INPUT_JSON`), un-normalised — the
|
|
96
|
+
* same subject the WHERE argPattern machinery matches, so a content rule
|
|
97
|
+
* sees the bytes a Write would put on disk, not a whitespace-collapsed
|
|
98
|
+
* rendering of them. */
|
|
99
|
+
subject?: GuardSubject;
|
|
100
|
+
/** Matched case-insensitively (`grep -iE` / the JS `i` flag). */
|
|
101
|
+
ignoreCase?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* What the rule does when it matches.
|
|
104
|
+
*
|
|
105
|
+
* `block` refuses the call. `warn` allows it and emits `tool_flagged` — the
|
|
106
|
+
* advisory tier that earns a pattern its promotion to `block`, following
|
|
107
|
+
* `sslGateEvaluator.ts` rather than inventing a second discipline. `shadow`
|
|
108
|
+
* also allows, but emits `tool_would_block`: the rule is certain and the
|
|
109
|
+
* workspace's interventionMode asked us not to act on it. The two allow-ing
|
|
110
|
+
* severities are kept apart because a SHADOW rollout is decided on the second
|
|
111
|
+
* count, and collapsing them into `warn` made it unmeasurable. `hold`
|
|
112
|
+
* refuses the call like `block` but records it for a human (`tool_held`
|
|
113
|
+
* plus a v1 hold record) and lets the exact same call through once an
|
|
114
|
+
* approved bypass for it exists — the `review_before:` / `REQUIRE_APPROVAL:`
|
|
115
|
+
* tier, one mechanism in every gate since gate body v8.
|
|
116
|
+
*/
|
|
117
|
+
severity: 'block' | 'warn' | 'shadow' | 'hold';
|
|
118
|
+
/** Shown to the developer when it fires. Written for someone who is mid-task
|
|
119
|
+
* and now blocked, not for a security reviewer. */
|
|
120
|
+
reason: string;
|
|
121
|
+
/** Why the rule exists **and what it deliberately does not catch**. The second
|
|
122
|
+
* half is not optional: a guard whose limits are undocumented gets mistaken
|
|
123
|
+
* for containment. */
|
|
124
|
+
rationale: string;
|
|
125
|
+
/** Commands that MUST match. */
|
|
126
|
+
matches: readonly string[];
|
|
127
|
+
/** Commands that MUST NOT match — the false-positive contract. A pattern with
|
|
128
|
+
* fewer than three of these fails {@link assertGuardTableSane}, because the
|
|
129
|
+
* cheapest way to write a guard that never fires wrongly is to never think
|
|
130
|
+
* about the case where it would. */
|
|
131
|
+
notMatches: readonly string[];
|
|
132
|
+
/**
|
|
133
|
+
* Argument-level condition from a ` WHERE ` SOP rule, as JS `RegExp` source.
|
|
134
|
+
*
|
|
135
|
+
* When present, a rule whose {@link source} has matched (for SOP rules: the
|
|
136
|
+
* tool NAME) fires only if this also matches the serialized tool input —
|
|
137
|
+
* `JSON.stringify(tool_input ?? {})`, compact, insertion order, non-ASCII
|
|
138
|
+
* intact, the exact shape `matchSopRule` in the control plane and
|
|
139
|
+
* `serialise_tool_input` in intutic-clawde match against. Without this field
|
|
140
|
+
* a `BLOCK:^shell$ WHERE kubectl\s+apply(?!.*@sha256:)` rule reached the
|
|
141
|
+
* gates as "block shell unconditionally" — over-blocking `make test` while
|
|
142
|
+
* failing to express the condition it was written for.
|
|
143
|
+
*
|
|
144
|
+
* Deliberately NOT restricted to the portable ERE subset
|
|
145
|
+
* ({@link assertPortableEre}): it never runs under grep. The JS gates compile
|
|
146
|
+
* it as a `RegExp`; the bash gates delegate the match to `python3` `re`. The
|
|
147
|
+
* supported dialect is therefore the JS ∩ Python `re` intersection —
|
|
148
|
+
* character classes, alternation, anchors, quantifiers, `\s`, lookahead and
|
|
149
|
+
* lookbehind all agree; named groups do NOT (`(?<n>…)` vs `(?P<n>…)`), nor do
|
|
150
|
+
* some Unicode escapes. A pattern one engine cannot compile is enforced
|
|
151
|
+
* name-only by that gate and reported as `rule_downgraded`, never silently
|
|
152
|
+
* dropped — the same honest-divergence stance the LangGraph port documents in
|
|
153
|
+
* its snapshot reader.
|
|
154
|
+
*/
|
|
155
|
+
argPattern?: string;
|
|
156
|
+
}
|
|
157
|
+
export declare const NORMALISE_CONTRACT: {
|
|
158
|
+
/**
|
|
159
|
+
* JS: produce the string patterns are matched against.
|
|
160
|
+
*
|
|
161
|
+
* Compiled from {@link NORMALISE_JS_EXPR} rather than written out, so it
|
|
162
|
+
* cannot diverge from what the gates run. `unknown` in, because callers pass
|
|
163
|
+
* whatever a harness put in its payload.
|
|
164
|
+
*/
|
|
165
|
+
readonly js: (s: unknown) => string;
|
|
166
|
+
/** The JS function, emitted verbatim into every JavaScript gate. */
|
|
167
|
+
readonly jsSource: "function intuticNormalise(s) {\n return ' ' + String(s == null ? '' : s).replace(/\\s+/g, ' ') + ' ';\n}";
|
|
168
|
+
/**
|
|
169
|
+
* The Python function, emitted into the Open WebUI filter.
|
|
170
|
+
*
|
|
171
|
+
* `re.sub(r"\s+", " ", …)` rather than the more idiomatic `" ".join(s.split())`,
|
|
172
|
+
* and the difference is the one a test caught: `split()` **strips** leading and
|
|
173
|
+
* trailing whitespace, while the JS `replace(/\s+/g, ' ')` collapses it to a
|
|
174
|
+
* single space and keeps it. So `" a b "` normalised to `" a b "` in Python
|
|
175
|
+
* and `" a b "` in JS and bash. Both match the same patterns, since patterns
|
|
176
|
+
* use single-space boundaries — but "happens to be equivalent" is not the
|
|
177
|
+
* property this contract claims, and the next edit would not have been so lucky.
|
|
178
|
+
*
|
|
179
|
+
* Python's `\s` on a `str` covers Unicode whitespace (U+00A0, U+3000), matching
|
|
180
|
+
* JS. The shell fallback cannot, which is why SHELL_EXTRACT pre-passes.
|
|
181
|
+
*/
|
|
182
|
+
/** Requires `re` in scope — the emitted filter imports it. */
|
|
183
|
+
readonly pySource: string;
|
|
184
|
+
/**
|
|
185
|
+
* Shell: pure parameter expansion — no subshell, no `tr`, no `python3`.
|
|
186
|
+
*
|
|
187
|
+
* **ASCII only, and deliberately second in line.** Bash cannot express a
|
|
188
|
+
* Unicode whitespace class, so this collapses only the C0 set; U+00A0 would
|
|
189
|
+
* survive it and a pattern written with a literal space would then miss. The
|
|
190
|
+
* bash gates are not exposed to that because `SHELL_EXTRACT` runs every field
|
|
191
|
+
* through Python's `" ".join(str(v).split())` *before* this ever sees it —
|
|
192
|
+
* which is Unicode-aware. This is the belt to that pair of braces, and saying
|
|
193
|
+
* so here is the point: an earlier version of this comment claimed the three
|
|
194
|
+
* normalisers were provably identical, and they were not.
|
|
195
|
+
*/
|
|
196
|
+
readonly shell: string;
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* Rejects any pattern that would not mean the same thing to `grep -E` and to
|
|
200
|
+
* JavaScript.
|
|
201
|
+
*
|
|
202
|
+
* Runs at module load rather than from a test, so a non-portable pattern breaks
|
|
203
|
+
* the import — every writer, every test, immediately — instead of breaking one
|
|
204
|
+
* test that someone might skip. The failure this prevents is not hypothetical:
|
|
205
|
+
* `cursorHooks` and `windsurfHooks` shipped `\s+` for a year.
|
|
206
|
+
*
|
|
207
|
+
* `\s`, `\d`, `\w` and `\b` are forbidden **even though GNU grep accepts them**,
|
|
208
|
+
* because GNU grep is not the only grep — busybox and BSD do not, and a customer
|
|
209
|
+
* running us in an Alpine image would get a gate that silently matches nothing.
|
|
210
|
+
*/
|
|
211
|
+
export declare function assertPortableEre(source: string, id: string): void;
|
|
212
|
+
/**
|
|
213
|
+
* Commands that disarm governance itself.
|
|
214
|
+
*
|
|
215
|
+
* The union of the nine hand-rolled `BYPASS_PATTERNS` lists, plus the two holes
|
|
216
|
+
* the union exposed: `cursorHooks`/`windsurfHooks` never guarded `.intutic` at
|
|
217
|
+
* all, and every list guarded only its own harness's env kill-switch.
|
|
218
|
+
*/
|
|
219
|
+
export declare const GOVERNANCE_BYPASS_PATTERNS: readonly GuardPattern[];
|
|
220
|
+
/**
|
|
221
|
+
* Credential VALUES in the content an agent is about to write.
|
|
222
|
+
*
|
|
223
|
+
* The gap this closes, measured before it was built: the PreToolUse gate —
|
|
224
|
+
* the only enforcement point that runs before the harness executes a tool —
|
|
225
|
+
* matched tool name, target path and command, never content. A key the model
|
|
226
|
+
* had seen anywhere in context could be written into a config file and the
|
|
227
|
+
* gate had no rule that could notice; the proxy's DLP covers the same case
|
|
228
|
+
* only when LLM traffic actually routes through the proxy, so a
|
|
229
|
+
* hooks-installed-but-unproxied workspace had zero write-direction scanning.
|
|
230
|
+
* (GitGuardian's 2025 measurement of why this matters: AI-assisted commits
|
|
231
|
+
* shipped hardcoded secrets at 3.2% against a 1.5% human baseline.)
|
|
232
|
+
*
|
|
233
|
+
* Sources live in `@intutic/shared-types` (`secretPatterns.ts`) because the
|
|
234
|
+
* pre-commit scan in `tools/cli/src/lib/gitHooks.ts` greps the SAME shapes
|
|
235
|
+
* over the staged diff — two hand-kept copies of "what is a credential" is
|
|
236
|
+
* the drift this repo keeps paying for. Fixtures stay here, where the
|
|
237
|
+
* table's self-test runs.
|
|
238
|
+
*
|
|
239
|
+
* ## Severity: block, deliberately skipping the warn-first ramp
|
|
240
|
+
*
|
|
241
|
+
* DESTRUCTIVE_COMMAND_PATTERNS shipped at warn to earn promotion on measured
|
|
242
|
+
* false positives. These four ship at block because both halves of that
|
|
243
|
+
* trade differ: the patterns are prefix-anchored value shapes already
|
|
244
|
+
* hard-blocking in production on the MCP proxy's input path with no
|
|
245
|
+
* false-positive reports, and the failure being prevented — a credential in
|
|
246
|
+
* a file, then in a commit — is not undone by an undo (the key is burned;
|
|
247
|
+
* rotation is the only remedy). A warn that lets the write proceed IS the
|
|
248
|
+
* incident.
|
|
249
|
+
*
|
|
250
|
+
* ## What this deliberately does not catch
|
|
251
|
+
*
|
|
252
|
+
* A secret split across two Writes, base64/hex-encoded values, secrets
|
|
253
|
+
* assembled by concatenation in generated code, values reached through
|
|
254
|
+
* env-var indirection, and any credential format without a distinctive
|
|
255
|
+
* prefix. Those need entropy analysis and cross-call state that belong in
|
|
256
|
+
* the proxy's DLP, not in a gate that must stay fast and false-positive-free
|
|
257
|
+
* to keep its block tier trusted. This is the same floor-vs-DLP split the
|
|
258
|
+
* warn tier's own comment draws ("arguments and paths stay out — that is
|
|
259
|
+
* DLP's job"): the floor takes the four shapes precise enough to refuse on.
|
|
260
|
+
*/
|
|
261
|
+
export declare const SECRET_CONTENT_PATTERNS: readonly GuardPattern[];
|
|
262
|
+
/**
|
|
263
|
+
* Writes and edits targeting the agent-skill surface — `.agents/skills/**`
|
|
264
|
+
* and `.claude/skills/**`.
|
|
265
|
+
*
|
|
266
|
+
* TD-358 (updated 2026-08-18): these two entries stay `warn` and stay
|
|
267
|
+
* compiled into {@link staticFloorPatterns} — that part of the original
|
|
268
|
+
* decision is unchanged. What changed is what that tier assignment now
|
|
269
|
+
* *means*: it is the DEGRADED-MODE baseline, not the intended steady state.
|
|
270
|
+
* The steady state, everywhere a valid policy snapshot has landed, is `block`
|
|
271
|
+
* — shipped as `skill_surface.*.tier` entries through
|
|
272
|
+
* `services/sync-daemon/src/lib/policySnapshot.ts`'s `buildSnapshotRules`,
|
|
273
|
+
* gated behind `SKILL_SURFACE_TIER_SEVERITY`. See that constant's doc comment
|
|
274
|
+
* for why skill-directory *path* writes no longer sit behind the same
|
|
275
|
+
* false-positive-rate justification skill-CONTENT scanning still does — this
|
|
276
|
+
* entry only covers the half that stayed at `warn`.
|
|
277
|
+
*
|
|
278
|
+
* The two design choices below are for different reasons and worth
|
|
279
|
+
* separating:
|
|
280
|
+
*
|
|
281
|
+
* - **Why the FLOOR copy stays `warn`:** a compiled-in rule ships on every
|
|
282
|
+
* machine at once and can only be changed by a release — no one-sync-cycle
|
|
283
|
+
* undo if it turns out wrong. That is exactly the property a degraded-mode
|
|
284
|
+
* baseline needs (safe to run forever, unconditionally, on a machine that
|
|
285
|
+
* has never talked to the control plane) and exactly the property the
|
|
286
|
+
* steady-state decision does not need to have, since it lives in the
|
|
287
|
+
* snapshot and can be retracted in a cycle. Blocking here as well would
|
|
288
|
+
* collapse that distinction and reintroduce the un-retractable blast radius
|
|
289
|
+
* the snapshot channel exists to avoid.
|
|
290
|
+
* - **Why a floor copy exists at all, unlike `DESTRUCTIVE_COMMAND_PATTERNS`:**
|
|
291
|
+
* this pattern set is NOT content-conditional — it does not run
|
|
292
|
+
* `scanSkillContent` or judge what a skill file says, only WHERE a write
|
|
293
|
+
* targets. That is a deterministic, zero-ambiguity match (a write's target
|
|
294
|
+
* path either is or is not under `.agents/skills/` or `.claude/skills/`),
|
|
295
|
+
* so a workspace with no control-plane connectivity (offline, pre-`connect`,
|
|
296
|
+
* or a snapshot that failed its digest/workspace check) still gets an
|
|
297
|
+
* advisory signal from the moment the gate is written, rather than falling
|
|
298
|
+
* back to nothing.
|
|
299
|
+
*
|
|
300
|
+
* `subject: 'target'` deliberately, not `'any'`: this matches the tool's own
|
|
301
|
+
* path argument (`file_path`/`path`/…, the same extraction every Write/Edit
|
|
302
|
+
* gate already does), not a bare mention in a shell command. A `cat` or `grep`
|
|
303
|
+
* of a skill file is a read, not a write, and is not what this pattern
|
|
304
|
+
* exists to flag — narrower than `UNIVERSAL_PROTECTED_PATHS`'s "any mention"
|
|
305
|
+
* rule on purpose, since that rule blocks and this one only advises.
|
|
306
|
+
*
|
|
307
|
+
* A future promotion to `block` on skill CONTENT (not path) is a different,
|
|
308
|
+
* later, and still-open decision — it needs the corpus measurement TD-358
|
|
309
|
+
* describes, which this change does not supply. It should ship through the
|
|
310
|
+
* same policy-snapshot channel the way this path-tier promotion and
|
|
311
|
+
* `DESTRUCTIVE_COMMAND_PATTERNS` both do, so it too is retractable in a sync
|
|
312
|
+
* cycle rather than a release if the measurement turns out wrong.
|
|
313
|
+
*/
|
|
314
|
+
export declare const SKILL_SURFACE_PATTERNS: readonly GuardPattern[];
|
|
315
|
+
/**
|
|
316
|
+
* Commands that destroy the machine or its data irrecoverably.
|
|
317
|
+
*
|
|
318
|
+
* TD-309: none of these were blocked by any harness. They are shipped through
|
|
319
|
+
* the policy snapshot rather than compiled into the gate, and at `warn` before
|
|
320
|
+
* `block` — see the note on `severity` below.
|
|
321
|
+
*
|
|
322
|
+
* ## What is deliberately allowed
|
|
323
|
+
*
|
|
324
|
+
* `rm -rf ./`, `rm -rf .`, `rm -rf *`, `rm -rf build/`, `rm -rf node_modules`
|
|
325
|
+
* and `rm -rf /home/…` are all ordinary developer commands and all pass. That
|
|
326
|
+
* is not an oversight to tighten later. A guard that blocks routine work
|
|
327
|
+
* teaches the developer to run `chflags nouchg` on the hook — the exact bypass
|
|
328
|
+
* `GOVERNANCE_BYPASS_PATTERNS` exists to stop. Blocking too much manufactures
|
|
329
|
+
* the adversary the rest of this file defends against.
|
|
330
|
+
*
|
|
331
|
+
* ## What no pattern here catches
|
|
332
|
+
*
|
|
333
|
+
* Shell redirection to a raw device (`cat /dev/zero > /dev/sda`), obfuscation
|
|
334
|
+
* (`eval "$(printf cm0gLXJmIC8= | base64 -d)"`), anything reached through a
|
|
335
|
+
* script file rather than typed as a command, and any of these run by a process
|
|
336
|
+
* the agent spawns rather than by the agent. This tier is a speed bump against
|
|
337
|
+
* accident and casual bypass. Containment is the proxy and OS permissions.
|
|
338
|
+
*/
|
|
339
|
+
export declare const DESTRUCTIVE_COMMAND_PATTERNS: readonly GuardPattern[];
|
|
340
|
+
export declare function protectedPathShellPatterns(): readonly GuardPattern[];
|
|
341
|
+
/** Every compiled-in guard: the static floor. Destructive patterns are **not**
|
|
342
|
+
* here — they ship through the policy snapshot so they can be retracted
|
|
343
|
+
* without a release. `SKILL_SURFACE_PATTERNS` IS here, deliberately, despite
|
|
344
|
+
* also being new: unlike the destructive tier it is a deterministic path
|
|
345
|
+
* match with no content judgement to get wrong, so there is no measurement
|
|
346
|
+
* risk to hold it back from — see its own doc comment. As of TD-358's
|
|
347
|
+
* block-tier promotion these two entries are the DEGRADED-MODE baseline
|
|
348
|
+
* only: the steady state is `block`, delivered through the policy snapshot
|
|
349
|
+
* (`SKILL_SURFACE_TIER_SEVERITY` in `lib/policySnapshot.ts`) and additive on
|
|
350
|
+
* top of these, exactly the way the destructive tier layers on top of this
|
|
351
|
+
* same floor. */
|
|
352
|
+
export declare function staticFloorPatterns(): readonly GuardPattern[];
|
|
353
|
+
//# sourceMappingURL=protectedPaths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protectedPaths.d.ts","sourceRoot":"","sources":["../../src/harness/protectedPaths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAIH,iEAAiE;AACjE,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EAiEtD,CAAA;AAMD;;;;;;;GAOG;AACH;;;;;;;;;GASG;AACH;;0DAE0D;AAC1D,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAA;AAEvF,MAAM,WAAW,YAAY;IAC3B;4EACwE;IACxE,EAAE,EAAE,MAAM,CAAA;IACV,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAA;IACd;;;;;6BAKyB;IACzB,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,iEAAiE;IACjE,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;;;;;;;;;OAcG;IACH,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAA;IAC9C;wDACoD;IACpD,MAAM,EAAE,MAAM,CAAA;IACd;;2BAEuB;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,gCAAgC;IAChC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1B;;;yCAGqC;IACrC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IAC7B;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAoCD,eAAO,MAAM,kBAAkB;IAC7B;;;;;;OAMG;iBACqD,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM;IAE9E,oEAAoE;;IAGpE;;;;;;;;;;;;;OAaG;IACH,8DAA8D;;IAM9D;;;;;;;;;;;OAWG;;CASK,CAAA;AAEV;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CA2BlE;AA0CD;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAAS,YAAY,EAyD5D,CAAA;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,YAAY,EAyG1D,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,YAAY,EA+CxD,CAAA;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,4BAA4B,EAAE,SAAS,YAAY,EAmJ/D,CAAA;AA2CD,wBAAgB,0BAA0B,IAAI,SAAS,YAAY,EAAE,CAEpE;AAED;;;;;;;;;;kBAUkB;AAClB,wBAAgB,mBAAmB,IAAI,SAAS,YAAY,EAAE,CAO7D"}
|