@sunaiva/gate 1.0.0 → 1.1.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/BUSINESS_LICENSE.md +70 -0
- package/CHANGELOG.md +148 -0
- package/LICENSE +0 -0
- package/README.md +411 -27
- package/dist/config/defaults.d.ts +22 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +56 -8
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/loader.d.ts +0 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +24 -6
- package/dist/config/loader.js.map +1 -1
- package/dist/engine/backend-client.d.ts +58 -0
- package/dist/engine/backend-client.d.ts.map +1 -0
- package/dist/engine/backend-client.js +287 -0
- package/dist/engine/backend-client.js.map +1 -0
- package/dist/engine/hmac-verifier.d.ts +33 -0
- package/dist/engine/hmac-verifier.d.ts.map +1 -0
- package/dist/engine/hmac-verifier.js +161 -0
- package/dist/engine/hmac-verifier.js.map +1 -0
- package/dist/engine/immutability.d.ts +59 -0
- package/dist/engine/immutability.d.ts.map +1 -0
- package/dist/engine/immutability.js +129 -0
- package/dist/engine/immutability.js.map +1 -0
- package/dist/engine/pattern-matcher.d.ts +13 -0
- package/dist/engine/pattern-matcher.d.ts.map +1 -1
- package/dist/engine/pattern-matcher.js +85 -17
- package/dist/engine/pattern-matcher.js.map +1 -1
- package/dist/engine/rule-engine.d.ts +62 -1
- package/dist/engine/rule-engine.d.ts.map +1 -1
- package/dist/engine/rule-engine.js +222 -12
- package/dist/engine/rule-engine.js.map +1 -1
- package/dist/engine/session-state.d.ts +0 -0
- package/dist/engine/session-state.d.ts.map +1 -1
- package/dist/engine/session-state.js +8 -2
- package/dist/engine/session-state.js.map +1 -1
- package/dist/engine/ship-confidence-gate.d.ts +184 -0
- package/dist/engine/ship-confidence-gate.d.ts.map +1 -0
- package/dist/engine/ship-confidence-gate.js +768 -0
- package/dist/engine/ship-confidence-gate.js.map +1 -0
- package/dist/index.d.ts +0 -0
- package/dist/index.d.ts.map +0 -0
- package/dist/index.js +289 -2
- package/dist/index.js.map +1 -1
- package/dist/rules/categories.json +0 -0
- package/dist/rules/presets.json +0 -0
- package/dist/rules/rules.json +200 -100
- package/dist/tools/audit.d.ts +6 -0
- package/dist/tools/audit.d.ts.map +1 -1
- package/dist/tools/audit.js +43 -6
- package/dist/tools/audit.js.map +1 -1
- package/dist/tools/bypass.d.ts +0 -0
- package/dist/tools/bypass.d.ts.map +1 -1
- package/dist/tools/bypass.js +50 -6
- package/dist/tools/bypass.js.map +1 -1
- package/dist/tools/rules.d.ts +0 -0
- package/dist/tools/rules.d.ts.map +0 -0
- package/dist/tools/rules.js +0 -0
- package/dist/tools/rules.js.map +0 -0
- package/dist/tools/ship-confidence.d.ts +11 -0
- package/dist/tools/ship-confidence.d.ts.map +1 -0
- package/dist/tools/ship-confidence.js +42 -0
- package/dist/tools/ship-confidence.js.map +1 -0
- package/dist/tools/update.d.ts +0 -0
- package/dist/tools/update.d.ts.map +1 -1
- package/dist/tools/update.js +45 -9
- package/dist/tools/update.js.map +1 -1
- package/dist/tools/validate.d.ts +0 -0
- package/dist/tools/validate.d.ts.map +1 -1
- package/dist/tools/validate.js +56 -4
- package/dist/tools/validate.js.map +1 -1
- package/dist/types/backend.d.ts +69 -0
- package/dist/types/backend.d.ts.map +1 -0
- package/dist/types/backend.js +18 -0
- package/dist/types/backend.js.map +1 -0
- package/package.json +11 -3
|
@@ -1,56 +1,266 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Rule Engine — loads rules.json, filters active rules, evaluates actions.
|
|
3
|
+
*
|
|
4
|
+
* Severity model (v1.0.1):
|
|
5
|
+
* - HARD = constitutional rules (always block on match, no enforcement_mode escape)
|
|
6
|
+
* - MEDIUM = standard rule with severity=block (warn-then-block)
|
|
7
|
+
* - SOFT = standard rule with severity=warn (warn only, never block)
|
|
8
|
+
*
|
|
9
|
+
* Premium rules: rules with `backend_required: true` and stub detection_pattern
|
|
10
|
+
* are skipped locally unless a backend URL is configured. Future versions will
|
|
11
|
+
* call the backend over HTTP for these.
|
|
3
12
|
*/
|
|
4
|
-
import { readFileSync } from "fs";
|
|
13
|
+
import { readFileSync, existsSync } from "fs";
|
|
5
14
|
import { join, dirname } from "path";
|
|
6
15
|
import { fileURLToPath } from "url";
|
|
7
16
|
import { matchAction } from "./pattern-matcher.js";
|
|
17
|
+
import { BackendClient } from "./backend-client.js";
|
|
8
18
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
//
|
|
10
|
-
|
|
19
|
+
// Try dist layout first (../rules from dist/engine/), then source layout
|
|
20
|
+
// (../../rules from src/engine/). This lets the same code run under tsc-built
|
|
21
|
+
// dist AND under ts-jest from source without a build step.
|
|
22
|
+
function resolveRulesPath() {
|
|
23
|
+
const candidates = [
|
|
24
|
+
join(__dirname, "../rules/rules.json"), // dist: dist/engine -> dist/rules (preferred)
|
|
25
|
+
join(__dirname, "../../dist/rules/rules.json"), // src: src/engine -> dist/rules (test runs from src after build)
|
|
26
|
+
join(__dirname, "../../rules/rules.json"), // src: src/engine -> rules (raw fallback, no backend_required flags)
|
|
27
|
+
];
|
|
28
|
+
for (const c of candidates) {
|
|
29
|
+
if (existsSync(c))
|
|
30
|
+
return c;
|
|
31
|
+
}
|
|
32
|
+
return candidates[0]; // fall back; loadAllRules will fail-open
|
|
33
|
+
}
|
|
34
|
+
const RULES_PATH = resolveRulesPath();
|
|
11
35
|
let _rules = null;
|
|
36
|
+
// ----------------------------------------------------------------------
|
|
37
|
+
// Backend client wiring (B4 closes the orphan B3 left behind)
|
|
38
|
+
// ----------------------------------------------------------------------
|
|
39
|
+
// A single process-wide BackendClient. Lazy-constructed on first use so
|
|
40
|
+
// the no-backend path stays cheap. When SUNAIVA_GATE_API_TOKEN is unset
|
|
41
|
+
// the client still works — it returns `skipped_no_token` for every rule
|
|
42
|
+
// and writes the once-per-process stderr notice.
|
|
43
|
+
let _backendClient = null;
|
|
44
|
+
/** Lazy singleton. Tests can override via setBackendClient(). */
|
|
45
|
+
function getBackendClient() {
|
|
46
|
+
if (!_backendClient)
|
|
47
|
+
_backendClient = new BackendClient();
|
|
48
|
+
return _backendClient;
|
|
49
|
+
}
|
|
50
|
+
/** Test-only injection point. */
|
|
51
|
+
export function setBackendClient(client) {
|
|
52
|
+
_backendClient = client;
|
|
53
|
+
}
|
|
54
|
+
/** True iff the premium backend is fully configured (token present). */
|
|
55
|
+
function isBackendConfigured() {
|
|
56
|
+
return getBackendClient().isConfigured();
|
|
57
|
+
}
|
|
12
58
|
export function loadAllRules() {
|
|
13
59
|
if (_rules)
|
|
14
60
|
return _rules;
|
|
15
|
-
|
|
16
|
-
|
|
61
|
+
try {
|
|
62
|
+
const raw = readFileSync(RULES_PATH, "utf-8");
|
|
63
|
+
_rules = JSON.parse(raw);
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
// FAIL-OPEN: never crash the MCP server on bad rules file.
|
|
67
|
+
// Stderr log so operator sees the problem; serve no rules.
|
|
68
|
+
console.error(`[sunaiva-gate] WARN: could not load rules.json at ${RULES_PATH}: ` +
|
|
69
|
+
(err instanceof Error ? err.message : String(err)));
|
|
70
|
+
_rules = [];
|
|
71
|
+
}
|
|
17
72
|
return _rules;
|
|
18
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Returns ONLY the rules whose `enforcement === "constitutional"`.
|
|
76
|
+
*
|
|
77
|
+
* This is the canonical "what is constitutional" function used by
|
|
78
|
+
* B4's immutability guard. It reads from the package-bundled rules
|
|
79
|
+
* (dist/rules/rules.json), never from the user-editable on-disk
|
|
80
|
+
* copy at ~/.sunaiva/rules.json — so a user cannot circumvent the
|
|
81
|
+
* guard by hand-editing their config.
|
|
82
|
+
*
|
|
83
|
+
* Cached via loadAllRules().
|
|
84
|
+
*/
|
|
85
|
+
export function loadConstitutionalRulesOnly() {
|
|
86
|
+
return loadAllRules().filter((r) => r &&
|
|
87
|
+
(r.enforcement === "constitutional" || r.constitutional === true));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Resolved path that loadAllRules() reads from. Exported for tests
|
|
91
|
+
* and diagnostic tooling (e.g. verify-bundle / smoke-test reporting).
|
|
92
|
+
*/
|
|
93
|
+
export function getResolvedRulesPath() {
|
|
94
|
+
return RULES_PATH;
|
|
95
|
+
}
|
|
19
96
|
export function getActiveRules(config) {
|
|
20
97
|
const all = loadAllRules();
|
|
21
98
|
return all.filter((r) => config.active_rules.includes(r.id));
|
|
22
99
|
}
|
|
100
|
+
/** Returns true if the rule should be enforced locally. */
|
|
101
|
+
function isLocalRule(rule, backendConfigured) {
|
|
102
|
+
if (!rule.backend_required)
|
|
103
|
+
return true;
|
|
104
|
+
// Premium rule + backend configured => future HTTP path. Until that lands,
|
|
105
|
+
// we still skip locally and log it so the caller can see what was deferred.
|
|
106
|
+
return backendConfigured;
|
|
107
|
+
}
|
|
108
|
+
/** Map a rule + enforcement_mode to the severity tier label. */
|
|
109
|
+
function deriveSeverity(rule) {
|
|
110
|
+
if (rule.enforcement === "constitutional")
|
|
111
|
+
return "HARD";
|
|
112
|
+
if (rule.severity === "warn")
|
|
113
|
+
return "SOFT";
|
|
114
|
+
// standard + block or warn-then-block
|
|
115
|
+
return "MEDIUM";
|
|
116
|
+
}
|
|
23
117
|
export function evaluateAction(action, config, warningCounts, context) {
|
|
24
118
|
const activeRules = getActiveRules(config);
|
|
25
119
|
const combined = context ? `${action} ${context}` : action;
|
|
26
120
|
const violations = [];
|
|
27
121
|
const warnings = [];
|
|
122
|
+
const wouldHaveBlocked = [];
|
|
123
|
+
const skippedPremium = [];
|
|
124
|
+
// B4: sync evaluateAction always skips premium rules locally — the
|
|
125
|
+
// BackendClient lives behind the async wrapper `evaluateActionAsync`.
|
|
126
|
+
// This keeps the smoke test + legacy sync callers synchronous. Premium
|
|
127
|
+
// rules show up in skipped_premium; the async wrapper then enriches the
|
|
128
|
+
// result with backend_results when SUNAIVA_GATE_API_TOKEN is set.
|
|
129
|
+
const backendConfigured = false;
|
|
130
|
+
let maxSeverity = null;
|
|
131
|
+
const bumpSeverity = (s) => {
|
|
132
|
+
if (maxSeverity === null)
|
|
133
|
+
maxSeverity = s;
|
|
134
|
+
else if (s === "HARD")
|
|
135
|
+
maxSeverity = "HARD";
|
|
136
|
+
else if (s === "MEDIUM" && maxSeverity === "SOFT")
|
|
137
|
+
maxSeverity = "MEDIUM";
|
|
138
|
+
};
|
|
28
139
|
for (const rule of activeRules) {
|
|
140
|
+
if (!isLocalRule(rule, backendConfigured)) {
|
|
141
|
+
skippedPremium.push(rule.id);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
29
144
|
const result = matchAction(combined, rule.detection_pattern);
|
|
30
145
|
if (!result.matched)
|
|
31
146
|
continue;
|
|
147
|
+
const tier = deriveSeverity(rule);
|
|
148
|
+
bumpSeverity(tier);
|
|
32
149
|
if (rule.enforcement === "constitutional") {
|
|
33
|
-
//
|
|
150
|
+
// HARD: always block, regardless of enforcement_mode.
|
|
34
151
|
violations.push(rule);
|
|
152
|
+
wouldHaveBlocked.push(rule.id);
|
|
153
|
+
}
|
|
154
|
+
else if (tier === "SOFT") {
|
|
155
|
+
// SOFT (warn severity): warning only, never block.
|
|
156
|
+
warnings.push(rule);
|
|
35
157
|
}
|
|
36
158
|
else {
|
|
37
|
-
//
|
|
159
|
+
// MEDIUM (standard + block): warn-then-block ladder.
|
|
38
160
|
const count = warningCounts.get(rule.id) ?? 0;
|
|
39
|
-
if (config.enforcement_mode === "warn-only") {
|
|
40
|
-
warnings.push(rule);
|
|
41
|
-
}
|
|
42
|
-
else if (config.enforcement_mode === "audit") {
|
|
161
|
+
if (config.enforcement_mode === "warn-only" || config.enforcement_mode === "audit") {
|
|
43
162
|
warnings.push(rule);
|
|
163
|
+
wouldHaveBlocked.push(rule.id);
|
|
44
164
|
}
|
|
45
165
|
else if (count === 0) {
|
|
46
166
|
warnings.push(rule);
|
|
47
167
|
}
|
|
48
168
|
else {
|
|
49
169
|
violations.push(rule);
|
|
170
|
+
wouldHaveBlocked.push(rule.id);
|
|
50
171
|
}
|
|
51
172
|
}
|
|
52
173
|
}
|
|
53
174
|
const allowed = violations.length === 0;
|
|
54
|
-
return {
|
|
175
|
+
return {
|
|
176
|
+
allowed,
|
|
177
|
+
violations,
|
|
178
|
+
warnings,
|
|
179
|
+
severity_tier: maxSeverity,
|
|
180
|
+
would_have_blocked: wouldHaveBlocked,
|
|
181
|
+
skipped_premium: skippedPremium,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Async wrapper around evaluateAction that ALSO delegates premium
|
|
186
|
+
* (`backend_required: true`) rules to the BackendClient. Used by
|
|
187
|
+
* `handleValidateAction` when SUNAIVA_GATE_API_TOKEN is set so premium
|
|
188
|
+
* rules actually get evaluated server-side instead of silently skipped.
|
|
189
|
+
*
|
|
190
|
+
* Failure mode: any backend error (network, 5xx, missing token) becomes
|
|
191
|
+
* a `skipped_*` status on the per-rule result. The BackendClient itself
|
|
192
|
+
* is fail-OPEN for the customer — a Sunaiva outage never blocks the user.
|
|
193
|
+
*
|
|
194
|
+
* Premium-rule semantics:
|
|
195
|
+
* - rule_id in skipped_premium → backend was NOT consulted (sync path)
|
|
196
|
+
* - rule_id in backend_results → backend WAS consulted; check status field
|
|
197
|
+
* - status === "matched" → backend wants to block; promoted to violation
|
|
198
|
+
* - status === "no_match" → backend evaluated clean
|
|
199
|
+
* - status === "skipped_*" → fail-OPEN (rule deferred, see error field)
|
|
200
|
+
*
|
|
201
|
+
* Tests #7 (token unset → skipped_no_token) lives here.
|
|
202
|
+
*/
|
|
203
|
+
export async function evaluateActionAsync(action, config, warningCounts, context) {
|
|
204
|
+
const sync = evaluateAction(action, config, warningCounts, context);
|
|
205
|
+
// Premium rule IDs that the sync pass deferred to the backend.
|
|
206
|
+
const activeRules = getActiveRules(config);
|
|
207
|
+
const premiumRules = activeRules.filter((r) => r.backend_required === true && sync.skipped_premium.includes(r.id));
|
|
208
|
+
if (premiumRules.length === 0)
|
|
209
|
+
return sync;
|
|
210
|
+
const combined = context ? `${action} ${context}` : action;
|
|
211
|
+
const client = getBackendClient();
|
|
212
|
+
const backendEval = await client.evaluate(premiumRules.map((r) => r.id), combined, { action_preview: action.slice(0, 200) });
|
|
213
|
+
// Promote backend-matched premium rules into violations / warnings
|
|
214
|
+
// following the same severity-tier ladder used by the local engine.
|
|
215
|
+
const violations = [...sync.violations];
|
|
216
|
+
const warnings = [...sync.warnings];
|
|
217
|
+
const wouldHaveBlocked = [...sync.would_have_blocked];
|
|
218
|
+
let maxSeverity = sync.severity_tier;
|
|
219
|
+
const bump = (s) => {
|
|
220
|
+
if (maxSeverity === null)
|
|
221
|
+
maxSeverity = s;
|
|
222
|
+
else if (s === "HARD")
|
|
223
|
+
maxSeverity = "HARD";
|
|
224
|
+
else if (s === "MEDIUM" && maxSeverity === "SOFT")
|
|
225
|
+
maxSeverity = "MEDIUM";
|
|
226
|
+
};
|
|
227
|
+
for (const r of backendEval.results) {
|
|
228
|
+
if (!r.matched)
|
|
229
|
+
continue;
|
|
230
|
+
const rule = premiumRules.find((p) => p.id === r.rule_id);
|
|
231
|
+
if (!rule)
|
|
232
|
+
continue;
|
|
233
|
+
// Backend severity overrides local; default to MEDIUM when unspecified.
|
|
234
|
+
const tier = rule.enforcement === "constitutional"
|
|
235
|
+
? "HARD"
|
|
236
|
+
: r.severity === "warn"
|
|
237
|
+
? "SOFT"
|
|
238
|
+
: "MEDIUM";
|
|
239
|
+
bump(tier);
|
|
240
|
+
if (tier === "SOFT") {
|
|
241
|
+
warnings.push(rule);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
violations.push(rule);
|
|
245
|
+
wouldHaveBlocked.push(rule.id);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// Premium rules that the backend handled are no longer "skipped".
|
|
249
|
+
const skippedPremium = sync.skipped_premium.filter((id) => !backendEval.results.some((r) => r.rule_id === id && r.status !== "skipped_no_token" && r.status !== "skipped_disabled" && !r.status.startsWith("skipped_")));
|
|
250
|
+
// Inverse: keep the rules that ARE still skipped (token absent or backend error).
|
|
251
|
+
const stillSkipped = backendEval.results
|
|
252
|
+
.filter((r) => r.status.startsWith("skipped_"))
|
|
253
|
+
.map((r) => r.rule_id);
|
|
254
|
+
// Union local-deferred + still-skipped (de-dup).
|
|
255
|
+
const finalSkipped = Array.from(new Set([...skippedPremium, ...stillSkipped]));
|
|
256
|
+
return {
|
|
257
|
+
allowed: violations.length === 0,
|
|
258
|
+
violations,
|
|
259
|
+
warnings,
|
|
260
|
+
severity_tier: maxSeverity,
|
|
261
|
+
would_have_blocked: wouldHaveBlocked,
|
|
262
|
+
skipped_premium: finalSkipped,
|
|
263
|
+
backend_results: backendEval.results,
|
|
264
|
+
};
|
|
55
265
|
}
|
|
56
266
|
//# sourceMappingURL=rule-engine.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-engine.js","sourceRoot":"","sources":["../../src/engine/rule-engine.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"rule-engine.js","sourceRoot":"","sources":["../../src/engine/rule-engine.ts"],"names":[],"mappings":"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"}
|
|
File without changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-state.d.ts","sourceRoot":"","sources":["../../src/engine/session-state.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"session-state.d.ts","sourceRoot":"","sources":["../../src/engine/session-state.ts"],"names":[],"mappings":"AAOA,UAAU,YAAY;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAMD,wBAAgB,QAAQ,IAAI,YAAY,CAGvC;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,YAAY,GAAG,IAAI,CAG/C;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKpD;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,gBAAgB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAGtD;AAED,wBAAgB,eAAe,IAAI,IAAI,CAItC;AAED,wBAAgB,UAAU,IAAI,IAAI,CAEjC"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
// Cross-platform tempdir (Windows: %LOCALAPPDATA%\Temp, Unix: /tmp)
|
|
5
|
+
const STATE_FILE = join(tmpdir(), "sunaiva_gate_session.json");
|
|
3
6
|
function defaultState() {
|
|
4
7
|
return { session_start: new Date().toISOString(), action_count: 0, warnings_issued: {} };
|
|
5
8
|
}
|
|
@@ -12,7 +15,10 @@ export function getState() {
|
|
|
12
15
|
}
|
|
13
16
|
}
|
|
14
17
|
export function saveState(s) {
|
|
15
|
-
|
|
18
|
+
try {
|
|
19
|
+
writeFileSync(STATE_FILE, JSON.stringify(s, null, 2));
|
|
20
|
+
}
|
|
21
|
+
catch { /* fail-OPEN: never crash because session state is unwritable */ }
|
|
16
22
|
}
|
|
17
23
|
export function recordWarning(ruleId) {
|
|
18
24
|
const s = getState();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-state.js","sourceRoot":"","sources":["../../src/engine/session-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"session-state.js","sourceRoot":"","sources":["../../src/engine/session-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,oEAAoE;AACpE,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,2BAA2B,CAAC,CAAC;AAQ/D,SAAS,YAAY;IACnB,OAAO,EAAE,aAAa,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC;AAC3F,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,IAAI,CAAC;QAAC,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;IAAC,CAAC;IAC7D,MAAM,CAAC;QAAC,OAAO,YAAY,EAAE,CAAC;IAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,CAAe;IACvC,IAAI,CAAC;QAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAAC,CAAC;IAC9D,MAAM,CAAC,CAAC,gEAAgE,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;IACrB,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IACjE,SAAS,CAAC,CAAC,CAAC,CAAC;IACb,OAAO,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,OAAO,QAAQ,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;IACrB,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;IACrB,CAAC,CAAC,YAAY,EAAE,CAAC;IACjB,SAAS,CAAC,CAAC,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
export declare const HOOK_NAME = "ship_confidence_gate";
|
|
2
|
+
export declare const HOOK_VERSION = "1.2.0";
|
|
3
|
+
export declare const RULE_NAME = "Rule 42 \u2014 Ship Confidence Gate";
|
|
4
|
+
export declare const RULE_VERSION = "1.0";
|
|
5
|
+
export type GateDecision = "allow" | "block" | "absent" | "skip_key";
|
|
6
|
+
export interface GateEvidence {
|
|
7
|
+
verdict_path?: string;
|
|
8
|
+
verdict_id?: string;
|
|
9
|
+
level?: string;
|
|
10
|
+
signed_at?: string;
|
|
11
|
+
signature_algorithm?: string;
|
|
12
|
+
age_minutes?: number;
|
|
13
|
+
token_path?: string;
|
|
14
|
+
paid_fallthrough_reason?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface GateResult {
|
|
17
|
+
/** Final policy decision after BOTH tiers are evaluated. */
|
|
18
|
+
decision: "allow" | "block";
|
|
19
|
+
/** Which tier produced the result (paid > free; null on block/error). */
|
|
20
|
+
tier: "paid" | "free" | null;
|
|
21
|
+
/** Reason string (human-readable). */
|
|
22
|
+
reason: string;
|
|
23
|
+
/** Structured evidence for the audit ledger. */
|
|
24
|
+
evidence: GateEvidence;
|
|
25
|
+
/** Verdict id if a verdict file was inspected. */
|
|
26
|
+
verdict_id?: string;
|
|
27
|
+
/** Verdict level if checked. */
|
|
28
|
+
level?: string;
|
|
29
|
+
/** signed_at ISO if checked. */
|
|
30
|
+
signed_at?: string;
|
|
31
|
+
/** Age in minutes if checked. */
|
|
32
|
+
age_minutes?: number;
|
|
33
|
+
/** Self-stamp block (matching the Python _stamp field). */
|
|
34
|
+
stamp: GateStamp;
|
|
35
|
+
/** When dry-run is active, the would-have-been decision is preserved here. */
|
|
36
|
+
would_have_been?: "allow" | "block";
|
|
37
|
+
/** When kill-switch active. */
|
|
38
|
+
bypassed?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export interface GateStamp {
|
|
41
|
+
hook_name: string;
|
|
42
|
+
hook_version: string;
|
|
43
|
+
rule_name: string;
|
|
44
|
+
rule_version: string;
|
|
45
|
+
why: string;
|
|
46
|
+
suggested_fix: string;
|
|
47
|
+
artifact_id: string;
|
|
48
|
+
command_preview: string;
|
|
49
|
+
timestamp: string;
|
|
50
|
+
label?: string;
|
|
51
|
+
tier?: "paid" | "free" | null;
|
|
52
|
+
}
|
|
53
|
+
export interface PaidCheckResult {
|
|
54
|
+
decision: GateDecision;
|
|
55
|
+
reason: string;
|
|
56
|
+
evidence: GateEvidence | null;
|
|
57
|
+
}
|
|
58
|
+
export interface GateOptions {
|
|
59
|
+
/** Override the verdict directory. Default: <genesisRoot>/data/ship_confidence_verdicts */
|
|
60
|
+
verdictDir?: string;
|
|
61
|
+
/** Override the approval token directory. Default: <genesisRoot>/data/deploy_queue/APPROVAL_TOKENS */
|
|
62
|
+
approvalDir?: string;
|
|
63
|
+
/** Max age of a signed verdict before it's considered stale (minutes). Default 60. */
|
|
64
|
+
maxAgeMinutes?: number;
|
|
65
|
+
/** Env var name holding the HMAC signing key. Default SHIP_CONFIDENCE_SIGNING_KEY. */
|
|
66
|
+
signingKeyEnv?: string;
|
|
67
|
+
/** Verdict levels that grant allow. Default ["GREEN"]. */
|
|
68
|
+
allowedLevels?: string[];
|
|
69
|
+
/** Token TTL in seconds. Default 3600. */
|
|
70
|
+
tokenTtlSeconds?: number;
|
|
71
|
+
/** Override the genesis root for path resolution. */
|
|
72
|
+
genesisRoot?: string;
|
|
73
|
+
/** Override the audit log path. */
|
|
74
|
+
auditLogPath?: string;
|
|
75
|
+
/** Override env (for tests). Defaults to process.env. */
|
|
76
|
+
env?: NodeJS.ProcessEnv;
|
|
77
|
+
}
|
|
78
|
+
/** Match the Python _sanitize: non-[A-Za-z0-9._-] → '-', strip dashes, max 128. */
|
|
79
|
+
export declare function sanitizeArtifactId(artifactId: string): string;
|
|
80
|
+
/**
|
|
81
|
+
* Detect the genesis-system root, matching the Python _detect_genesis_root logic.
|
|
82
|
+
* Priority: env override → WSL path → Windows path → fall back to a path that
|
|
83
|
+
* may not exist (so log paths stay consistent).
|
|
84
|
+
*/
|
|
85
|
+
declare function detectGenesisRoot(env: NodeJS.ProcessEnv): string;
|
|
86
|
+
/** Parse signed_at ISO timestamps tolerantly (accepts 'Z' suffix). */
|
|
87
|
+
declare function parseSignedAt(raw: unknown): Date | null;
|
|
88
|
+
export declare class ShipConfidenceGate {
|
|
89
|
+
private readonly verdictDir;
|
|
90
|
+
private readonly approvalDir;
|
|
91
|
+
private readonly maxAgeMinutes;
|
|
92
|
+
private readonly signingKeyEnv;
|
|
93
|
+
private readonly allowedLevels;
|
|
94
|
+
private readonly tokenTtlSeconds;
|
|
95
|
+
private readonly genesisRoot;
|
|
96
|
+
private readonly auditLogPath;
|
|
97
|
+
private readonly env;
|
|
98
|
+
constructor(opts?: GateOptions);
|
|
99
|
+
/**
|
|
100
|
+
* Look up and verify the signed Verdict for an artifact.
|
|
101
|
+
*
|
|
102
|
+
* Return-value semantics mirror the Python _check_signed_verdict:
|
|
103
|
+
* - "allow" : valid signature, allowed level, fresh → grant
|
|
104
|
+
* - "block" : verdict present but fails policy → HARD block
|
|
105
|
+
* - "absent" : no verdict file → caller tries free-tier fallback
|
|
106
|
+
* - "skip_key" : verdict present but signing key env unset → fall through
|
|
107
|
+
*
|
|
108
|
+
* NEVER throws. Any unexpected error → "absent" (the outer fail-open handler
|
|
109
|
+
* in check() preserves overall fail-OPEN semantics).
|
|
110
|
+
*/
|
|
111
|
+
checkSignedVerdict(artifactId: string): Promise<PaidCheckResult>;
|
|
112
|
+
/**
|
|
113
|
+
* Find a fresh approval token matching the artifact_id.
|
|
114
|
+
* Token must be JSON with at minimum {artifact_id, timestamp}.
|
|
115
|
+
*/
|
|
116
|
+
findApprovalToken(artifactId: string): Promise<string | null>;
|
|
117
|
+
/** Single-use token consumption. Never throws. */
|
|
118
|
+
consumeToken(tokenPath: string): Promise<void>;
|
|
119
|
+
/** Self-stamped allow payload (matches Python format_allow + _stamp). */
|
|
120
|
+
formatAllow(opts: {
|
|
121
|
+
artifactId: string;
|
|
122
|
+
tier: "paid" | "free" | null;
|
|
123
|
+
why: string;
|
|
124
|
+
commandPreview?: string;
|
|
125
|
+
label?: string;
|
|
126
|
+
extraContext?: string;
|
|
127
|
+
}): {
|
|
128
|
+
continue: true;
|
|
129
|
+
additionalContext: string;
|
|
130
|
+
_stamp: GateStamp;
|
|
131
|
+
};
|
|
132
|
+
/** Self-stamped block payload (matches Python format_block + _stamp). */
|
|
133
|
+
formatBlock(opts: {
|
|
134
|
+
label: string;
|
|
135
|
+
reason: string;
|
|
136
|
+
artifactId: string;
|
|
137
|
+
commandPreview?: string;
|
|
138
|
+
suggestedFix: string;
|
|
139
|
+
}): {
|
|
140
|
+
continue: false;
|
|
141
|
+
decision: "block";
|
|
142
|
+
reason: string;
|
|
143
|
+
stopReason: string;
|
|
144
|
+
message: string;
|
|
145
|
+
_stamp: GateStamp;
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Write an entry to the unified audit ledger. Matches the schema in §6.2 of
|
|
149
|
+
* BUILD_PLAN_1_1_0_WAVE2_BRIEFS.md and tags it with event_type so a single
|
|
150
|
+
* file is queryable (Opus orchestrator decision on [VERIFY] item 5).
|
|
151
|
+
*/
|
|
152
|
+
writeAudit(entry: Record<string, unknown>): void;
|
|
153
|
+
/** Expose the audit log path for tests and tooling. */
|
|
154
|
+
getAuditLogPath(): string;
|
|
155
|
+
/**
|
|
156
|
+
* Run the full gate logic for a given artifact_id + optional command preview.
|
|
157
|
+
* Returns a GateResult with a final allow/block decision and a self-stamped
|
|
158
|
+
* payload. Audit entries are written to the unified ledger.
|
|
159
|
+
*
|
|
160
|
+
* Behaviour order (matching Python main()):
|
|
161
|
+
* 0. Kill-switch (DISABLE_SUNAIVA_GATE=1) → allow, log bypass.
|
|
162
|
+
* 1. Dry-run (SUNAIVA_GATE_DRY_RUN=1) → probe both tiers, log dry-run, allow.
|
|
163
|
+
* 2. PAID tier → if allow, return allow tier=paid.
|
|
164
|
+
* If block, hard-block (never fall through).
|
|
165
|
+
* If absent/skip_key, continue to free tier.
|
|
166
|
+
* 3. FREE tier → token present and fresh → allow tier=free.
|
|
167
|
+
* Token missing → block with dual-path hint.
|
|
168
|
+
*
|
|
169
|
+
* FAIL-OPEN: any uncaught exception → allow with audit event_type=ship_confidence_error.
|
|
170
|
+
*/
|
|
171
|
+
check(artifactId: string, options?: {
|
|
172
|
+
commandPreview?: string;
|
|
173
|
+
label?: string;
|
|
174
|
+
}): Promise<GateResult>;
|
|
175
|
+
}
|
|
176
|
+
export declare const __test: {
|
|
177
|
+
detectGenesisRoot: typeof detectGenesisRoot;
|
|
178
|
+
parseSignedAt: typeof parseSignedAt;
|
|
179
|
+
DEFAULT_AUDIT_PATH: string;
|
|
180
|
+
DEFAULT_AUDIT_DIR: string;
|
|
181
|
+
};
|
|
182
|
+
export declare function defaultAuditLogPath(): string;
|
|
183
|
+
export {};
|
|
184
|
+
//# sourceMappingURL=ship-confidence-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ship-confidence-gate.d.ts","sourceRoot":"","sources":["../../src/engine/ship-confidence-gate.ts"],"names":[],"mappings":"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"}
|