hackmyagent 0.11.14 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -3
- package/dist/.integrity-manifest.json +1 -0
- package/dist/cli.js +79 -5
- package/dist/cli.js.map +1 -1
- package/dist/nanomind-core/analyzers/capability-analyzer.d.ts +40 -0
- package/dist/nanomind-core/analyzers/capability-analyzer.d.ts.map +1 -0
- package/dist/nanomind-core/analyzers/capability-analyzer.js +310 -0
- package/dist/nanomind-core/analyzers/capability-analyzer.js.map +1 -0
- package/dist/nanomind-core/analyzers/code-analyzer.d.ts +21 -0
- package/dist/nanomind-core/analyzers/code-analyzer.d.ts.map +1 -0
- package/dist/nanomind-core/analyzers/code-analyzer.js +350 -0
- package/dist/nanomind-core/analyzers/code-analyzer.js.map +1 -0
- package/dist/nanomind-core/analyzers/credential-analyzer.d.ts +20 -0
- package/dist/nanomind-core/analyzers/credential-analyzer.d.ts.map +1 -0
- package/dist/nanomind-core/analyzers/credential-analyzer.js +317 -0
- package/dist/nanomind-core/analyzers/credential-analyzer.js.map +1 -0
- package/dist/nanomind-core/analyzers/governance-analyzer.d.ts +22 -0
- package/dist/nanomind-core/analyzers/governance-analyzer.d.ts.map +1 -0
- package/dist/nanomind-core/analyzers/governance-analyzer.js +393 -0
- package/dist/nanomind-core/analyzers/governance-analyzer.js.map +1 -0
- package/dist/nanomind-core/analyzers/prompt-analyzer.d.ts +22 -0
- package/dist/nanomind-core/analyzers/prompt-analyzer.d.ts.map +1 -0
- package/dist/nanomind-core/analyzers/prompt-analyzer.js +486 -0
- package/dist/nanomind-core/analyzers/prompt-analyzer.js.map +1 -0
- package/dist/nanomind-core/analyzers/scope-analyzer.d.ts +20 -0
- package/dist/nanomind-core/analyzers/scope-analyzer.d.ts.map +1 -0
- package/dist/nanomind-core/analyzers/scope-analyzer.js +326 -0
- package/dist/nanomind-core/analyzers/scope-analyzer.js.map +1 -0
- package/dist/nanomind-core/compiler/semantic-compiler.d.ts +41 -0
- package/dist/nanomind-core/compiler/semantic-compiler.d.ts.map +1 -0
- package/dist/nanomind-core/compiler/semantic-compiler.js +490 -0
- package/dist/nanomind-core/compiler/semantic-compiler.js.map +1 -0
- package/dist/nanomind-core/index.d.ts +30 -0
- package/dist/nanomind-core/index.d.ts.map +1 -0
- package/dist/nanomind-core/index.js +45 -0
- package/dist/nanomind-core/index.js.map +1 -0
- package/dist/nanomind-core/ingestion/artifact-parser.d.ts +48 -0
- package/dist/nanomind-core/ingestion/artifact-parser.d.ts.map +1 -0
- package/dist/nanomind-core/ingestion/artifact-parser.js +203 -0
- package/dist/nanomind-core/ingestion/artifact-parser.js.map +1 -0
- package/dist/nanomind-core/ingestion/input-sanitizer.d.ts +49 -0
- package/dist/nanomind-core/ingestion/input-sanitizer.d.ts.map +1 -0
- package/dist/nanomind-core/ingestion/input-sanitizer.js +80 -0
- package/dist/nanomind-core/ingestion/input-sanitizer.js.map +1 -0
- package/dist/nanomind-core/scanner-bridge.d.ts +49 -0
- package/dist/nanomind-core/scanner-bridge.d.ts.map +1 -0
- package/dist/nanomind-core/scanner-bridge.js +317 -0
- package/dist/nanomind-core/scanner-bridge.js.map +1 -0
- package/dist/nanomind-core/security/defense-in-depth.d.ts +99 -0
- package/dist/nanomind-core/security/defense-in-depth.d.ts.map +1 -0
- package/dist/nanomind-core/security/defense-in-depth.js +206 -0
- package/dist/nanomind-core/security/defense-in-depth.js.map +1 -0
- package/dist/nanomind-core/security/integrity-verifier.d.ts +132 -0
- package/dist/nanomind-core/security/integrity-verifier.d.ts.map +1 -0
- package/dist/nanomind-core/security/integrity-verifier.js +437 -0
- package/dist/nanomind-core/security/integrity-verifier.js.map +1 -0
- package/dist/nanomind-core/types.d.ts +125 -0
- package/dist/nanomind-core/types.d.ts.map +1 -0
- package/dist/nanomind-core/types.js +22 -0
- package/dist/nanomind-core/types.js.map +1 -0
- package/dist/semantic/index.d.ts +2 -0
- package/dist/semantic/index.d.ts.map +1 -1
- package/dist/semantic/index.js +6 -2
- package/dist/semantic/index.js.map +1 -1
- package/dist/semantic/nanomind-enhancer.d.ts +50 -0
- package/dist/semantic/nanomind-enhancer.d.ts.map +1 -0
- package/dist/semantic/nanomind-enhancer.js +203 -0
- package/dist/semantic/nanomind-enhancer.js.map +1 -0
- package/dist/skills/builder.d.ts +55 -0
- package/dist/skills/builder.d.ts.map +1 -0
- package/dist/skills/builder.js +282 -0
- package/dist/skills/builder.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* NanoMind Scanner Bridge
|
|
4
|
+
*
|
|
5
|
+
* Integrates AST-based analyzers into the existing HMA scan flow.
|
|
6
|
+
* Defense-in-depth: both static and AST checks run. NanoMind can
|
|
7
|
+
* UPGRADE findings (add new, increase severity) but NEVER suppress
|
|
8
|
+
* static findings.
|
|
9
|
+
*
|
|
10
|
+
* Flow:
|
|
11
|
+
* 1. verifyAll() integrity check
|
|
12
|
+
* 2. Discover security-relevant files in target directory
|
|
13
|
+
* 3. Compile each file into a SecurityAST via SemanticCompiler
|
|
14
|
+
* 4. Run ALL AST analyzers (capability, credential, governance, scope)
|
|
15
|
+
* 5. Merge AST findings with static findings using defense-in-depth rules
|
|
16
|
+
* 6. Return merged findings + integrity status
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.runNanoMindScan = runNanoMindScan;
|
|
20
|
+
exports.mergeFindings = mergeFindings;
|
|
21
|
+
const promises_1 = require("node:fs/promises");
|
|
22
|
+
const node_path_1 = require("node:path");
|
|
23
|
+
const semantic_compiler_js_1 = require("./compiler/semantic-compiler.js");
|
|
24
|
+
const capability_analyzer_js_1 = require("./analyzers/capability-analyzer.js");
|
|
25
|
+
const credential_analyzer_js_1 = require("./analyzers/credential-analyzer.js");
|
|
26
|
+
const governance_analyzer_js_1 = require("./analyzers/governance-analyzer.js");
|
|
27
|
+
const scope_analyzer_js_1 = require("./analyzers/scope-analyzer.js");
|
|
28
|
+
const defense_in_depth_js_1 = require("./security/defense-in-depth.js");
|
|
29
|
+
const integrity_verifier_js_1 = require("./security/integrity-verifier.js");
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Constants
|
|
32
|
+
// ============================================================================
|
|
33
|
+
/**
|
|
34
|
+
* File extensions and names that are security-relevant and should be compiled
|
|
35
|
+
* into SecurityASTs. Kept intentionally broad -- the compiler's artifact
|
|
36
|
+
* classifier will set type to 'unknown' for non-matching content, and
|
|
37
|
+
* analyzers simply produce no findings for unknowns.
|
|
38
|
+
*/
|
|
39
|
+
const SECURITY_RELEVANT_EXTENSIONS = new Set([
|
|
40
|
+
'.md', '.json', '.yaml', '.yml', '.toml',
|
|
41
|
+
'.ts', '.js', '.py', '.go', '.rs',
|
|
42
|
+
'.env', '.cfg', '.ini', '.conf',
|
|
43
|
+
]);
|
|
44
|
+
const SECURITY_RELEVANT_NAMES = new Set([
|
|
45
|
+
'.env', '.cursorrules', '.clinerules', '.windsurfrules',
|
|
46
|
+
'mcp.json', 'agent.json', 'SOUL.md', 'SKILL.md', 'CLAUDE.md',
|
|
47
|
+
]);
|
|
48
|
+
/** Maximum file size to compile (1 MB). Larger files are skipped. */
|
|
49
|
+
const MAX_FILE_SIZE = 1048576;
|
|
50
|
+
/** Maximum number of files to compile per scan to bound runtime. */
|
|
51
|
+
const MAX_FILES_PER_SCAN = 200;
|
|
52
|
+
/**
|
|
53
|
+
* Run the NanoMind AST-based scan and merge results with existing static
|
|
54
|
+
* findings. This is the main entry point called from the scanner flow.
|
|
55
|
+
*
|
|
56
|
+
* Defense-in-depth rules:
|
|
57
|
+
* - AST findings ADD to the list (never remove static findings)
|
|
58
|
+
* - If AST and static both flag the same issue, use the higher severity
|
|
59
|
+
* - If AST says benign but static flags it, static wins (suppression blocked)
|
|
60
|
+
*/
|
|
61
|
+
async function runNanoMindScan(targetDir, existingFindings) {
|
|
62
|
+
// Step 1: Integrity check before anything else
|
|
63
|
+
const integrity = (0, integrity_verifier_js_1.verifyAll)();
|
|
64
|
+
// If quarantined, return existing findings untouched with status
|
|
65
|
+
if (integrity.status === 'QUARANTINE') {
|
|
66
|
+
return {
|
|
67
|
+
mergedFindings: [...existingFindings],
|
|
68
|
+
astFindings: [],
|
|
69
|
+
integrityStatus: 'QUARANTINE',
|
|
70
|
+
compiledArtifacts: 0,
|
|
71
|
+
nanomindAvailable: false,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// Step 2: Initialize compiler (heuristic-only if degraded)
|
|
75
|
+
const useNanoMind = integrity.status === 'CLEAN';
|
|
76
|
+
const compiler = new semantic_compiler_js_1.SemanticCompiler({ useNanoMind });
|
|
77
|
+
// Step 3: Discover security-relevant files
|
|
78
|
+
const files = await discoverFiles(targetDir);
|
|
79
|
+
// Step 4: Compile each file and run analyzers
|
|
80
|
+
const allASTFindings = [];
|
|
81
|
+
let compiledCount = 0;
|
|
82
|
+
let nanomindUsedAtLeastOnce = false;
|
|
83
|
+
for (const filePath of files) {
|
|
84
|
+
try {
|
|
85
|
+
const content = await (0, promises_1.readFile)(filePath, 'utf-8');
|
|
86
|
+
const relativePath = (0, node_path_1.relative)(targetDir, filePath);
|
|
87
|
+
const result = await compiler.compile(content, relativePath);
|
|
88
|
+
compiledCount++;
|
|
89
|
+
if (result.nanomindUsed) {
|
|
90
|
+
nanomindUsedAtLeastOnce = true;
|
|
91
|
+
}
|
|
92
|
+
// Run ALL four analyzers against this AST
|
|
93
|
+
const verifier = (ast) => compiler.verifyAST(ast);
|
|
94
|
+
const findings = runAllAnalyzers(result.ast, verifier);
|
|
95
|
+
allASTFindings.push(...findings);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Skip files that fail to read or compile -- do not block the scan
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Step 5: Merge using defense-in-depth rules
|
|
103
|
+
const mergedFindings = mergeFindings(existingFindings, allASTFindings);
|
|
104
|
+
return {
|
|
105
|
+
mergedFindings,
|
|
106
|
+
astFindings: allASTFindings,
|
|
107
|
+
integrityStatus: integrity.status,
|
|
108
|
+
compiledArtifacts: compiledCount,
|
|
109
|
+
nanomindAvailable: nanomindUsedAtLeastOnce || useNanoMind,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// ============================================================================
|
|
113
|
+
// File Discovery
|
|
114
|
+
// ============================================================================
|
|
115
|
+
/**
|
|
116
|
+
* Recursively discover security-relevant files in the target directory.
|
|
117
|
+
* Skips node_modules, .git, dist, and other non-security directories.
|
|
118
|
+
*/
|
|
119
|
+
async function discoverFiles(dir) {
|
|
120
|
+
const results = [];
|
|
121
|
+
await walkDir(dir, results, 0);
|
|
122
|
+
return results.slice(0, MAX_FILES_PER_SCAN);
|
|
123
|
+
}
|
|
124
|
+
const SKIP_DIRS = new Set([
|
|
125
|
+
'node_modules', '.git', 'dist', 'build', 'coverage',
|
|
126
|
+
'.next', '.nuxt', '__pycache__', '.venv', 'venv',
|
|
127
|
+
'.tox', '.mypy_cache', 'target', '.cache',
|
|
128
|
+
]);
|
|
129
|
+
async function walkDir(dir, results, depth) {
|
|
130
|
+
if (depth > 10 || results.length >= MAX_FILES_PER_SCAN)
|
|
131
|
+
return;
|
|
132
|
+
let entries;
|
|
133
|
+
try {
|
|
134
|
+
entries = await (0, promises_1.readdir)(dir, { withFileTypes: true });
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return; // Permission denied or similar
|
|
138
|
+
}
|
|
139
|
+
for (const entry of entries) {
|
|
140
|
+
if (results.length >= MAX_FILES_PER_SCAN)
|
|
141
|
+
break;
|
|
142
|
+
const fullPath = (0, node_path_1.join)(dir, entry.name);
|
|
143
|
+
if (entry.isDirectory()) {
|
|
144
|
+
if (!SKIP_DIRS.has(entry.name) && !entry.name.startsWith('.')) {
|
|
145
|
+
await walkDir(fullPath, results, depth + 1);
|
|
146
|
+
}
|
|
147
|
+
// Also check dotfiles that are security-relevant (e.g., .cursorrules)
|
|
148
|
+
if (entry.name === '.claude') {
|
|
149
|
+
await walkDir(fullPath, results, depth + 1);
|
|
150
|
+
}
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (!entry.isFile())
|
|
154
|
+
continue;
|
|
155
|
+
// Check by exact name first
|
|
156
|
+
if (SECURITY_RELEVANT_NAMES.has(entry.name)) {
|
|
157
|
+
if (await isWithinSizeLimit(fullPath)) {
|
|
158
|
+
results.push(fullPath);
|
|
159
|
+
}
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
// Check by extension
|
|
163
|
+
const ext = (0, node_path_1.extname)(entry.name).toLowerCase();
|
|
164
|
+
if (SECURITY_RELEVANT_EXTENSIONS.has(ext)) {
|
|
165
|
+
if (await isWithinSizeLimit(fullPath)) {
|
|
166
|
+
results.push(fullPath);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Dotfiles without extension (e.g., .env.local)
|
|
170
|
+
if (entry.name.startsWith('.env')) {
|
|
171
|
+
if (await isWithinSizeLimit(fullPath)) {
|
|
172
|
+
results.push(fullPath);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async function isWithinSizeLimit(filePath) {
|
|
178
|
+
try {
|
|
179
|
+
const s = await (0, promises_1.stat)(filePath);
|
|
180
|
+
return s.size <= MAX_FILE_SIZE && s.size > 0;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// ============================================================================
|
|
187
|
+
// Analyzer Orchestration
|
|
188
|
+
// ============================================================================
|
|
189
|
+
/**
|
|
190
|
+
* Run all four AST analyzers against a compiled SecurityAST.
|
|
191
|
+
* Each analyzer independently queries the AST structure.
|
|
192
|
+
*/
|
|
193
|
+
function runAllAnalyzers(ast, verifier) {
|
|
194
|
+
const findings = [];
|
|
195
|
+
// Capability analyzer does not require verifier (checks internally)
|
|
196
|
+
findings.push(...(0, capability_analyzer_js_1.analyzeCapabilities)(ast));
|
|
197
|
+
// Credential, governance, and scope analyzers require AST integrity verification
|
|
198
|
+
findings.push(...(0, credential_analyzer_js_1.analyzeCredentials)(ast, verifier));
|
|
199
|
+
findings.push(...(0, governance_analyzer_js_1.analyzeGovernance)(ast, verifier));
|
|
200
|
+
findings.push(...(0, scope_analyzer_js_1.analyzeScope)(ast, verifier));
|
|
201
|
+
return findings;
|
|
202
|
+
}
|
|
203
|
+
// ============================================================================
|
|
204
|
+
// Finding Merge (Defense-in-Depth)
|
|
205
|
+
// ============================================================================
|
|
206
|
+
/**
|
|
207
|
+
* Severity rank for comparison. Higher number = more severe.
|
|
208
|
+
*/
|
|
209
|
+
const SEVERITY_RANK = {
|
|
210
|
+
critical: 4,
|
|
211
|
+
high: 3,
|
|
212
|
+
medium: 2,
|
|
213
|
+
low: 1,
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Build a dedup key for matching AST findings to static findings.
|
|
217
|
+
* Two findings are considered "the same issue" if they share the same
|
|
218
|
+
* file path and a matching attack class or check ID prefix.
|
|
219
|
+
*/
|
|
220
|
+
function findingMatchKey(file, attackClass, checkId) {
|
|
221
|
+
const f = file ?? '__global__';
|
|
222
|
+
const a = attackClass ?? checkId.split('-').slice(0, 2).join('-');
|
|
223
|
+
return `${f}::${a}`;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Merge AST findings into the static findings list following defense-in-depth:
|
|
227
|
+
*
|
|
228
|
+
* 1. Start with ALL static findings (never remove any)
|
|
229
|
+
* 2. For each AST finding:
|
|
230
|
+
* a. If it matches a static finding (same file + attack class), upgrade
|
|
231
|
+
* severity if AST severity is higher. Never downgrade.
|
|
232
|
+
* b. If AST says passed but static says failed for the same issue,
|
|
233
|
+
* the static finding wins (suppression blocked via validateEnhancement).
|
|
234
|
+
* c. If no matching static finding, add the AST finding as a new finding.
|
|
235
|
+
*/
|
|
236
|
+
function mergeFindings(staticFindings, astFindings) {
|
|
237
|
+
// Clone static findings so we don't mutate the originals
|
|
238
|
+
const merged = staticFindings.map(f => ({ ...f }));
|
|
239
|
+
// Index static findings by match key for O(1) lookup
|
|
240
|
+
const staticIndex = new Map();
|
|
241
|
+
for (let i = 0; i < merged.length; i++) {
|
|
242
|
+
const key = findingMatchKey(merged[i].file, merged[i].attackClass, merged[i].checkId);
|
|
243
|
+
const indices = staticIndex.get(key) ?? [];
|
|
244
|
+
indices.push(i);
|
|
245
|
+
staticIndex.set(key, indices);
|
|
246
|
+
}
|
|
247
|
+
for (const astFinding of astFindings) {
|
|
248
|
+
// Skip passed AST findings -- they don't add value in defense-in-depth
|
|
249
|
+
// (they can't suppress static findings and don't represent new issues)
|
|
250
|
+
if (astFinding.passed)
|
|
251
|
+
continue;
|
|
252
|
+
const key = findingMatchKey(astFinding.file, astFinding.attackClass, astFinding.checkId);
|
|
253
|
+
const matchIndices = staticIndex.get(key);
|
|
254
|
+
if (matchIndices && matchIndices.length > 0) {
|
|
255
|
+
// Matching static finding(s) exist -- apply defense-in-depth rules
|
|
256
|
+
for (const idx of matchIndices) {
|
|
257
|
+
const staticFinding = merged[idx];
|
|
258
|
+
// Rule: If AST says passed but static says failed, static wins
|
|
259
|
+
if (!(0, defense_in_depth_js_1.validateEnhancement)(staticFinding.passed, astFinding.passed)) {
|
|
260
|
+
// Suppression blocked -- keep static finding as-is
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
// Rule: Use the higher severity (AST can only upgrade)
|
|
264
|
+
const astSeverity = normalizeSeverity(astFinding.severity);
|
|
265
|
+
const resolvedSeverity = (0, defense_in_depth_js_1.enforceSeverityFloor)(staticFinding.severity, astSeverity);
|
|
266
|
+
merged[idx] = {
|
|
267
|
+
...staticFinding,
|
|
268
|
+
severity: resolvedSeverity,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
// No matching static finding -- add as a new finding
|
|
274
|
+
merged.push(astFindingToSecurityFinding(astFinding));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return merged;
|
|
278
|
+
}
|
|
279
|
+
// ============================================================================
|
|
280
|
+
// Conversion Helpers
|
|
281
|
+
// ============================================================================
|
|
282
|
+
/**
|
|
283
|
+
* Convert an ASTFinding to a SecurityFinding for the merged output.
|
|
284
|
+
* The `file` property must be truthy for findings to pass the scanner filter.
|
|
285
|
+
*/
|
|
286
|
+
function astFindingToSecurityFinding(ast) {
|
|
287
|
+
return {
|
|
288
|
+
checkId: ast.checkId,
|
|
289
|
+
name: ast.name,
|
|
290
|
+
description: ast.description,
|
|
291
|
+
category: ast.category,
|
|
292
|
+
severity: normalizeSeverity(ast.severity),
|
|
293
|
+
passed: ast.passed,
|
|
294
|
+
message: ast.message,
|
|
295
|
+
fixable: ast.fixable,
|
|
296
|
+
file: ast.file ?? 'ast-analysis',
|
|
297
|
+
line: ast.line,
|
|
298
|
+
fix: ast.fix,
|
|
299
|
+
guidance: ast.guidance,
|
|
300
|
+
attackClass: ast.attackClass,
|
|
301
|
+
details: {
|
|
302
|
+
source: 'nanomind-ast',
|
|
303
|
+
confidence: ast.confidence,
|
|
304
|
+
evidence: ast.evidence,
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Normalize the 'info' severity level (used by AST analyzers) to 'low'
|
|
310
|
+
* (used by SecurityFinding). The SecurityFinding type does not include 'info'.
|
|
311
|
+
*/
|
|
312
|
+
function normalizeSeverity(severity) {
|
|
313
|
+
if (severity === 'info')
|
|
314
|
+
return 'low';
|
|
315
|
+
return severity;
|
|
316
|
+
}
|
|
317
|
+
//# sourceMappingURL=scanner-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner-bridge.js","sourceRoot":"","sources":["../../src/nanomind-core/scanner-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NanoMind Defense-in-Depth
|
|
3
|
+
*
|
|
4
|
+
* Core security principle: NanoMind is ADVISORY, never AUTHORITATIVE.
|
|
5
|
+
* Even a fully compromised NanoMind gains the attacker nothing.
|
|
6
|
+
*
|
|
7
|
+
* Rules:
|
|
8
|
+
* 1. NanoMind can UPGRADE findings (add, increase severity) but NEVER SUPPRESS
|
|
9
|
+
* 2. Static checks always run regardless of NanoMind's opinion
|
|
10
|
+
* 3. Simulation validates independently -- two systems must agree
|
|
11
|
+
* 4. NanoMind has zero access to credentials, secrets, or sensitive data
|
|
12
|
+
* 5. NanoMind daemon is sandboxed: localhost only, no filesystem beyond model
|
|
13
|
+
* 6. AST signatures verified at every consumer
|
|
14
|
+
* 7. Training data provenance tracked and Claude-reviewed
|
|
15
|
+
* 8. A finding from static analysis can never be removed by NanoMind
|
|
16
|
+
*/
|
|
17
|
+
import type { SecurityAST, IntentClass } from '../types.js';
|
|
18
|
+
export type SeverityLevel = 'critical' | 'high' | 'medium' | 'low' | 'info';
|
|
19
|
+
/**
|
|
20
|
+
* Enforce that NanoMind can only upgrade severity, never downgrade.
|
|
21
|
+
* If static analysis says HIGH, NanoMind can upgrade to CRITICAL
|
|
22
|
+
* but can NEVER downgrade to LOW or suppress the finding.
|
|
23
|
+
*
|
|
24
|
+
* This means even if NanoMind is compromised and always returns "benign",
|
|
25
|
+
* static findings are never suppressed.
|
|
26
|
+
*/
|
|
27
|
+
export declare function enforceSeverityFloor(staticSeverity: SeverityLevel, nanomindSeverity: SeverityLevel): SeverityLevel;
|
|
28
|
+
/**
|
|
29
|
+
* Validate that a NanoMind enhancement never suppresses a static finding.
|
|
30
|
+
* Returns true if the enhancement is valid (doesn't suppress).
|
|
31
|
+
*/
|
|
32
|
+
export declare function validateEnhancement(staticFindingPassed: boolean, nanomindSaysPassed: boolean): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Require agreement between NanoMind and at least one other system
|
|
35
|
+
* before classifying an artifact as definitively benign.
|
|
36
|
+
*
|
|
37
|
+
* NanoMind alone saying "benign" is not sufficient.
|
|
38
|
+
* Static checks must also show zero findings.
|
|
39
|
+
*/
|
|
40
|
+
export declare function requireBenignConsensus(nanomindIntent: IntentClass, staticFindingCount: number, simulationVerdict?: 'CLEAN' | 'SUSPICIOUS' | 'MALICIOUS'): {
|
|
41
|
+
finalClassification: IntentClass;
|
|
42
|
+
consensusReached: boolean;
|
|
43
|
+
reason: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Strip ALL credential-like content before sending to NanoMind.
|
|
47
|
+
* This goes beyond the input sanitizer (which strips meta-instructions).
|
|
48
|
+
* This strips actual secrets so NanoMind never sees real credentials.
|
|
49
|
+
*
|
|
50
|
+
* Even if the daemon is compromised, it cannot exfiltrate credentials
|
|
51
|
+
* because it never received them.
|
|
52
|
+
*/
|
|
53
|
+
export declare function redactSecretsForNanoMind(content: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* Verify AST hasn't been tampered with before any analyzer consumes it.
|
|
56
|
+
* Every analyzer MUST call this before processing.
|
|
57
|
+
*/
|
|
58
|
+
export declare function assertASTIntegrity(ast: SecurityAST, verifier: (ast: SecurityAST) => boolean): void;
|
|
59
|
+
export declare class SecurityError extends Error {
|
|
60
|
+
constructor(message: string);
|
|
61
|
+
}
|
|
62
|
+
export interface TrainingDataProvenance {
|
|
63
|
+
/** SHA-256 hash of the training sample */
|
|
64
|
+
contentHash: string;
|
|
65
|
+
/** Where this sample came from */
|
|
66
|
+
source: 'registry_scan' | 'simulation' | 'attack_session' | 'hma_payload' | 'dvaa' | 'manual';
|
|
67
|
+
/** Who/what labeled it */
|
|
68
|
+
labeledBy: 'heuristic' | 'nanomind' | 'claude_review' | 'human';
|
|
69
|
+
/** Confidence in the label */
|
|
70
|
+
confidence: number;
|
|
71
|
+
/** When it was created */
|
|
72
|
+
createdAt: string;
|
|
73
|
+
/** Has this been reviewed by Claude? */
|
|
74
|
+
claudeReviewed: boolean;
|
|
75
|
+
/** Signature of the provenance record */
|
|
76
|
+
signature: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Verify that a training sample has valid provenance.
|
|
80
|
+
* Samples without provenance are rejected from training.
|
|
81
|
+
*/
|
|
82
|
+
export declare function verifyTrainingProvenance(provenance: TrainingDataProvenance): boolean;
|
|
83
|
+
export interface NanoMindAuditEvent {
|
|
84
|
+
timestamp: string;
|
|
85
|
+
event: 'classification' | 'suppression_blocked' | 'ast_tamper_detected' | 'secret_redacted' | 'manipulation_detected' | 'consensus_override';
|
|
86
|
+
details: string;
|
|
87
|
+
artifactHash?: string;
|
|
88
|
+
severity: 'info' | 'warning' | 'critical';
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Log a NanoMind security event. These events are immutable and
|
|
92
|
+
* should be forwarded to the transparency log.
|
|
93
|
+
*/
|
|
94
|
+
export declare function logSecurityEvent(event: Omit<NanoMindAuditEvent, 'timestamp'>): void;
|
|
95
|
+
/**
|
|
96
|
+
* Get all audit events since a given timestamp.
|
|
97
|
+
*/
|
|
98
|
+
export declare function getAuditEvents(since?: string): NanoMindAuditEvent[];
|
|
99
|
+
//# sourceMappingURL=defense-in-depth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defense-in-depth.d.ts","sourceRoot":"","sources":["../../../src/nanomind-core/security/defense-in-depth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM5D,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;AAU5E;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,cAAc,EAAE,aAAa,EAC7B,gBAAgB,EAAE,aAAa,GAC9B,aAAa,CAMf;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,mBAAmB,EAAE,OAAO,EAC5B,kBAAkB,EAAE,OAAO,GAC1B,OAAO,CAMT;AAMD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,WAAW,EAC3B,kBAAkB,EAAE,MAAM,EAC1B,iBAAiB,CAAC,EAAE,OAAO,GAAG,YAAY,GAAG,WAAW,GACvD;IACD,mBAAmB,EAAE,WAAW,CAAC;IACjC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB,CA8CA;AAMD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAqBhE;AAMD;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,OAAO,GACtC,IAAI,CAaN;AAED,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAI5B;AAMD,MAAM,WAAW,sBAAsB;IACrC,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,MAAM,EAAE,eAAe,GAAG,YAAY,GAAG,gBAAgB,GAAG,aAAa,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC9F,0BAA0B;IAC1B,SAAS,EAAE,WAAW,GAAG,UAAU,GAAG,eAAe,GAAG,OAAO,CAAC;IAChE,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,cAAc,EAAE,OAAO,CAAC;IACxB,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,sBAAsB,GAAG,OAAO,CAYpF;AAMD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,gBAAgB,GAAG,qBAAqB,GAAG,qBAAqB,GAAG,iBAAiB,GAAG,uBAAuB,GAAG,oBAAoB,CAAC;IAC7I,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;CAC3C;AAID;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,GAAG,IAAI,CAKnF;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAGnE"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* NanoMind Defense-in-Depth
|
|
4
|
+
*
|
|
5
|
+
* Core security principle: NanoMind is ADVISORY, never AUTHORITATIVE.
|
|
6
|
+
* Even a fully compromised NanoMind gains the attacker nothing.
|
|
7
|
+
*
|
|
8
|
+
* Rules:
|
|
9
|
+
* 1. NanoMind can UPGRADE findings (add, increase severity) but NEVER SUPPRESS
|
|
10
|
+
* 2. Static checks always run regardless of NanoMind's opinion
|
|
11
|
+
* 3. Simulation validates independently -- two systems must agree
|
|
12
|
+
* 4. NanoMind has zero access to credentials, secrets, or sensitive data
|
|
13
|
+
* 5. NanoMind daemon is sandboxed: localhost only, no filesystem beyond model
|
|
14
|
+
* 6. AST signatures verified at every consumer
|
|
15
|
+
* 7. Training data provenance tracked and Claude-reviewed
|
|
16
|
+
* 8. A finding from static analysis can never be removed by NanoMind
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.SecurityError = void 0;
|
|
20
|
+
exports.enforceSeverityFloor = enforceSeverityFloor;
|
|
21
|
+
exports.validateEnhancement = validateEnhancement;
|
|
22
|
+
exports.requireBenignConsensus = requireBenignConsensus;
|
|
23
|
+
exports.redactSecretsForNanoMind = redactSecretsForNanoMind;
|
|
24
|
+
exports.assertASTIntegrity = assertASTIntegrity;
|
|
25
|
+
exports.verifyTrainingProvenance = verifyTrainingProvenance;
|
|
26
|
+
exports.logSecurityEvent = logSecurityEvent;
|
|
27
|
+
exports.getAuditEvents = getAuditEvents;
|
|
28
|
+
const SEVERITY_ORDER = {
|
|
29
|
+
critical: 5,
|
|
30
|
+
high: 4,
|
|
31
|
+
medium: 3,
|
|
32
|
+
low: 2,
|
|
33
|
+
info: 1,
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Enforce that NanoMind can only upgrade severity, never downgrade.
|
|
37
|
+
* If static analysis says HIGH, NanoMind can upgrade to CRITICAL
|
|
38
|
+
* but can NEVER downgrade to LOW or suppress the finding.
|
|
39
|
+
*
|
|
40
|
+
* This means even if NanoMind is compromised and always returns "benign",
|
|
41
|
+
* static findings are never suppressed.
|
|
42
|
+
*/
|
|
43
|
+
function enforceSeverityFloor(staticSeverity, nanomindSeverity) {
|
|
44
|
+
const staticRank = SEVERITY_ORDER[staticSeverity];
|
|
45
|
+
const nmRank = SEVERITY_ORDER[nanomindSeverity];
|
|
46
|
+
// NanoMind can only make it MORE severe, never less
|
|
47
|
+
return nmRank >= staticRank ? nanomindSeverity : staticSeverity;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Validate that a NanoMind enhancement never suppresses a static finding.
|
|
51
|
+
* Returns true if the enhancement is valid (doesn't suppress).
|
|
52
|
+
*/
|
|
53
|
+
function validateEnhancement(staticFindingPassed, nanomindSaysPassed) {
|
|
54
|
+
// Static said FAIL → NanoMind cannot change it to PASS
|
|
55
|
+
if (!staticFindingPassed && nanomindSaysPassed) {
|
|
56
|
+
return false; // BLOCKED: suppression attempt
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// Rule 3: Two-System Agreement for Benign Classification
|
|
62
|
+
// ============================================================================
|
|
63
|
+
/**
|
|
64
|
+
* Require agreement between NanoMind and at least one other system
|
|
65
|
+
* before classifying an artifact as definitively benign.
|
|
66
|
+
*
|
|
67
|
+
* NanoMind alone saying "benign" is not sufficient.
|
|
68
|
+
* Static checks must also show zero findings.
|
|
69
|
+
*/
|
|
70
|
+
function requireBenignConsensus(nanomindIntent, staticFindingCount, simulationVerdict) {
|
|
71
|
+
// If static found issues, NanoMind's "benign" is overruled
|
|
72
|
+
if (nanomindIntent === 'benign' && staticFindingCount > 0) {
|
|
73
|
+
return {
|
|
74
|
+
finalClassification: 'suspicious',
|
|
75
|
+
consensusReached: false,
|
|
76
|
+
reason: `NanoMind says benign but ${staticFindingCount} static finding(s) exist. Static findings cannot be suppressed.`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
// If NanoMind says malicious, trust it (it can only upgrade)
|
|
80
|
+
if (nanomindIntent === 'malicious') {
|
|
81
|
+
return {
|
|
82
|
+
finalClassification: 'malicious',
|
|
83
|
+
consensusReached: true,
|
|
84
|
+
reason: 'NanoMind classified as malicious. Malicious classifications are always trusted (can only upgrade).',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// If simulation ran and disagrees with NanoMind
|
|
88
|
+
if (simulationVerdict === 'MALICIOUS' && nanomindIntent === 'benign') {
|
|
89
|
+
return {
|
|
90
|
+
finalClassification: 'malicious',
|
|
91
|
+
consensusReached: false,
|
|
92
|
+
reason: 'Simulation observed malicious behavior. NanoMind benign classification overruled by behavioral evidence.',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// Consensus: NanoMind benign + zero static findings + simulation clean (or not run)
|
|
96
|
+
if (nanomindIntent === 'benign' && staticFindingCount === 0) {
|
|
97
|
+
const simClean = !simulationVerdict || simulationVerdict === 'CLEAN';
|
|
98
|
+
return {
|
|
99
|
+
finalClassification: 'benign',
|
|
100
|
+
consensusReached: simClean,
|
|
101
|
+
reason: simClean
|
|
102
|
+
? 'Consensus: NanoMind benign + zero static findings + simulation clean.'
|
|
103
|
+
: 'NanoMind benign + zero static findings, but simulation not yet run.',
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// Default: suspicious when no consensus
|
|
107
|
+
return {
|
|
108
|
+
finalClassification: 'suspicious',
|
|
109
|
+
consensusReached: false,
|
|
110
|
+
reason: 'No consensus reached. Treated as suspicious until verified.',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// Rule 4: NanoMind Has Zero Access to Secrets
|
|
115
|
+
// ============================================================================
|
|
116
|
+
/**
|
|
117
|
+
* Strip ALL credential-like content before sending to NanoMind.
|
|
118
|
+
* This goes beyond the input sanitizer (which strips meta-instructions).
|
|
119
|
+
* This strips actual secrets so NanoMind never sees real credentials.
|
|
120
|
+
*
|
|
121
|
+
* Even if the daemon is compromised, it cannot exfiltrate credentials
|
|
122
|
+
* because it never received them.
|
|
123
|
+
*/
|
|
124
|
+
function redactSecretsForNanoMind(content) {
|
|
125
|
+
let redacted = content;
|
|
126
|
+
// API keys
|
|
127
|
+
redacted = redacted.replace(/sk-ant-api\d{2}-[a-zA-Z0-9_-]{20,}/g, '[REDACTED_ANTHROPIC_KEY]');
|
|
128
|
+
redacted = redacted.replace(/sk-proj-[a-zA-Z0-9]{20,}/g, '[REDACTED_OPENAI_KEY]');
|
|
129
|
+
redacted = redacted.replace(/AKIA[0-9A-Z]{16}/g, '[REDACTED_AWS_KEY]');
|
|
130
|
+
redacted = redacted.replace(/ghp_[a-zA-Z0-9]{36}/g, '[REDACTED_GITHUB_TOKEN]');
|
|
131
|
+
redacted = redacted.replace(/glpat-[a-zA-Z0-9_-]{20,}/g, '[REDACTED_GITLAB_TOKEN]');
|
|
132
|
+
// Generic secret patterns
|
|
133
|
+
redacted = redacted.replace(/-----BEGIN [A-Z ]+ KEY-----[\s\S]*?-----END [A-Z ]+ KEY-----/g, '[REDACTED_PRIVATE_KEY]');
|
|
134
|
+
redacted = redacted.replace(/(?:password|secret|token|key)\s*[=:]\s*['"][^'"]{8,}['"]/gi, (match) => {
|
|
135
|
+
const prefix = match.split(/[=:]/)[0];
|
|
136
|
+
return `${prefix}=[REDACTED]`;
|
|
137
|
+
});
|
|
138
|
+
// Connection strings
|
|
139
|
+
redacted = redacted.replace(/(?:postgres|mysql|mongodb|redis):\/\/[^\s'"]+/gi, '[REDACTED_CONNECTION_STRING]');
|
|
140
|
+
return redacted;
|
|
141
|
+
}
|
|
142
|
+
// ============================================================================
|
|
143
|
+
// Rule 6: AST Integrity Verification
|
|
144
|
+
// ============================================================================
|
|
145
|
+
/**
|
|
146
|
+
* Verify AST hasn't been tampered with before any analyzer consumes it.
|
|
147
|
+
* Every analyzer MUST call this before processing.
|
|
148
|
+
*/
|
|
149
|
+
function assertASTIntegrity(ast, verifier) {
|
|
150
|
+
if (!ast.signature) {
|
|
151
|
+
throw new SecurityError('AST has no signature. Refusing to process unsigned AST.');
|
|
152
|
+
}
|
|
153
|
+
if (!ast.contentHash) {
|
|
154
|
+
throw new SecurityError('AST has no content hash. Refusing to process.');
|
|
155
|
+
}
|
|
156
|
+
if (!verifier(ast)) {
|
|
157
|
+
throw new SecurityError('AST signature verification FAILED. The AST may have been tampered with. ' +
|
|
158
|
+
'This is a critical security event that should be investigated.');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
class SecurityError extends Error {
|
|
162
|
+
constructor(message) {
|
|
163
|
+
super(message);
|
|
164
|
+
this.name = 'SecurityError';
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
exports.SecurityError = SecurityError;
|
|
168
|
+
/**
|
|
169
|
+
* Verify that a training sample has valid provenance.
|
|
170
|
+
* Samples without provenance are rejected from training.
|
|
171
|
+
*/
|
|
172
|
+
function verifyTrainingProvenance(provenance) {
|
|
173
|
+
if (!provenance.contentHash)
|
|
174
|
+
return false;
|
|
175
|
+
if (!provenance.source)
|
|
176
|
+
return false;
|
|
177
|
+
if (!provenance.labeledBy)
|
|
178
|
+
return false;
|
|
179
|
+
if (provenance.confidence < 0 || provenance.confidence > 1)
|
|
180
|
+
return false;
|
|
181
|
+
// High-risk: samples from external sources must be Claude-reviewed
|
|
182
|
+
if (provenance.source === 'registry_scan' && !provenance.claudeReviewed) {
|
|
183
|
+
return false; // External data must be validated before training
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
const auditLog = [];
|
|
188
|
+
/**
|
|
189
|
+
* Log a NanoMind security event. These events are immutable and
|
|
190
|
+
* should be forwarded to the transparency log.
|
|
191
|
+
*/
|
|
192
|
+
function logSecurityEvent(event) {
|
|
193
|
+
auditLog.push({
|
|
194
|
+
...event,
|
|
195
|
+
timestamp: new Date().toISOString(),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Get all audit events since a given timestamp.
|
|
200
|
+
*/
|
|
201
|
+
function getAuditEvents(since) {
|
|
202
|
+
if (!since)
|
|
203
|
+
return [...auditLog];
|
|
204
|
+
return auditLog.filter(e => e.timestamp >= since);
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=defense-in-depth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defense-in-depth.js","sourceRoot":"","sources":["../../../src/nanomind-core/security/defense-in-depth.ts"],"names":[],"mappings":"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"}
|