@skillsmith/core 0.10.0 → 0.11.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/CHANGELOG.md +4 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/analysis/tree-sitter/pythonIncremental.d.ts +23 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.d.ts.map +1 -1
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.js +137 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.js.map +1 -1
- package/dist/src/analysis/tree-sitter/pythonIncremental.js +83 -10
- package/dist/src/analysis/tree-sitter/pythonIncremental.js.map +1 -1
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.js +6 -1
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.js.map +1 -1
- package/dist/src/config/audit-notify-state.d.ts +46 -0
- package/dist/src/config/audit-notify-state.d.ts.map +1 -0
- package/dist/src/config/audit-notify-state.js +55 -0
- package/dist/src/config/audit-notify-state.js.map +1 -0
- package/dist/src/config/audit-notify-state.test.d.ts +9 -0
- package/dist/src/config/audit-notify-state.test.d.ts.map +1 -0
- package/dist/src/config/audit-notify-state.test.js +63 -0
- package/dist/src/config/audit-notify-state.test.js.map +1 -0
- package/dist/src/config/index.d.ts +10 -0
- package/dist/src/config/index.d.ts.map +1 -1
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/exports/services.d.ts +1 -1
- package/dist/src/exports/services.d.ts.map +1 -1
- package/dist/src/exports/services.js +3 -1
- package/dist/src/exports/services.js.map +1 -1
- package/dist/src/index.d.ts +4 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/logging/context.d.ts +58 -0
- package/dist/src/logging/context.d.ts.map +1 -0
- package/dist/src/logging/context.js +62 -0
- package/dist/src/logging/context.js.map +1 -0
- package/dist/src/logging/context.test.d.ts +14 -0
- package/dist/src/logging/context.test.d.ts.map +1 -0
- package/dist/src/logging/context.test.js +88 -0
- package/dist/src/logging/context.test.js.map +1 -0
- package/dist/src/logging/index.d.ts +13 -0
- package/dist/src/logging/index.d.ts.map +1 -0
- package/dist/src/logging/index.js +12 -0
- package/dist/src/logging/index.js.map +1 -0
- package/dist/src/logging/logger.d.ts +71 -0
- package/dist/src/logging/logger.d.ts.map +1 -0
- package/dist/src/logging/logger.js +264 -0
- package/dist/src/logging/logger.js.map +1 -0
- package/dist/src/logging/logger.test.d.ts +9 -0
- package/dist/src/logging/logger.test.d.ts.map +1 -0
- package/dist/src/logging/logger.test.js +320 -0
- package/dist/src/logging/logger.test.js.map +1 -0
- package/dist/src/logging/redact.d.ts +35 -0
- package/dist/src/logging/redact.d.ts.map +1 -0
- package/dist/src/logging/redact.js +152 -0
- package/dist/src/logging/redact.js.map +1 -0
- package/dist/src/logging/redact.test.d.ts +8 -0
- package/dist/src/logging/redact.test.d.ts.map +1 -0
- package/dist/src/logging/redact.test.js +330 -0
- package/dist/src/logging/redact.test.js.map +1 -0
- package/dist/src/logging/rotation.d.ts +75 -0
- package/dist/src/logging/rotation.d.ts.map +1 -0
- package/dist/src/logging/rotation.js +284 -0
- package/dist/src/logging/rotation.js.map +1 -0
- package/dist/src/logging/rotation.test.d.ts +11 -0
- package/dist/src/logging/rotation.test.d.ts.map +1 -0
- package/dist/src/logging/rotation.test.js +132 -0
- package/dist/src/logging/rotation.test.js.map +1 -0
- package/dist/src/logging/types.d.ts +69 -0
- package/dist/src/logging/types.d.ts.map +1 -0
- package/dist/src/logging/types.js +9 -0
- package/dist/src/logging/types.js.map +1 -0
- package/dist/src/security/index.d.ts +2 -0
- package/dist/src/security/index.d.ts.map +1 -1
- package/dist/src/security/index.js +4 -0
- package/dist/src/security/index.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.hostile-update.d.ts +46 -0
- package/dist/src/security/scanner/SecurityScanner.hostile-update.d.ts.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.hostile-update.js +209 -0
- package/dist/src/security/scanner/SecurityScanner.hostile-update.js.map +1 -0
- package/dist/src/security/scanner/index.d.ts +2 -1
- package/dist/src/security/scanner/index.d.ts.map +1 -1
- package/dist/src/security/scanner/index.js +2 -0
- package/dist/src/security/scanner/index.js.map +1 -1
- package/dist/src/security/scanner/types.d.ts +21 -0
- package/dist/src/security/scanner/types.d.ts.map +1 -1
- package/dist/src/services/skill-installation.io.d.ts.map +1 -1
- package/dist/src/services/skill-installation.io.js +19 -2
- package/dist/src/services/skill-installation.io.js.map +1 -1
- package/dist/src/sync/access-token.d.ts +27 -0
- package/dist/src/sync/access-token.d.ts.map +1 -0
- package/dist/src/sync/access-token.js +40 -0
- package/dist/src/sync/access-token.js.map +1 -0
- package/dist/src/sync/audit-notify-client.d.ts +83 -0
- package/dist/src/sync/audit-notify-client.d.ts.map +1 -0
- package/dist/src/sync/audit-notify-client.js +126 -0
- package/dist/src/sync/audit-notify-client.js.map +1 -0
- package/dist/src/sync/audit-notify-client.test.d.ts +14 -0
- package/dist/src/sync/audit-notify-client.test.d.ts.map +1 -0
- package/dist/src/sync/audit-notify-client.test.js +133 -0
- package/dist/src/sync/audit-notify-client.test.js.map +1 -0
- package/dist/src/sync/index.d.ts +1 -0
- package/dist/src/sync/index.d.ts.map +1 -1
- package/dist/src/sync/index.js +2 -0
- package/dist/src/sync/index.js.map +1 -1
- package/dist/src/sync/inventory-client.d.ts.map +1 -1
- package/dist/src/sync/inventory-client.js +5 -16
- package/dist/src/sync/inventory-client.js.map +1 -1
- package/dist/src/telemetry/posthog.d.ts +15 -0
- package/dist/src/telemetry/posthog.d.ts.map +1 -1
- package/dist/src/telemetry/posthog.js +6 -1
- package/dist/src/telemetry/posthog.js.map +1 -1
- package/dist/src/telemetry/wrap.bench.js +5 -0
- package/dist/src/telemetry/wrap.bench.js.map +1 -1
- package/dist/src/telemetry/wrap.correlation.test.d.ts +20 -0
- package/dist/src/telemetry/wrap.correlation.test.d.ts.map +1 -0
- package/dist/src/telemetry/wrap.correlation.test.js +174 -0
- package/dist/src/telemetry/wrap.correlation.test.js.map +1 -0
- package/dist/src/telemetry/wrap.d.ts.map +1 -1
- package/dist/src/telemetry/wrap.gate.test.js +5 -0
- package/dist/src/telemetry/wrap.gate.test.js.map +1 -1
- package/dist/src/telemetry/wrap.js +111 -52
- package/dist/src/telemetry/wrap.js.map +1 -1
- package/dist/src/telemetry/wrap.marker.test.js +5 -0
- package/dist/src/telemetry/wrap.marker.test.js.map +1 -1
- package/dist/src/telemetry/wrap.test.js +10 -0
- package/dist/src/telemetry/wrap.test.js.map +1 -1
- package/dist/src/types.d.ts +6 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/tests/security/scanner-regression-guard.test.js +179 -1
- package/dist/tests/security/scanner-regression-guard.test.js.map +1 -1
- package/package.json +7 -2
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hostile-Update Detection — R0 Wave 2A (SMI-5535)
|
|
3
|
+
* @module @skillsmith/core/security/scanner/SecurityScanner.hostile-update
|
|
4
|
+
*
|
|
5
|
+
* A PURE comparator (no I/O, no clock, no mutation of its inputs) that detects a
|
|
6
|
+
* benign→malicious "rug-pull" between two SecurityScanner scans of the SAME
|
|
7
|
+
* skill. The scanner already blocks a skill that is malicious on first sight;
|
|
8
|
+
* this detector catches the harder case where an initially-clean skill is
|
|
9
|
+
* silently updated to exfiltrate credentials or execute remote payloads.
|
|
10
|
+
*
|
|
11
|
+
* The verdict is delta-based: it never re-scores content, it only reasons about
|
|
12
|
+
* what changed between `previous` and `current`. "hostile" is reserved for the
|
|
13
|
+
* benign→malicious transition — a skill that was ALREADY flagged and merely got
|
|
14
|
+
* worse is a worsening (`suspicious`), not a fresh rug-pull.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Default risk-score threshold. Mirrors SecurityScanner's `riskThreshold`
|
|
18
|
+
* default (SMI-5359) so the "crossed the failure bar" signal here lines up with
|
|
19
|
+
* the `passed` computation in `SecurityScanner.scan`.
|
|
20
|
+
*/
|
|
21
|
+
export const DEFAULT_RISK_THRESHOLD = 40;
|
|
22
|
+
/**
|
|
23
|
+
* Minimum positive risk jump (short of crossing the threshold) that still counts
|
|
24
|
+
* as a "material" worsening worth a `suspicious` verdict.
|
|
25
|
+
*/
|
|
26
|
+
const MATERIAL_RISK_DELTA = 10;
|
|
27
|
+
/**
|
|
28
|
+
* Co-occurrence finding types that, paired with a NEW `code_execution` finding,
|
|
29
|
+
* indicate a supply-chain execution rug-pull. Mirrors the intuition of
|
|
30
|
+
* `escalateCodeExecution`'s CODE_EXECUTION_CO_OCCURRENCE set (SecurityScanner.exec.ts),
|
|
31
|
+
* minus `obfuscated_directive` — a live concealed directive is already critical
|
|
32
|
+
* and self-quarantines on its own, so it needs no code_execution pairing here.
|
|
33
|
+
*/
|
|
34
|
+
const SUPPLY_CHAIN_CO_SIGNALS = new Set([
|
|
35
|
+
'data_exfiltration',
|
|
36
|
+
'privilege_escalation',
|
|
37
|
+
'sensitive_path',
|
|
38
|
+
]);
|
|
39
|
+
const SEVERITY_ORDER = {
|
|
40
|
+
low: 0,
|
|
41
|
+
medium: 1,
|
|
42
|
+
high: 2,
|
|
43
|
+
critical: 3,
|
|
44
|
+
};
|
|
45
|
+
function isHighOrCritical(f) {
|
|
46
|
+
return f.severity === 'high' || f.severity === 'critical';
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Normalize free-text so cosmetic churn (whitespace runs, casing) does not make
|
|
50
|
+
* an otherwise-identical finding look "new". lineNumber is deliberately excluded
|
|
51
|
+
* from the key (see `findingKey`) because inserting benign prose above a finding
|
|
52
|
+
* shifts its line without changing the finding itself.
|
|
53
|
+
*/
|
|
54
|
+
function normalizeText(text) {
|
|
55
|
+
if (!text)
|
|
56
|
+
return '';
|
|
57
|
+
return text.toLowerCase().replace(/\s+/g, ' ').trim();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Stable identity for a finding: type + severity + normalized message + location.
|
|
61
|
+
* Severity is part of the key on purpose — an escalation (e.g. code_execution
|
|
62
|
+
* medium→critical, which also rewrites the message) is a genuinely NEW hostile
|
|
63
|
+
* signal, not the "same" finding. A finding that got SAFER (higher→lower
|
|
64
|
+
* severity, or disappeared entirely) therefore never shows up in `newFindings`.
|
|
65
|
+
*/
|
|
66
|
+
function findingKey(f) {
|
|
67
|
+
return [f.type, f.severity, normalizeText(f.message), normalizeText(f.location)].join('\u0000');
|
|
68
|
+
}
|
|
69
|
+
/** Findings present in `current` whose key is absent from `previous`. */
|
|
70
|
+
function diffNewFindings(previous, current) {
|
|
71
|
+
const previousKeys = new Set(previous.findings.map(findingKey));
|
|
72
|
+
return current.findings.filter((f) => !previousKeys.has(findingKey(f)));
|
|
73
|
+
}
|
|
74
|
+
function highestSeverity(findings) {
|
|
75
|
+
return findings.reduce((worst, f) => SEVERITY_ORDER[f.severity] > SEVERITY_ORDER[worst.severity] ? f : worst);
|
|
76
|
+
}
|
|
77
|
+
/** Compact, one-line, single-number formatting for a (possibly fractional) score. */
|
|
78
|
+
function fmt(n) {
|
|
79
|
+
const rounded = Math.round(n * 10) / 10;
|
|
80
|
+
return Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);
|
|
81
|
+
}
|
|
82
|
+
function signed(n) {
|
|
83
|
+
return n > 0 ? `+${fmt(n)}` : fmt(n);
|
|
84
|
+
}
|
|
85
|
+
function describeFinding(f) {
|
|
86
|
+
const msg = f.message.length > 80 ? `${f.message.slice(0, 77)}...` : f.message;
|
|
87
|
+
return `${f.type} (${f.severity}): ${msg}`;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* A NEW, non-documentation `code_execution` finding paired with a NEW,
|
|
91
|
+
* non-documentation high/critical exfiltration/privilege/credential-path
|
|
92
|
+
* finding = supply-chain execution. Requiring both to be new (and non-doc)
|
|
93
|
+
* keeps a security-research skill that merely documents these techniques from
|
|
94
|
+
* tripping the strongest signal.
|
|
95
|
+
*/
|
|
96
|
+
function detectSupplyChain(newFindings) {
|
|
97
|
+
const codeExec = newFindings.find((f) => f.type === 'code_execution' && f.inDocumentationContext !== true);
|
|
98
|
+
if (!codeExec)
|
|
99
|
+
return null;
|
|
100
|
+
const coSignal = newFindings.find((f) => f !== codeExec &&
|
|
101
|
+
SUPPLY_CHAIN_CO_SIGNALS.has(f.type) &&
|
|
102
|
+
f.inDocumentationContext !== true &&
|
|
103
|
+
isHighOrCritical(f));
|
|
104
|
+
if (!coSignal)
|
|
105
|
+
return null;
|
|
106
|
+
return { codeExec, coSignal };
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Compare two scans of the same skill and classify the transition.
|
|
110
|
+
*
|
|
111
|
+
* Ordering is hostile → suspicious → benign so the strongest signal wins.
|
|
112
|
+
*
|
|
113
|
+
* @param previous Scan of the prior (trusted) version.
|
|
114
|
+
* @param current Scan of the incoming version.
|
|
115
|
+
* @param threshold Risk-score failure bar; defaults to {@link DEFAULT_RISK_THRESHOLD}.
|
|
116
|
+
*
|
|
117
|
+
* CALLER CONTRACT (LOW-2): this value MUST equal the `riskThreshold` the
|
|
118
|
+
* `SecurityScanner` instance used to produce BOTH `previous` and `current`
|
|
119
|
+
* (i.e. the same `ScannerOptions.riskThreshold` — or the scanner's own
|
|
120
|
+
* default of 40, which is why this parameter also defaults to
|
|
121
|
+
* {@link DEFAULT_RISK_THRESHOLD}). `previouslyBenign` below is derived from
|
|
122
|
+
* `previous.passed`, which was computed by the scanner against ITS OWN
|
|
123
|
+
* `riskThreshold` at scan time — if the caller passes a `threshold` here
|
|
124
|
+
* that differs from that value, `crossedThreshold`'s "did the score cross
|
|
125
|
+
* the bar" signal and `previouslyBenign`'s "was it passing" signal are
|
|
126
|
+
* reasoning about two different bars, and the hostile/suspicious/benign
|
|
127
|
+
* verdict can disagree with what `previous.passed` / `current.passed`
|
|
128
|
+
* would say under a freshly-run scan.
|
|
129
|
+
*/
|
|
130
|
+
export function compareScanReports(previous, current, threshold = DEFAULT_RISK_THRESHOLD) {
|
|
131
|
+
const newFindings = diffNewFindings(previous, current);
|
|
132
|
+
const riskDelta = current.riskScore - previous.riskScore;
|
|
133
|
+
const previouslyBenign = previous.passed === true;
|
|
134
|
+
const crossedThreshold = previous.riskScore < threshold && current.riskScore >= threshold;
|
|
135
|
+
const newHighCritical = newFindings.filter(isHighOrCritical);
|
|
136
|
+
// Mirror escalateCodeExecution's non-doc gate: a new high/critical finding that
|
|
137
|
+
// lives ONLY in a documentation context (a fenced example) is weak rug-pull
|
|
138
|
+
// evidence, so it does not on its own trip `hostile` — it falls to `suspicious`.
|
|
139
|
+
const newHighCriticalNonDoc = newHighCritical.filter((f) => f.inDocumentationContext !== true);
|
|
140
|
+
const newMedium = newFindings.filter((f) => f.severity === 'medium');
|
|
141
|
+
const materialDelta = riskDelta >= MATERIAL_RISK_DELTA;
|
|
142
|
+
// A new medium finding only signals a worsening when the overall risk actually
|
|
143
|
+
// rose. This suppresses the case where an old high finding was DOWNGRADED to
|
|
144
|
+
// medium (its medium variant is technically "new" under the severity-keyed diff,
|
|
145
|
+
// but the net change made the skill safer — riskDelta <= 0).
|
|
146
|
+
const mediumWorsening = newMedium.length > 0 && riskDelta > 0;
|
|
147
|
+
const scoreClause = `risk ${fmt(previous.riskScore)}->${fmt(current.riskScore)}`;
|
|
148
|
+
// ---- hostile: a benign→malicious flip ----------------------------------
|
|
149
|
+
if (previouslyBenign && (newHighCriticalNonDoc.length > 0 || crossedThreshold)) {
|
|
150
|
+
const supplyChain = detectSupplyChain(newFindings);
|
|
151
|
+
let reason;
|
|
152
|
+
if (supplyChain) {
|
|
153
|
+
reason =
|
|
154
|
+
`Rug-pull: previously-benign skill now ships a remote-execution command ` +
|
|
155
|
+
`[${describeFinding(supplyChain.codeExec)}] co-occurring with a fresh ` +
|
|
156
|
+
`${supplyChain.coSignal.type} signal [${describeFinding(supplyChain.coSignal)}] - ` +
|
|
157
|
+
`classic supply-chain execution (${scoreClause}).`;
|
|
158
|
+
}
|
|
159
|
+
else if (newHighCriticalNonDoc.length > 0) {
|
|
160
|
+
const worst = highestSeverity(newHighCriticalNonDoc);
|
|
161
|
+
reason =
|
|
162
|
+
`Rug-pull: previously-benign skill introduced ${newHighCriticalNonDoc.length} new ` +
|
|
163
|
+
`high/critical finding(s), e.g. ${describeFinding(worst)} (${scoreClause}).`;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
reason =
|
|
167
|
+
`Rug-pull: previously-benign skill's risk score crossed the failure bar ` +
|
|
168
|
+
`(${fmt(previous.riskScore)} -> ${fmt(current.riskScore)} >= ${threshold}).`;
|
|
169
|
+
}
|
|
170
|
+
return { verdict: 'hostile', newFindings, riskDelta, reason };
|
|
171
|
+
}
|
|
172
|
+
// ---- suspicious: a worsening short of a benign→malicious flip -----------
|
|
173
|
+
if (newHighCritical.length > 0 || mediumWorsening || materialDelta) {
|
|
174
|
+
let reason;
|
|
175
|
+
if (newHighCritical.length > 0) {
|
|
176
|
+
const worst = highestSeverity(newHighCritical);
|
|
177
|
+
const base = previouslyBenign
|
|
178
|
+
? `Update added ${newHighCritical.length} new high/critical finding(s) confined to a documentation context`
|
|
179
|
+
: `Already-flagged skill added ${newHighCritical.length} new high/critical finding(s) - a worsening, not a fresh benign->malicious transition`;
|
|
180
|
+
reason = `${base}, e.g. ${describeFinding(worst)} (${scoreClause}).`;
|
|
181
|
+
}
|
|
182
|
+
else if (newMedium.length > 0) {
|
|
183
|
+
reason =
|
|
184
|
+
`Update introduced ${newMedium.length} new medium finding(s) without reaching the ` +
|
|
185
|
+
`hostile bar (${scoreClause}).`;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
reason =
|
|
189
|
+
`Risk score rose materially (${scoreClause}, delta ${signed(riskDelta)}) ` +
|
|
190
|
+
`without any new high/critical findings.`;
|
|
191
|
+
}
|
|
192
|
+
return { verdict: 'suspicious', newFindings, riskDelta, reason };
|
|
193
|
+
}
|
|
194
|
+
// ---- benign: unchanged, reduced, or harmless churn ---------------------
|
|
195
|
+
let reason;
|
|
196
|
+
if (newFindings.length === 0) {
|
|
197
|
+
reason =
|
|
198
|
+
riskDelta < 0
|
|
199
|
+
? `No new findings; risk score fell (${scoreClause}) - a safer revision or benign churn.`
|
|
200
|
+
: `No new findings and no risk increase (${scoreClause}) - content unchanged or benign churn.`;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
reason =
|
|
204
|
+
`No new high/critical findings and no threshold crossing (${scoreClause}); ` +
|
|
205
|
+
`${newFindings.length} minor new finding(s) below the suspicious bar.`;
|
|
206
|
+
}
|
|
207
|
+
return { verdict: 'benign', newFindings, riskDelta, reason };
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=SecurityScanner.hostile-update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SecurityScanner.hostile-update.js","sourceRoot":"","sources":["../../../../src/security/scanner/SecurityScanner.hostile-update.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Re-exports for security scanning functionality.
|
|
5
5
|
*/
|
|
6
|
-
export type { SecurityFindingType, SecuritySeverity, SecurityFinding, RiskScoreBreakdown, ScanReport, ScannerOptions, } from './types.js';
|
|
6
|
+
export type { SecurityFindingType, SecuritySeverity, SecurityFinding, RiskScoreBreakdown, ScanReport, ScannerOptions, HostileUpdateVerdict, } from './types.js';
|
|
7
7
|
export { DEFAULT_ALLOWED_DOMAINS, SENSITIVE_PATH_PATTERNS, JAILBREAK_PATTERNS, SUSPICIOUS_PATTERNS, SOCIAL_ENGINEERING_PATTERNS, PROMPT_LEAKING_PATTERNS, DATA_EXFILTRATION_PATTERNS, PRIVILEGE_ESCALATION_PATTERNS, AI_DEFENCE_PATTERNS, SSRF_INSTRUCTION_PATTERNS, PII_PATTERNS, CODE_EXECUTION_PATTERNS, } from './patterns.js';
|
|
8
8
|
export { SEVERITY_WEIGHTS, CATEGORY_WEIGHTS } from './weights.js';
|
|
9
9
|
export { MAX_LINE_LENGTH_FOR_REGEX, safeRegexTest, safeRegexCheck } from './regex-utils.js';
|
|
10
10
|
export { SecurityScanner, default } from './SecurityScanner.js';
|
|
11
|
+
export { compareScanReports, DEFAULT_RISK_THRESHOLD } from './SecurityScanner.hostile-update.js';
|
|
11
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,UAAU,EACV,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,oBAAoB,GACrB,MAAM,YAAY,CAAA;AAGnB,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,0BAA0B,EAC1B,6BAA6B,EAC7B,mBAAmB,EACnB,yBAAyB,EACzB,YAAY,EACZ,uBAAuB,GACxB,MAAM,eAAe,CAAA;AAGtB,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAGjE,OAAO,EAAE,yBAAyB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAG3F,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAG/D,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAA"}
|
|
@@ -11,4 +11,6 @@ export { SEVERITY_WEIGHTS, CATEGORY_WEIGHTS } from './weights.js';
|
|
|
11
11
|
export { MAX_LINE_LENGTH_FOR_REGEX, safeRegexTest, safeRegexCheck } from './regex-utils.js';
|
|
12
12
|
// Main class
|
|
13
13
|
export { SecurityScanner, default } from './SecurityScanner.js';
|
|
14
|
+
// Hostile-update comparator (SMI-5535, R0 Wave 2A)
|
|
15
|
+
export { compareScanReports, DEFAULT_RISK_THRESHOLD } from './SecurityScanner.hostile-update.js';
|
|
14
16
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/security/scanner/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/security/scanner/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,mCAAmC;AACnC,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,0BAA0B,EAC1B,6BAA6B,EAC7B,mBAAmB,EACnB,yBAAyB,EACzB,YAAY,EACZ,uBAAuB,GACxB,MAAM,eAAe,CAAA;AAEtB,kCAAkC;AAClC,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAEjE,0CAA0C;AAC1C,OAAO,EAAE,yBAAyB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAE3F,aAAa;AACb,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAE/D,mDAAmD;AACnD,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAA"}
|
|
@@ -68,6 +68,27 @@ export interface ScanReport {
|
|
|
68
68
|
/** Breakdown of risk score by category */
|
|
69
69
|
riskBreakdown: RiskScoreBreakdown;
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Verdict returned by comparing two scans of the SAME skill to detect a
|
|
73
|
+
* benign→malicious "rug-pull" update (SMI-5535, R0 Wave 2A).
|
|
74
|
+
*
|
|
75
|
+
* - `hostile` — a previously-passing skill introduced new high/critical
|
|
76
|
+
* findings (or crossed the risk threshold): a genuine benign→malicious flip.
|
|
77
|
+
* - `suspicious` — the update added new medium findings or a material risk
|
|
78
|
+
* increase, but did not meet the hostile bar (also covers an already-flagged
|
|
79
|
+
* skill that got materially worse, which is a worsening — not a new rug-pull).
|
|
80
|
+
* - `benign` — no new high/critical findings and no threshold crossing;
|
|
81
|
+
* findings are unchanged, reduced, or only benign churn.
|
|
82
|
+
*/
|
|
83
|
+
export interface HostileUpdateVerdict {
|
|
84
|
+
verdict: 'benign' | 'suspicious' | 'hostile';
|
|
85
|
+
/** Findings present in the current scan but absent from the previous scan. */
|
|
86
|
+
newFindings: SecurityFinding[];
|
|
87
|
+
/** `current.riskScore - previous.riskScore` (positive = riskier). */
|
|
88
|
+
riskDelta: number;
|
|
89
|
+
/** One concrete human-readable sentence citing the deciding signal. */
|
|
90
|
+
reason: string;
|
|
91
|
+
}
|
|
71
92
|
/**
|
|
72
93
|
* Configuration options for the security scanner
|
|
73
94
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAC3B,KAAK,GACL,gBAAgB,GAChB,WAAW,GACX,oBAAoB,GACpB,oBAAoB,GACpB,gBAAgB,GAChB,mBAAmB,GACnB,sBAAsB,GACtB,YAAY,GACZ,MAAM,GACN,KAAK,GACL,gBAAgB,GAChB,sBAAsB,CAAA;AAE1B;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAA;AAErE;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAA;AAEzD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,mBAAmB,CAAA;IACzB,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iFAAiF;IACjF,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,qEAAqE;IACrE,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAC9B,0IAA0I;IAC1I,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,MAAM,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,MAAM,CAAA;IACrB,mBAAmB,EAAE,MAAM,CAAA;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,SAAS,EAAE,IAAI,CAAA;IACf,cAAc,EAAE,MAAM,CAAA;IACtB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAA;IACjB,0CAA0C;IAC1C,aAAa,EAAE,kBAAkB,CAAA;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAC3B,KAAK,GACL,gBAAgB,GAChB,WAAW,GACX,oBAAoB,GACpB,oBAAoB,GACpB,gBAAgB,GAChB,mBAAmB,GACnB,sBAAsB,GACtB,YAAY,GACZ,MAAM,GACN,KAAK,GACL,gBAAgB,GAChB,sBAAsB,CAAA;AAE1B;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAA;AAErE;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAA;AAEzD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,mBAAmB,CAAA;IACzB,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iFAAiF;IACjF,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,qEAAqE;IACrE,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAC9B,0IAA0I;IAC1I,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,MAAM,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,MAAM,CAAA;IACrB,mBAAmB,EAAE,MAAM,CAAA;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,SAAS,EAAE,IAAI,CAAA;IACf,cAAc,EAAE,MAAM,CAAA;IACtB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAA;IACjB,0CAA0C;IAC1C,aAAa,EAAE,kBAAkB,CAAA;CAClC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,QAAQ,GAAG,YAAY,GAAG,SAAS,CAAA;IAC5C,8EAA8E;IAC9E,WAAW,EAAE,eAAe,EAAE,CAAA;IAC9B,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAA;IACjB,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-installation.io.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.io.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAStE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQ1E;
|
|
1
|
+
{"version":3,"file":"skill-installation.io.d.ts","sourceRoot":"","sources":["../../../src/services/skill-installation.io.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAStE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQ1E;AAkBD,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAe,GACtB,OAAO,CAAC,MAAM,CAAC,CAyBjB;AAED,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,OAAO,CAAC,CAkBlB;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,EACzB,aAAa,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,EAC3D,eAAe,EAAE,MAAM,GAAG,SAAS,GAClC,OAAO,CAAC,kBAAkB,CAAC,CA0E7B;AAED,MAAM,WAAW,0BAA0B;IACzC,uEAAuE;IACvE,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB;;;OAGG;IACH,WAAW,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE,CAAC,CAAA;IACxD,4EAA4E;IAC5E,YAAY,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC3D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,cAAc,GAAG,IAAI,GACpC,OAAO,CAAC,0BAA0B,CAAC,CAgDrC"}
|
|
@@ -17,13 +17,30 @@ export function assertNotEncrypted(content, filePath) {
|
|
|
17
17
|
'" is git-crypt encrypted. The repository uses git-crypt and this file cannot be fetched from GitHub.');
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* SMI-5582: per-request timeout for raw.githubusercontent.com fetches.
|
|
22
|
+
*
|
|
23
|
+
* This is THE critical-path GitHub fetch for a registry install: it is reached
|
|
24
|
+
* from `SkillInstallationService.install()` (SKILL.md) and from
|
|
25
|
+
* `fetchAndScanOptionalFiles` (up to 7 optional files), each with a possible
|
|
26
|
+
* `main`→`master` fallback — i.e. up to ~16 sequential network calls per
|
|
27
|
+
* skill. Before SMI-5582 none of them were bounded, so a single hung GitHub
|
|
28
|
+
* socket could stall the caller indefinitely. Since Tier-1 auto-install now
|
|
29
|
+
* runs this chain at server startup, an unbounded fetch here is what would
|
|
30
|
+
* have hung MCP startup. 10s matches the "generous but finite" budget of the
|
|
31
|
+
* other startup network paths (`version-check.ts` uses 3s for a single npm
|
|
32
|
+
* ping; this is a heavier raw-content fetch, hence the larger bound).
|
|
33
|
+
*/
|
|
34
|
+
const GITHUB_FETCH_TIMEOUT_MS = 10_000;
|
|
20
35
|
export async function fetchFromGitHub(owner, repo, filePath, branch = 'main') {
|
|
21
36
|
const url = 'https://raw.githubusercontent.com/' + owner + '/' + repo + '/' + branch + '/' + filePath;
|
|
22
|
-
const response = await fetch(url);
|
|
37
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(GITHUB_FETCH_TIMEOUT_MS) });
|
|
23
38
|
if (!response.ok) {
|
|
24
39
|
if (branch === 'main') {
|
|
25
40
|
const masterUrl = 'https://raw.githubusercontent.com/' + owner + '/' + repo + '/master/' + filePath;
|
|
26
|
-
const masterResponse = await fetch(masterUrl
|
|
41
|
+
const masterResponse = await fetch(masterUrl, {
|
|
42
|
+
signal: AbortSignal.timeout(GITHUB_FETCH_TIMEOUT_MS),
|
|
43
|
+
});
|
|
27
44
|
if (!masterResponse.ok) {
|
|
28
45
|
throw new Error('Failed to fetch ' + filePath + ': ' + response.status);
|
|
29
46
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-installation.io.js","sourceRoot":"","sources":["../../../src/services/skill-installation.io.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,aAAa,CAAA;AACjC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAA;AACzE,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,kCAAkC,EAClC,gBAAgB,GACjB,MAAM,gCAAgC,CAAA;AAEvC,MAAM,UAAU,kBAAkB,CAAC,OAAe,EAAE,QAAgB;IAClE,IAAI,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CACb,QAAQ;YACN,QAAQ;YACR,sGAAsG,CACzG,CAAA;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAa,EACb,IAAY,EACZ,QAAgB,EAChB,SAAiB,MAAM;IAEvB,MAAM,GAAG,GACP,oCAAoC,GAAG,KAAK,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,MAAM,GAAG,GAAG,GAAG,QAAQ,CAAA;IAC3F,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"skill-installation.io.js","sourceRoot":"","sources":["../../../src/services/skill-installation.io.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared device-session access-token resolver (SMI-5541, extracted from
|
|
3
|
+
* SMI-5392 `inventory-client.ts`).
|
|
4
|
+
*
|
|
5
|
+
* Both the inventory upload/purge client and the audit-notify client need to
|
|
6
|
+
* resolve a fresh access token from the stored device-login credentials,
|
|
7
|
+
* transparently refreshing (and persisting) one that is within
|
|
8
|
+
* {@link TOKEN_REFRESH_SKEW_MS} of expiry. This is security-sensitive logic
|
|
9
|
+
* (token refresh + persistence), so it lives in exactly one place; each caller
|
|
10
|
+
* supplies its own namespaced "not authenticated" error via `makeAuthError` so
|
|
11
|
+
* the shared helper stays error-type-agnostic.
|
|
12
|
+
*
|
|
13
|
+
* @module @skillsmith/core/sync/access-token
|
|
14
|
+
*/
|
|
15
|
+
/** Refresh the access token this many ms before it actually expires. */
|
|
16
|
+
export declare const TOKEN_REFRESH_SKEW_MS = 60000;
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a fresh access token, refreshing (and persisting) if the stored one
|
|
19
|
+
* is within {@link TOKEN_REFRESH_SKEW_MS} of expiry.
|
|
20
|
+
*
|
|
21
|
+
* @param makeAuthError - Factory for the caller's namespaced auth error, thrown
|
|
22
|
+
* when there are no stored credentials or the refresh fails. Kept as a factory
|
|
23
|
+
* (not a value) so the stack originates at the throw site.
|
|
24
|
+
* @throws Whatever `makeAuthError()` returns, on no/expired-unrefreshable session.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveAccessToken(makeAuthError: () => Error): Promise<string>;
|
|
27
|
+
//# sourceMappingURL=access-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-token.d.ts","sourceRoot":"","sources":["../../../src/sync/access-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,wEAAwE;AACxE,eAAO,MAAM,qBAAqB,QAAS,CAAA;AAE3C;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,aAAa,EAAE,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAYpF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared device-session access-token resolver (SMI-5541, extracted from
|
|
3
|
+
* SMI-5392 `inventory-client.ts`).
|
|
4
|
+
*
|
|
5
|
+
* Both the inventory upload/purge client and the audit-notify client need to
|
|
6
|
+
* resolve a fresh access token from the stored device-login credentials,
|
|
7
|
+
* transparently refreshing (and persisting) one that is within
|
|
8
|
+
* {@link TOKEN_REFRESH_SKEW_MS} of expiry. This is security-sensitive logic
|
|
9
|
+
* (token refresh + persistence), so it lives in exactly one place; each caller
|
|
10
|
+
* supplies its own namespaced "not authenticated" error via `makeAuthError` so
|
|
11
|
+
* the shared helper stays error-type-agnostic.
|
|
12
|
+
*
|
|
13
|
+
* @module @skillsmith/core/sync/access-token
|
|
14
|
+
*/
|
|
15
|
+
import { loadCredentials, refreshAccessToken, storeCredentials, } from '../config/token-credentials.js';
|
|
16
|
+
/** Refresh the access token this many ms before it actually expires. */
|
|
17
|
+
export const TOKEN_REFRESH_SKEW_MS = 60_000;
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a fresh access token, refreshing (and persisting) if the stored one
|
|
20
|
+
* is within {@link TOKEN_REFRESH_SKEW_MS} of expiry.
|
|
21
|
+
*
|
|
22
|
+
* @param makeAuthError - Factory for the caller's namespaced auth error, thrown
|
|
23
|
+
* when there are no stored credentials or the refresh fails. Kept as a factory
|
|
24
|
+
* (not a value) so the stack originates at the throw site.
|
|
25
|
+
* @throws Whatever `makeAuthError()` returns, on no/expired-unrefreshable session.
|
|
26
|
+
*/
|
|
27
|
+
export async function resolveAccessToken(makeAuthError) {
|
|
28
|
+
const creds = await loadCredentials();
|
|
29
|
+
if (!creds)
|
|
30
|
+
throw makeAuthError();
|
|
31
|
+
if (creds.expiresAt <= Date.now() + TOKEN_REFRESH_SKEW_MS) {
|
|
32
|
+
const refreshed = await refreshAccessToken(creds.refreshToken);
|
|
33
|
+
if (!refreshed)
|
|
34
|
+
throw makeAuthError();
|
|
35
|
+
await storeCredentials(refreshed);
|
|
36
|
+
return refreshed.accessToken;
|
|
37
|
+
}
|
|
38
|
+
return creds.accessToken;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=access-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-token.js","sourceRoot":"","sources":["../../../src/sync/access-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,gCAAgC,CAAA;AAEvC,wEAAwE;AACxE,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAA;AAE3C;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,aAA0B;IACjE,MAAM,KAAK,GAAG,MAAM,eAAe,EAAE,CAAA;IACrC,IAAI,CAAC,KAAK;QAAE,MAAM,aAAa,EAAE,CAAA;IAEjC,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,qBAAqB,EAAE,CAAC;QAC1D,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;QAC9D,IAAI,CAAC,SAAS;YAAE,MAAM,aAAa,EAAE,CAAA;QACrC,MAAM,gBAAgB,CAAC,SAAS,CAAC,CAAA;QACjC,OAAO,SAAS,CAAC,WAAW,CAAA;IAC9B,CAAC;IAED,OAAO,KAAK,CAAC,WAAW,CAAA;AAC1B,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit-digest push client (SMI-5541 Wave 2C Stage 2, Option 1).
|
|
3
|
+
*
|
|
4
|
+
* The security audit runs CLIENT-side (the inventory data plane is
|
|
5
|
+
* metadata-only, ADR-124), so the digest's email channel is a thin push: this
|
|
6
|
+
* client authenticates with the stored device-login session and POSTs a
|
|
7
|
+
* COMPACT findings summary — never raw skill content — to the gateway-verified
|
|
8
|
+
* `audit-notify` edge function, which gates on consent + a verified email
|
|
9
|
+
* server-side and sends the email via Resend.
|
|
10
|
+
*
|
|
11
|
+
* The payload contract here is the client-side twin of the edge function's
|
|
12
|
+
* `parseDigest`: `{ scanned, hostile, malicious, suspicious, findings }`, each
|
|
13
|
+
* finding `{ identifier, kind, verdict, reason }`. Defined independently in
|
|
14
|
+
* `@skillsmith/core` (which must not depend on `@skillsmith/mcp-server`); the
|
|
15
|
+
* mapping from a `RunSecurityAuditResult` into this shape lives in
|
|
16
|
+
* `@skillsmith/mcp-server/audit` (`buildAuditDigestPayload`), which can see
|
|
17
|
+
* both types.
|
|
18
|
+
*
|
|
19
|
+
* @module @skillsmith/core/sync/audit-notify-client
|
|
20
|
+
*/
|
|
21
|
+
/** The three security verdicts the digest carries (mirrors the edge fn). */
|
|
22
|
+
export type AuditDigestVerdict = 'hostile' | 'malicious' | 'suspicious';
|
|
23
|
+
/** One compact finding in a pushed digest. Carries NO raw skill content. */
|
|
24
|
+
export interface AuditDigestPushFinding {
|
|
25
|
+
/** Skill/command/agent identifier (e.g. `author/name`). */
|
|
26
|
+
identifier: string;
|
|
27
|
+
/** Inventory kind (`skill` | `command` | `agent`). */
|
|
28
|
+
kind: string;
|
|
29
|
+
verdict: AuditDigestVerdict;
|
|
30
|
+
/** One human-readable sentence citing the deciding signal. */
|
|
31
|
+
reason: string;
|
|
32
|
+
}
|
|
33
|
+
/** The compact digest pushed to `audit-notify`. Mirrors the edge fn's parser. */
|
|
34
|
+
export interface AuditDigestPushPayload {
|
|
35
|
+
scanned: number;
|
|
36
|
+
hostile: number;
|
|
37
|
+
malicious: number;
|
|
38
|
+
suspicious: number;
|
|
39
|
+
findings: AuditDigestPushFinding[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Outcome of a push. `ok`/`sent`/`reason` mirror the edge function's 200-body
|
|
43
|
+
* shapes so callers can render a precise message without re-deriving:
|
|
44
|
+
* - `{ ok: true, sent: true }` — email dispatched.
|
|
45
|
+
* - `{ ok: true, sent: false, reason: 'nothing_to_report' }`
|
|
46
|
+
* - `{ ok: false, reason: 'not_consented' | 'email_not_verified' | 'no_email' }`
|
|
47
|
+
* - `{ ok: false, sent: false, reason: 'email_send_failed' }`
|
|
48
|
+
*/
|
|
49
|
+
export interface AuditDigestPushResult {
|
|
50
|
+
ok: boolean;
|
|
51
|
+
sent: boolean;
|
|
52
|
+
reason?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* No usable device session. The message hints at the recovery action so the
|
|
56
|
+
* CLI/MCP surface can relay it.
|
|
57
|
+
*/
|
|
58
|
+
export declare class AuditNotifyAuthError extends Error {
|
|
59
|
+
constructor(message?: string);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Catch-all for transport failures and unexpected server responses (HTTP 5xx,
|
|
63
|
+
* 400 `invalid_payload`, network errors, unparseable bodies).
|
|
64
|
+
*/
|
|
65
|
+
export declare class AuditNotifyError extends Error {
|
|
66
|
+
constructor(message: string);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Push a compact audit digest to the `audit-notify` edge function.
|
|
70
|
+
*
|
|
71
|
+
* A `200` response is returned verbatim as an {@link AuditDigestPushResult} —
|
|
72
|
+
* this INCLUDES the consent-off `{ ok: false, reason: 'not_consented' }` and
|
|
73
|
+
* the `{ ok: true, sent: false, reason: 'nothing_to_report' }` no-ops, which
|
|
74
|
+
* are successful round-trips, not errors.
|
|
75
|
+
*
|
|
76
|
+
* @param payload - The compact digest (never raw content).
|
|
77
|
+
* @returns The edge function's outcome.
|
|
78
|
+
* @throws {AuditNotifyAuthError} HTTP 401, or no/expired session.
|
|
79
|
+
* @throws {AuditNotifyError} HTTP 400/5xx, network failure, or unparseable 200 body.
|
|
80
|
+
* @see SMI-5541
|
|
81
|
+
*/
|
|
82
|
+
export declare function sendAuditDigest(payload: AuditDigestPushPayload): Promise<AuditDigestPushResult>;
|
|
83
|
+
//# sourceMappingURL=audit-notify-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-notify-client.d.ts","sourceRoot":"","sources":["../../../src/sync/audit-notify-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,4EAA4E;AAC5E,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,CAAA;AAEvE,4EAA4E;AAC5E,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAA;IAClB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,kBAAkB,CAAA;IAC3B,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAA;CACf;AAED,iFAAiF;AACjF,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,sBAAsB,EAAE,CAAA;CACnC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,OAAO,CAAA;IACX,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,SAA6D;CAIjF;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAkBD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CA2DhC"}
|