hackmyagent 0.17.7 → 0.17.9
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/dist/.integrity-manifest.json +1 -1
- package/dist/arp/enforcement/kill-switch.d.ts.map +1 -1
- package/dist/arp/enforcement/kill-switch.js +8 -0
- package/dist/arp/enforcement/kill-switch.js.map +1 -1
- package/dist/cli.js +237 -30
- package/dist/cli.js.map +1 -1
- package/dist/hardening/scanner.d.ts +36 -0
- package/dist/hardening/scanner.d.ts.map +1 -1
- package/dist/hardening/scanner.js +120 -2
- package/dist/hardening/scanner.js.map +1 -1
- package/dist/hardening/security-check.d.ts +2 -1
- package/dist/hardening/security-check.d.ts.map +1 -1
- package/dist/nanomind-core/analyzers/capability-analyzer.d.ts +2 -1
- package/dist/nanomind-core/analyzers/capability-analyzer.d.ts.map +1 -1
- package/dist/nanomind-core/analyzers/capability-analyzer.js +44 -16
- package/dist/nanomind-core/analyzers/capability-analyzer.js.map +1 -1
- package/dist/nanomind-core/analyzers/credential-analyzer.d.ts +2 -1
- package/dist/nanomind-core/analyzers/credential-analyzer.d.ts.map +1 -1
- package/dist/nanomind-core/analyzers/credential-analyzer.js +57 -30
- package/dist/nanomind-core/analyzers/credential-analyzer.js.map +1 -1
- package/dist/nanomind-core/analyzers/governance-analyzer.d.ts +2 -1
- package/dist/nanomind-core/analyzers/governance-analyzer.d.ts.map +1 -1
- package/dist/nanomind-core/analyzers/governance-analyzer.js +7 -1
- package/dist/nanomind-core/analyzers/governance-analyzer.js.map +1 -1
- package/dist/nanomind-core/analyzers/prompt-analyzer.d.ts +2 -1
- package/dist/nanomind-core/analyzers/prompt-analyzer.d.ts.map +1 -1
- package/dist/nanomind-core/analyzers/prompt-analyzer.js +6 -1
- package/dist/nanomind-core/analyzers/prompt-analyzer.js.map +1 -1
- package/dist/nanomind-core/analyzers/scope-analyzer.d.ts +2 -1
- package/dist/nanomind-core/analyzers/scope-analyzer.d.ts.map +1 -1
- package/dist/nanomind-core/analyzers/scope-analyzer.js +6 -1
- package/dist/nanomind-core/analyzers/scope-analyzer.js.map +1 -1
- package/dist/nanomind-core/inference/analm-infer-llamacpp.py +113 -0
- package/dist/nanomind-core/inference/security-analyst.d.ts +1 -1
- package/dist/nanomind-core/inference/security-analyst.d.ts.map +1 -1
- package/dist/nanomind-core/inference/security-analyst.js +108 -21
- package/dist/nanomind-core/inference/security-analyst.js.map +1 -1
- package/dist/nanomind-core/orchestrate.d.ts +2 -1
- package/dist/nanomind-core/orchestrate.d.ts.map +1 -1
- package/dist/nanomind-core/orchestrate.js +1 -1
- package/dist/nanomind-core/orchestrate.js.map +1 -1
- package/dist/nanomind-core/scanner-bridge.d.ts +2 -2
- package/dist/nanomind-core/scanner-bridge.d.ts.map +1 -1
- package/dist/nanomind-core/scanner-bridge.js +67 -12
- package/dist/nanomind-core/scanner-bridge.js.map +1 -1
- package/dist/plugins/credvault.d.ts.map +1 -1
- package/dist/plugins/credvault.js +16 -6
- package/dist/plugins/credvault.js.map +1 -1
- package/dist/plugins/signcrypt.d.ts.map +1 -1
- package/dist/plugins/signcrypt.js +11 -8
- package/dist/plugins/signcrypt.js.map +1 -1
- package/dist/plugins/skillguard.d.ts.map +1 -1
- package/dist/plugins/skillguard.js +12 -9
- package/dist/plugins/skillguard.js.map +1 -1
- package/package.json +2 -2
|
@@ -51,6 +51,36 @@ export declare function calculateSecurityScore(findings: Array<{
|
|
|
51
51
|
score: number;
|
|
52
52
|
maxScore: number;
|
|
53
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* Check if a finding applies to the given project type based on the
|
|
56
|
+
* CHECK_PROJECT_TYPES map. Exported so CLI can filter findings after
|
|
57
|
+
* NanoMind merge.
|
|
58
|
+
*/
|
|
59
|
+
export declare function findingAppliesTo(finding: SecurityFinding, projectType: ProjectType): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Parsed .hmaignore rules split into path patterns and check ID patterns.
|
|
62
|
+
* Check ID patterns start with `!` and support trailing `*` wildcards.
|
|
63
|
+
* Example: `!SANDBOX-*` suppresses all SANDBOX checks.
|
|
64
|
+
*/
|
|
65
|
+
export interface HmaIgnoreRules {
|
|
66
|
+
paths: string[];
|
|
67
|
+
checkIds: string[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Load .hmaignore patterns from a target directory. Exported so CLI
|
|
71
|
+
* can re-apply ignore filtering after NanoMind merge.
|
|
72
|
+
*/
|
|
73
|
+
export declare function loadHmaIgnore(targetDir: string): Promise<HmaIgnoreRules>;
|
|
74
|
+
/**
|
|
75
|
+
* Check if a file path matches any .hmaignore path pattern. Exported so CLI
|
|
76
|
+
* can filter findings after NanoMind merge.
|
|
77
|
+
*/
|
|
78
|
+
export declare function isPathIgnored(filePath: string, ignoredPaths: string[]): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Check if a checkId matches any .hmaignore check ID pattern.
|
|
81
|
+
* Supports exact match and trailing `*` wildcard (e.g. `SANDBOX-*`).
|
|
82
|
+
*/
|
|
83
|
+
export declare function isCheckIgnored(checkId: string, ignoredChecks: string[]): boolean;
|
|
54
84
|
export declare class HardeningScanner {
|
|
55
85
|
private cliName;
|
|
56
86
|
private static readonly BACKUP_FILES;
|
|
@@ -83,6 +113,12 @@ export declare class HardeningScanner {
|
|
|
83
113
|
* Detect the project type based on package.json and project structure
|
|
84
114
|
*/
|
|
85
115
|
private detectProjectType;
|
|
116
|
+
/**
|
|
117
|
+
* Detect if a package is an SDK/API client. Requires 2+ independent
|
|
118
|
+
* signals to avoid false positives (a random library with axios isn't
|
|
119
|
+
* necessarily an SDK).
|
|
120
|
+
*/
|
|
121
|
+
private detectSDKPackage;
|
|
86
122
|
/**
|
|
87
123
|
* Check if a finding applies to the given project type
|
|
88
124
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/hardening/scanner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAY,WAAW,EAAE,MAAM,kBAAkB,CAAC;AA4G3F,0CAA0C;AAC1C,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;AAEtD,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,oDAAoD;IACpD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA6HD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG;IACrJ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB,CAgCA;AA6GD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,OAAO,CAAiB;IAEhC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CA2BlC;IAEF;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAM7B;;;OAGG;YACW,aAAa;IAwB3B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAa3B;;;OAGG;IACG,oBAAoB,CACxB,QAAQ,EAAE,eAAe,EAAE,EAC3B,SAAS,EAAE,MAAM,EACjB,qBAAqB,CAAC,EAAE,MAAM,EAAE,GAC/B,OAAO,CAAC,eAAe,EAAE,CAAC;IAgB7B;;OAEG;IACH,OAAO,CAAC,aAAa;IASf,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/hardening/scanner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAY,WAAW,EAAE,MAAM,kBAAkB,CAAC;AA4G3F,0CAA0C;AAC1C,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;AAEtD,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,oDAAoD;IACpD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA6HD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG;IACrJ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB,CAgCA;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAQ5F;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAe9E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAO/E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAQhF;AA6GD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,OAAO,CAAiB;IAEhC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CA2BlC;IAEF;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAM7B;;;OAGG;YACW,aAAa;IAwB3B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAa3B;;;OAGG;IACG,oBAAoB,CACxB,QAAQ,EAAE,eAAe,EAAE,EAC3B,SAAS,EAAE,MAAM,EACjB,qBAAqB,CAAC,EAAE,MAAM,EAAE,GAC/B,OAAO,CAAC,eAAe,EAAE,CAAC;IAgB7B;;OAEG;IACH,OAAO,CAAC,aAAa;IASf,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;YAgavC,cAAc;IAwE5B;;OAEG;YACW,iBAAiB;IA4G/B;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IA8CxB;;OAEG;IACH,gBAAgB,CAAC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO;YAe/D,uBAAuB;YA4GvB,aAAa;YAiDb,cAAc;YAiGd,oBAAoB;YAyDpB,gBAAgB;YAgJhB,oBAAoB;YAkFpB,gBAAgB;YA8IhB,mBAAmB;YA8EnB,iBAAiB;YA0CjB,iBAAiB;YAiEjB,wBAAwB;YA6FxB,wBAAwB;YAqExB,wBAAwB;YAyHxB,oBAAoB;YAmHpB,uBAAuB;YA4IvB,iBAAiB;YAkHjB,oBAAoB;YA0HpB,mBAAmB;YAqGnB,gBAAgB;YAwIhB,oBAAoB;YAwIpB,gBAAgB;YA6HhB,qBAAqB;YAmHrB,eAAe;IAqI7B;;OAEG;YACW,mBAAmB;IAkHjC;;OAEG;YACW,oBAAoB;IAqKlC;;OAEG;YACW,iBAAiB;IAgJ/B;;OAEG;YACW,oBAAoB;IA4IlC;;OAEG;YACW,eAAe;IAyJ7B;;OAEG;YACW,eAAe;IA2I7B;;OAEG;YACW,eAAe;IA6G7B;;OAEG;YACW,mBAAmB;IAuHjC,cAAc,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG;QAC3C,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;KAClB;IAID;;OAEG;YACW,YAAY;IAmE1B;;OAEG;IACG,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6DhD;;;OAGG;YACW,cAAc;IAgD5B;;OAEG;YACW,mBAAmB;IA8qBjC;;;OAGG;YACW,kBAAkB;IAgDhC;;OAEG;YACW,sBAAsB;IAkMpC;;OAEG;YACW,sBAAsB;IA+BpC;;OAEG;YACW,oBAAoB;IAgWlC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA4B3B;;OAEG;YACW,iBAAiB;IA8D/B;;OAEG;YACW,mBAAmB;IAsXjC;;OAEG;YACW,wBAAwB;IAqPtC;;OAEG;YACW,gBAAgB;IAoK9B;;;OAGG;YACW,eAAe;IAoD7B;;;OAGG;YACW,aAAa;IAwC3B;;;OAGG;YACW,oBAAoB;IAoKlC;;;OAGG;YACW,iBAAiB;IAiI/B;;;OAGG;YACW,kBAAkB;IAkFhC;;;OAGG;YACW,aAAa;IA0F3B;;OAEG;YACW,gBAAgB;IAiE9B;;;;OAIG;YACW,yBAAyB;IA+YvC;;;;;OAKG;YACW,qBAAqB;IAqnBnC;;;;OAIG;YACW,gBAAgB;IA2G9B;;;;OAIG;YACW,mBAAmB;IAmKjC;;;;OAIG;YACW,gBAAgB;IAkF9B;;;OAGG;YACW,iBAAiB;IA+C/B;;;;OAIG;YACW,yBAAyB;IA6FvC;;;OAGG;YACW,kBAAkB;IA8ChC;;;OAGG;YACW,mBAAmB;IA4CjC;;;OAGG;YACW,6BAA6B;IAiD3C;;;OAGG;YACW,oBAAoB;IA4ClC;;;OAGG;YACW,WAAW;IA4DzB;;;OAGG;YACW,aAAa;IAgD3B;;;OAGG;YACW,oBAAoB;IA6ClC;;;OAGG;YACW,YAAY;IAmD1B;;;OAGG;YACW,qBAAqB;IA+DnC;;;;OAIG;YACW,oBAAoB;IAyHlC;;;OAGG;YACW,iBAAiB;IA+F/B;;;OAGG;YACW,4BAA4B;IAqD1C;;;OAGG;YACW,8BAA8B;IAgE5C;;;;OAIG;YACW,qBAAqB;IAgBnC,+DAA+D;YACjD,YAAY;CA+B3B"}
|
|
@@ -39,6 +39,10 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
40
|
exports.HardeningScanner = void 0;
|
|
41
41
|
exports.calculateSecurityScore = calculateSecurityScore;
|
|
42
|
+
exports.findingAppliesTo = findingAppliesTo;
|
|
43
|
+
exports.loadHmaIgnore = loadHmaIgnore;
|
|
44
|
+
exports.isPathIgnored = isPathIgnored;
|
|
45
|
+
exports.isCheckIgnored = isCheckIgnored;
|
|
42
46
|
const fs = __importStar(require("fs/promises"));
|
|
43
47
|
const crypto = __importStar(require("crypto"));
|
|
44
48
|
const path = __importStar(require("path"));
|
|
@@ -283,6 +287,69 @@ function calculateSecurityScore(findings) {
|
|
|
283
287
|
: Math.round(100 * Math.exp(-weightedSum / DECAY_CONSTANT));
|
|
284
288
|
return { score, maxScore: 100 };
|
|
285
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* Check if a finding applies to the given project type based on the
|
|
292
|
+
* CHECK_PROJECT_TYPES map. Exported so CLI can filter findings after
|
|
293
|
+
* NanoMind merge.
|
|
294
|
+
*/
|
|
295
|
+
function findingAppliesTo(finding, projectType) {
|
|
296
|
+
for (const [prefix, types] of Object.entries(CHECK_PROJECT_TYPES)) {
|
|
297
|
+
if (finding.checkId.startsWith(prefix)) {
|
|
298
|
+
if (types.includes('all'))
|
|
299
|
+
return true;
|
|
300
|
+
return types.includes(projectType);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Load .hmaignore patterns from a target directory. Exported so CLI
|
|
307
|
+
* can re-apply ignore filtering after NanoMind merge.
|
|
308
|
+
*/
|
|
309
|
+
async function loadHmaIgnore(targetDir) {
|
|
310
|
+
const ignorePath = path.join(targetDir, '.hmaignore');
|
|
311
|
+
try {
|
|
312
|
+
const content = await fs.readFile(ignorePath, 'utf-8');
|
|
313
|
+
const lines = content
|
|
314
|
+
.split('\n')
|
|
315
|
+
.map(line => line.trim())
|
|
316
|
+
.filter(line => line && !line.startsWith('#'));
|
|
317
|
+
return {
|
|
318
|
+
paths: lines.filter(l => !l.startsWith('!')),
|
|
319
|
+
checkIds: lines.filter(l => l.startsWith('!')).map(l => l.slice(1)),
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
catch {
|
|
323
|
+
return { paths: [], checkIds: [] };
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Check if a file path matches any .hmaignore path pattern. Exported so CLI
|
|
328
|
+
* can filter findings after NanoMind merge.
|
|
329
|
+
*/
|
|
330
|
+
function isPathIgnored(filePath, ignoredPaths) {
|
|
331
|
+
if (!filePath || ignoredPaths.length === 0)
|
|
332
|
+
return false;
|
|
333
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
334
|
+
return ignoredPaths.some(pattern => {
|
|
335
|
+
const normalizedPattern = pattern.replace(/\\/g, '/').replace(/\/$/, '');
|
|
336
|
+
return normalized.startsWith(normalizedPattern + '/') || normalized === normalizedPattern;
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Check if a checkId matches any .hmaignore check ID pattern.
|
|
341
|
+
* Supports exact match and trailing `*` wildcard (e.g. `SANDBOX-*`).
|
|
342
|
+
*/
|
|
343
|
+
function isCheckIgnored(checkId, ignoredChecks) {
|
|
344
|
+
if (!checkId || ignoredChecks.length === 0)
|
|
345
|
+
return false;
|
|
346
|
+
return ignoredChecks.some(pattern => {
|
|
347
|
+
if (pattern.endsWith('*')) {
|
|
348
|
+
return checkId.startsWith(pattern.slice(0, -1));
|
|
349
|
+
}
|
|
350
|
+
return checkId === pattern;
|
|
351
|
+
});
|
|
352
|
+
}
|
|
286
353
|
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB max file size to prevent memory exhaustion
|
|
287
354
|
const MAX_LINE_LENGTH = 10000; // 10KB max line length for regex safety
|
|
288
355
|
/** Shell-escape a string for safe interpolation into advisory fix commands. */
|
|
@@ -488,6 +555,7 @@ class HardeningScanner {
|
|
|
488
555
|
// Check ID suppression patterns from .hmaignore (supports wildcards)
|
|
489
556
|
const suppressedCheckPatterns = hmaIgnore.checkIds;
|
|
490
557
|
// Normalize ignore list to uppercase for case-insensitive matching
|
|
558
|
+
// Merge CLI --ignore flags with .hmaignore !-prefixed check IDs
|
|
491
559
|
const ignoredChecks = new Set(ignore.map((id) => id.toUpperCase()));
|
|
492
560
|
// In dry-run mode, we detect what would be fixed but don't modify anything
|
|
493
561
|
const shouldFix = autoFix && !dryRun;
|
|
@@ -879,7 +947,7 @@ class HardeningScanner {
|
|
|
879
947
|
*/
|
|
880
948
|
async detectProjectType(targetDir) {
|
|
881
949
|
// Check for OpenClaw project indicators (check first as it's more specific)
|
|
882
|
-
const openclawIndicators = ['.openclaw', '.moltbot', '.clawdbot', 'SKILL.md', 'openclaw.json'];
|
|
950
|
+
const openclawIndicators = ['.openclaw', '.moltbot', '.clawdbot', 'SKILL.md', 'HEARTBEAT.md', 'openclaw.json'];
|
|
883
951
|
for (const indicator of openclawIndicators) {
|
|
884
952
|
try {
|
|
885
953
|
await fs.access(path.join(targetDir, indicator));
|
|
@@ -887,7 +955,7 @@ class HardeningScanner {
|
|
|
887
955
|
}
|
|
888
956
|
catch { }
|
|
889
957
|
}
|
|
890
|
-
// Check for *.skill.md files (OpenClaw skill project)
|
|
958
|
+
// Check for *.skill.md files at root level (OpenClaw skill project)
|
|
891
959
|
try {
|
|
892
960
|
const files = await fs.readdir(targetDir);
|
|
893
961
|
if (files.some(f => f.endsWith('.skill.md'))) {
|
|
@@ -895,6 +963,12 @@ class HardeningScanner {
|
|
|
895
963
|
}
|
|
896
964
|
}
|
|
897
965
|
catch { }
|
|
966
|
+
// Check for skills/ subdirectory with SKILL.md files (common OpenClaw layout)
|
|
967
|
+
try {
|
|
968
|
+
await fs.access(path.join(targetDir, 'skills'));
|
|
969
|
+
return 'openclaw';
|
|
970
|
+
}
|
|
971
|
+
catch { }
|
|
898
972
|
try {
|
|
899
973
|
const pkgPath = path.join(targetDir, 'package.json');
|
|
900
974
|
const content = await fs.readFile(pkgPath, 'utf-8');
|
|
@@ -910,6 +984,12 @@ class HardeningScanner {
|
|
|
910
984
|
pkg.name?.includes('mcp')) {
|
|
911
985
|
return 'mcp';
|
|
912
986
|
}
|
|
987
|
+
// Check for SDK/API client packages (requires 2+ signals).
|
|
988
|
+
// Must run before CLI check: some SDKs ship CLI shims (e.g., openai)
|
|
989
|
+
// but are primarily libraries.
|
|
990
|
+
if (this.detectSDKPackage(pkg, allDeps)) {
|
|
991
|
+
return 'sdk';
|
|
992
|
+
}
|
|
913
993
|
// Check if it's a CLI tool (has bin field)
|
|
914
994
|
if (pkg.bin) {
|
|
915
995
|
return 'cli';
|
|
@@ -959,6 +1039,44 @@ class HardeningScanner {
|
|
|
959
1039
|
// Default to library for generic projects
|
|
960
1040
|
return 'library';
|
|
961
1041
|
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Detect if a package is an SDK/API client. Requires 2+ independent
|
|
1044
|
+
* signals to avoid false positives (a random library with axios isn't
|
|
1045
|
+
* necessarily an SDK).
|
|
1046
|
+
*/
|
|
1047
|
+
detectSDKPackage(pkg, allDeps) {
|
|
1048
|
+
const name = (pkg.name ?? '').toLowerCase();
|
|
1049
|
+
const desc = (pkg.description ?? '').toLowerCase();
|
|
1050
|
+
const keywords = pkg.keywords ?? [];
|
|
1051
|
+
// Signal 1: Package name contains SDK/client indicators
|
|
1052
|
+
const nameSignals = ['/sdk', '-sdk', '-client', 'api-client', '-api']
|
|
1053
|
+
.some(s => name.includes(s)) || name.endsWith('sdk');
|
|
1054
|
+
// Signal 2: Description mentions SDK/client/wrapper/library-for-API patterns
|
|
1055
|
+
const descSignals = [
|
|
1056
|
+
'sdk', 'client library', 'api client', 'api wrapper', 'official client',
|
|
1057
|
+
'library for the', 'library for', 'client for the', 'client for',
|
|
1058
|
+
].some(s => desc.includes(s)) && desc.includes('api');
|
|
1059
|
+
// Signal 3: Has library exports (main/exports). SDKs may also ship CLI
|
|
1060
|
+
// shims, so we don't exclude on bin presence.
|
|
1061
|
+
const hasLibraryExports = !!(pkg.main || pkg.exports || pkg.module);
|
|
1062
|
+
// Signal 4: Depends on HTTP clients
|
|
1063
|
+
const httpDeps = ['node-fetch', 'axios', 'got', 'undici', 'cross-fetch', 'ky', 'superagent']
|
|
1064
|
+
.some(d => d in allDeps);
|
|
1065
|
+
// Signal 5: Keywords include sdk/client
|
|
1066
|
+
const kwSignals = keywords.some(k => ['sdk', 'client', 'api-client', 'wrapper'].includes(k.toLowerCase()));
|
|
1067
|
+
// Signal 6: Description pattern "for the X API" -- strong signal that
|
|
1068
|
+
// this is an API client library
|
|
1069
|
+
const forApiPattern = /\bfor\s+the\s+\w+\s+api\b/i.test(desc) ||
|
|
1070
|
+
/\b(official|typescript|javascript|node)\s+\w*\s*(library|client|sdk)\b/i.test(desc);
|
|
1071
|
+
const signalCount = [
|
|
1072
|
+
nameSignals,
|
|
1073
|
+
descSignals,
|
|
1074
|
+
hasLibraryExports && httpDeps,
|
|
1075
|
+
kwSignals,
|
|
1076
|
+
forApiPattern && hasLibraryExports,
|
|
1077
|
+
].filter(Boolean).length;
|
|
1078
|
+
return signalCount >= 2;
|
|
1079
|
+
}
|
|
962
1080
|
/**
|
|
963
1081
|
* Check if a finding applies to the given project type
|
|
964
1082
|
*/
|