hackmyagent 0.16.5 → 0.16.7
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/crypto/hybrid-signing.d.ts +107 -0
- package/dist/arp/crypto/hybrid-signing.d.ts.map +1 -0
- package/dist/arp/crypto/hybrid-signing.js +321 -0
- package/dist/arp/crypto/hybrid-signing.js.map +1 -0
- package/dist/arp/crypto/index.d.ts +13 -0
- package/dist/arp/crypto/index.d.ts.map +1 -0
- package/dist/arp/crypto/index.js +33 -0
- package/dist/arp/crypto/index.js.map +1 -0
- package/dist/arp/crypto/manifest-loader.d.ts +117 -0
- package/dist/arp/crypto/manifest-loader.d.ts.map +1 -0
- package/dist/arp/crypto/manifest-loader.js +361 -0
- package/dist/arp/crypto/manifest-loader.js.map +1 -0
- package/dist/arp/crypto/types.d.ts +69 -0
- package/dist/arp/crypto/types.d.ts.map +1 -0
- package/dist/arp/crypto/types.js +11 -0
- package/dist/arp/crypto/types.js.map +1 -0
- package/dist/arp/index.d.ts +27 -0
- package/dist/arp/index.d.ts.map +1 -1
- package/dist/arp/index.js +94 -1
- package/dist/arp/index.js.map +1 -1
- package/dist/arp/intelligence/behavioral-risk-server.d.ts +82 -0
- package/dist/arp/intelligence/behavioral-risk-server.d.ts.map +1 -0
- package/dist/arp/intelligence/behavioral-risk-server.js +258 -0
- package/dist/arp/intelligence/behavioral-risk-server.js.map +1 -0
- package/dist/arp/intelligence/behavioral-risk.d.ts +217 -0
- package/dist/arp/intelligence/behavioral-risk.d.ts.map +1 -0
- package/dist/arp/intelligence/behavioral-risk.js +429 -0
- package/dist/arp/intelligence/behavioral-risk.js.map +1 -0
- package/dist/arp/intelligence/coordinator.d.ts +93 -2
- package/dist/arp/intelligence/coordinator.d.ts.map +1 -1
- package/dist/arp/intelligence/coordinator.js +281 -1
- package/dist/arp/intelligence/coordinator.js.map +1 -1
- package/dist/arp/intelligence/guard-anomaly.d.ts +349 -0
- package/dist/arp/intelligence/guard-anomaly.d.ts.map +1 -0
- package/dist/arp/intelligence/guard-anomaly.js +399 -0
- package/dist/arp/intelligence/guard-anomaly.js.map +1 -0
- package/dist/arp/intelligence/nanomind-l1.d.ts +37 -0
- package/dist/arp/intelligence/nanomind-l1.d.ts.map +1 -1
- package/dist/arp/intelligence/nanomind-l1.js +78 -0
- package/dist/arp/intelligence/nanomind-l1.js.map +1 -1
- package/dist/arp/intelligence/verify-classification.d.ts +124 -0
- package/dist/arp/intelligence/verify-classification.d.ts.map +1 -0
- package/dist/arp/intelligence/verify-classification.js +329 -0
- package/dist/arp/intelligence/verify-classification.js.map +1 -0
- package/dist/arp/proxy/server.d.ts +38 -8
- package/dist/arp/proxy/server.d.ts.map +1 -1
- package/dist/arp/proxy/server.js +89 -0
- package/dist/arp/proxy/server.js.map +1 -1
- package/dist/arp/types.d.ts +228 -1
- package/dist/arp/types.d.ts.map +1 -1
- package/dist/cli.js +85 -18
- package/dist/cli.js.map +1 -1
- package/dist/nanomind-core/compiler/semantic-compiler.d.ts.map +1 -1
- package/dist/nanomind-core/compiler/semantic-compiler.js +170 -10
- package/dist/nanomind-core/compiler/semantic-compiler.js.map +1 -1
- package/dist/nanomind-core/compiler/source-code-preprocessor.d.ts +64 -0
- package/dist/nanomind-core/compiler/source-code-preprocessor.d.ts.map +1 -0
- package/dist/nanomind-core/compiler/source-code-preprocessor.js +656 -0
- package/dist/nanomind-core/compiler/source-code-preprocessor.js.map +1 -0
- package/dist/nanomind-core/ingestion/artifact-parser.d.ts.map +1 -1
- package/dist/nanomind-core/ingestion/artifact-parser.js +15 -6
- package/dist/nanomind-core/ingestion/artifact-parser.js.map +1 -1
- package/package.json +3 -1
package/dist/cli.js
CHANGED
|
@@ -47,6 +47,8 @@ program.showHelpAfterError('(run with --help for usage)');
|
|
|
47
47
|
// Total security check count across all scanner modules.
|
|
48
48
|
// Update when adding new checks (verify with: grep -r "checkId:" src/hardening/ | grep -o "checkId: '[^']*'" | sort -u | wc -l)
|
|
49
49
|
const CHECK_COUNT = 209;
|
|
50
|
+
// How long registry-cached scan data is considered fresh before `check` re-scans.
|
|
51
|
+
const STALE_SCAN_DAYS = 3;
|
|
50
52
|
// Write a string to stdout synchronously with retry for pipe backpressure.
|
|
51
53
|
// process.stdout.write() is async and gets truncated when process.exit()
|
|
52
54
|
// runs before the stream flushes. fs.writeFileSync(1, ...) can fail with
|
|
@@ -171,17 +173,21 @@ program
|
|
|
171
173
|
.description(`Check if a package, repo, or skill is safe
|
|
172
174
|
|
|
173
175
|
Accepts npm packages, GitHub repos, local paths, or skill identifiers:
|
|
174
|
-
• npm package:
|
|
175
|
-
•
|
|
176
|
+
• npm package: queries the registry; downloads + scans (${CHECK_COUNT} checks + NanoMind) if data is missing or stale (>${STALE_SCAN_DAYS}d)
|
|
177
|
+
• PyPI package: downloads + scans (${CHECK_COUNT} checks + NanoMind)
|
|
178
|
+
• GitHub repo: queries the registry; shallow clones + scans if data is missing or stale (>${STALE_SCAN_DAYS}d)
|
|
176
179
|
• Local path: runs NanoMind semantic analysis
|
|
177
180
|
• Skill identifier: verifies publisher, permissions, revocation
|
|
178
181
|
|
|
182
|
+
Use --rescan to skip the registry cache and force a fresh local scan.
|
|
183
|
+
|
|
179
184
|
Risk levels: low, medium, high, critical
|
|
180
185
|
Exit code 1 if high/critical risk detected.
|
|
181
186
|
|
|
182
187
|
Examples:
|
|
183
188
|
$ hackmyagent check express
|
|
184
189
|
$ hackmyagent check @modelcontextprotocol/server-filesystem
|
|
190
|
+
$ hackmyagent check @sentry/mcp-server --rescan
|
|
185
191
|
$ hackmyagent check modelcontextprotocol/servers
|
|
186
192
|
$ hackmyagent check https://github.com/punkpeye/awesome-mcp-servers
|
|
187
193
|
$ hackmyagent check ./my-agent/
|
|
@@ -195,6 +201,7 @@ Examples:
|
|
|
195
201
|
.option('-v, --verbose', 'Show detailed verification info')
|
|
196
202
|
.option('--json', 'Output as JSON (for scripting/CI)')
|
|
197
203
|
.option('--offline', 'Skip DNS verification (offline mode)')
|
|
204
|
+
.option('--rescan', 'Force a fresh local scan, bypassing cached registry data')
|
|
198
205
|
.action(async (skill, options) => {
|
|
199
206
|
try {
|
|
200
207
|
// Detect local file/directory paths - run NanoMind scan instead of registry lookup
|
|
@@ -245,6 +252,7 @@ Examples:
|
|
|
245
252
|
console.log(`\n ... and ${issues.length - 10} more`);
|
|
246
253
|
console.log();
|
|
247
254
|
}
|
|
255
|
+
printCheckNextSteps(resolved);
|
|
248
256
|
if (risk === 'critical' || risk === 'high')
|
|
249
257
|
process.exit(1);
|
|
250
258
|
return;
|
|
@@ -269,12 +277,17 @@ Examples:
|
|
|
269
277
|
await checkNpmPackage(skill, options);
|
|
270
278
|
return;
|
|
271
279
|
}
|
|
280
|
+
// --rescan only applies to targets that otherwise hit the registry cache.
|
|
281
|
+
// For skill identifiers we fall through to the registry lookup below.
|
|
282
|
+
if (options.rescan && !options.json) {
|
|
283
|
+
console.error(`Note: --rescan has no effect on skill identifiers; it applies to npm/PyPI/GitHub targets.`);
|
|
284
|
+
}
|
|
272
285
|
// Registry lookup path (non-local identifier) with 10s timeout
|
|
273
286
|
const checkPromise = (0, index_1.checkSkill)(skill, {
|
|
274
287
|
skipDnsVerification: options.offline,
|
|
275
288
|
});
|
|
276
289
|
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error(`Timed out verifying "${skill}" (10s). The publisher may not exist or DNS is unreachable.\n` +
|
|
277
|
-
`Try: ${
|
|
290
|
+
`Try: ${getCheckCommand()} ${skill} --offline`)), 10000));
|
|
278
291
|
const result = await Promise.race([checkPromise, timeoutPromise]);
|
|
279
292
|
if (options.json) {
|
|
280
293
|
writeJsonStdout(result);
|
|
@@ -5802,7 +5815,6 @@ function parseGitHubTarget(target) {
|
|
|
5802
5815
|
};
|
|
5803
5816
|
}
|
|
5804
5817
|
const REGISTRY_URL = 'https://api.oa2a.org';
|
|
5805
|
-
const STALE_SCAN_DAYS = 3;
|
|
5806
5818
|
// ============================================================================
|
|
5807
5819
|
// Scan counter + contribute preference (~/.hackmyagent/config.json)
|
|
5808
5820
|
// ============================================================================
|
|
@@ -5975,6 +5987,57 @@ function displayRegistryResult(data) {
|
|
|
5975
5987
|
const days = Math.floor((Date.now() - new Date(data.lastScannedAt).getTime()) / (1000 * 60 * 60 * 24));
|
|
5976
5988
|
console.log(` Scanned: ${days === 0 ? 'today' : days + ' day(s) ago'}`);
|
|
5977
5989
|
}
|
|
5990
|
+
printCheckNextSteps(data.name);
|
|
5991
|
+
}
|
|
5992
|
+
/**
|
|
5993
|
+
* Resolve the "run a check" command string for use in user-facing hints.
|
|
5994
|
+
*
|
|
5995
|
+
* Precedence:
|
|
5996
|
+
* 1. HMA_CHECK_COMMAND env var (full command string, e.g. "opena2a check")
|
|
5997
|
+
* 2. `${CLI_PREFIX} check` — sensible default derived from how HMA was
|
|
5998
|
+
* invoked.
|
|
5999
|
+
*
|
|
6000
|
+
* Parent CLIs should set HMA_CHECK_COMMAND when their verb layout differs
|
|
6001
|
+
* from hackmyagent's, rather than trying to encode the full verb into
|
|
6002
|
+
* HMA_CLI_PREFIX (which is treated as a binary-level prefix everywhere else).
|
|
6003
|
+
*/
|
|
6004
|
+
function getCheckCommand() {
|
|
6005
|
+
const override = process.env.HMA_CHECK_COMMAND?.trim();
|
|
6006
|
+
if (override)
|
|
6007
|
+
return override;
|
|
6008
|
+
return `${CLI_PREFIX} check`;
|
|
6009
|
+
}
|
|
6010
|
+
/**
|
|
6011
|
+
* Resolve the "full project scan" hint command string.
|
|
6012
|
+
*
|
|
6013
|
+
* Precedence:
|
|
6014
|
+
* 1. HMA_FULL_SCAN_HINT env var (full command string, e.g. "opena2a review")
|
|
6015
|
+
* 2. `${CLI_PREFIX} secure <dir>` — default.
|
|
6016
|
+
*/
|
|
6017
|
+
function getFullScanHint() {
|
|
6018
|
+
const override = process.env.HMA_FULL_SCAN_HINT?.trim();
|
|
6019
|
+
if (override)
|
|
6020
|
+
return override;
|
|
6021
|
+
return `${CLI_PREFIX} secure <dir>`;
|
|
6022
|
+
}
|
|
6023
|
+
/**
|
|
6024
|
+
* Print the standard 3-line next-steps footer shown after every `check`
|
|
6025
|
+
* invocation. Lines:
|
|
6026
|
+
* 1. How to force a fresh local scan of *this* target.
|
|
6027
|
+
* 2. How to run the full project scan (respects HMA_FULL_SCAN_HINT so that
|
|
6028
|
+
* sibling CLIs like opena2a can redirect users to their own flagship
|
|
6029
|
+
* command instead of `hackmyagent secure <dir>`).
|
|
6030
|
+
* 3. Discoverability: the other target syntaxes `check` accepts.
|
|
6031
|
+
*
|
|
6032
|
+
* Suppressed in --ci so machine-readable output stays clean.
|
|
6033
|
+
*/
|
|
6034
|
+
function printCheckNextSteps(target) {
|
|
6035
|
+
if (globalCiMode)
|
|
6036
|
+
return;
|
|
6037
|
+
console.log();
|
|
6038
|
+
console.log(` ${colors.dim}Run a fresh local scan: ${getCheckCommand()} ${target} --rescan${RESET()}`);
|
|
6039
|
+
console.log(` ${colors.dim}Full project scan: ${getFullScanHint()}${RESET()}`);
|
|
6040
|
+
console.log(` ${colors.dim}Also accepts: pip:<pkg> · <owner>/<repo> · ./<dir> · @publisher/skill${RESET()}`);
|
|
5978
6041
|
console.log();
|
|
5979
6042
|
}
|
|
5980
6043
|
/**
|
|
@@ -6043,8 +6106,8 @@ async function suggestSimilarPackages(name) {
|
|
|
6043
6106
|
async function checkGitHubRepo(target, options) {
|
|
6044
6107
|
const { org, repo, cloneUrl } = parseGitHubTarget(target);
|
|
6045
6108
|
const displayName = `${org}/${repo}`;
|
|
6046
|
-
// Step 1: Check registry for existing trust data
|
|
6047
|
-
if (!options.offline) {
|
|
6109
|
+
// Step 1: Check registry for existing trust data (unless --rescan forces a fresh scan)
|
|
6110
|
+
if (!options.offline && !options.rescan) {
|
|
6048
6111
|
const registryData = await queryRegistry(displayName);
|
|
6049
6112
|
if (registryData?.found && !isScanStale(registryData.lastScannedAt)) {
|
|
6050
6113
|
if (options.json) {
|
|
@@ -6061,6 +6124,9 @@ async function checkGitHubRepo(target, options) {
|
|
|
6061
6124
|
}
|
|
6062
6125
|
}
|
|
6063
6126
|
}
|
|
6127
|
+
else if (options.rescan && !options.json && !globalCiMode) {
|
|
6128
|
+
console.error(`Forcing fresh local scan (--rescan)...`);
|
|
6129
|
+
}
|
|
6064
6130
|
// Step 2: Clone and scan
|
|
6065
6131
|
const { mkdtemp, rm } = await Promise.resolve().then(() => __importStar(require('node:fs/promises')));
|
|
6066
6132
|
const { tmpdir } = await Promise.resolve().then(() => __importStar(require('node:os')));
|
|
@@ -6149,8 +6215,7 @@ async function checkGitHubRepo(target, options) {
|
|
|
6149
6215
|
queuePendingScan(displayName, result);
|
|
6150
6216
|
}
|
|
6151
6217
|
}
|
|
6152
|
-
|
|
6153
|
-
console.log();
|
|
6218
|
+
printCheckNextSteps(displayName);
|
|
6154
6219
|
if (critical.length > 0 || high.length > 0)
|
|
6155
6220
|
process.exit(1);
|
|
6156
6221
|
}
|
|
@@ -6164,7 +6229,7 @@ async function checkGitHubRepo(target, options) {
|
|
|
6164
6229
|
console.error(`Error: Cloning "${displayName}" timed out (120s). The repo may be too large.`);
|
|
6165
6230
|
console.error(`\nTry cloning manually and scanning the local path:`);
|
|
6166
6231
|
console.error(` git clone --depth 1 ${cloneUrl}`);
|
|
6167
|
-
console.error(` ${
|
|
6232
|
+
console.error(` ${getCheckCommand()} ./${repo}/`);
|
|
6168
6233
|
}
|
|
6169
6234
|
else {
|
|
6170
6235
|
console.error(`Error: ${message}`);
|
|
@@ -6293,8 +6358,9 @@ async function checkPyPiPackage(target, options) {
|
|
|
6293
6358
|
else {
|
|
6294
6359
|
console.log(`\n ${colors.green}No security issues found.${RESET()}`);
|
|
6295
6360
|
}
|
|
6296
|
-
|
|
6297
|
-
|
|
6361
|
+
// Pass the original target (with pip: / pypi: prefix preserved) so the
|
|
6362
|
+
// rescan hint stays runnable — `hackmyagent check requests` would try npm.
|
|
6363
|
+
printCheckNextSteps(target);
|
|
6298
6364
|
if (critical.length > 0 || high.length > 0)
|
|
6299
6365
|
process.exit(1);
|
|
6300
6366
|
}
|
|
@@ -6453,8 +6519,7 @@ async function checkRawUrl(url, options) {
|
|
|
6453
6519
|
queuePendingScan(displayName, result);
|
|
6454
6520
|
}
|
|
6455
6521
|
}
|
|
6456
|
-
|
|
6457
|
-
console.log();
|
|
6522
|
+
printCheckNextSteps(displayName);
|
|
6458
6523
|
if (critical.length > 0 || high.length > 0)
|
|
6459
6524
|
process.exit(1);
|
|
6460
6525
|
}
|
|
@@ -6467,7 +6532,7 @@ async function checkRawUrl(url, options) {
|
|
|
6467
6532
|
else if (message.includes('timeout') || message.includes('Timeout')) {
|
|
6468
6533
|
console.error(`Error: Fetching "${url}" timed out. The target may be too large.`);
|
|
6469
6534
|
console.error(`\nTry downloading manually and scanning the local path:`);
|
|
6470
|
-
console.error(` ${
|
|
6535
|
+
console.error(` ${getCheckCommand()} ./downloaded-dir/`);
|
|
6471
6536
|
}
|
|
6472
6537
|
else {
|
|
6473
6538
|
console.error(`Error scanning URL: ${message}`);
|
|
@@ -6479,8 +6544,8 @@ async function checkRawUrl(url, options) {
|
|
|
6479
6544
|
}
|
|
6480
6545
|
}
|
|
6481
6546
|
async function checkNpmPackage(name, options) {
|
|
6482
|
-
// Step 1: Check registry for existing trust data
|
|
6483
|
-
if (!options.offline) {
|
|
6547
|
+
// Step 1: Check registry for existing trust data (unless --rescan forces a fresh scan)
|
|
6548
|
+
if (!options.offline && !options.rescan) {
|
|
6484
6549
|
const registryData = await queryRegistry(name);
|
|
6485
6550
|
if (registryData?.found && !isScanStale(registryData.lastScannedAt)) {
|
|
6486
6551
|
// Fresh data in registry — show it
|
|
@@ -6499,6 +6564,9 @@ async function checkNpmPackage(name, options) {
|
|
|
6499
6564
|
}
|
|
6500
6565
|
}
|
|
6501
6566
|
}
|
|
6567
|
+
else if (options.rescan && !options.json && !globalCiMode) {
|
|
6568
|
+
console.error(`Forcing fresh local scan (--rescan)...`);
|
|
6569
|
+
}
|
|
6502
6570
|
// Step 2: Download and scan
|
|
6503
6571
|
const { mkdtemp, rm } = await Promise.resolve().then(() => __importStar(require('node:fs/promises')));
|
|
6504
6572
|
const { tmpdir } = await Promise.resolve().then(() => __importStar(require('node:os')));
|
|
@@ -6591,8 +6659,7 @@ async function checkNpmPackage(name, options) {
|
|
|
6591
6659
|
queuePendingScan(name, result);
|
|
6592
6660
|
}
|
|
6593
6661
|
}
|
|
6594
|
-
|
|
6595
|
-
console.log();
|
|
6662
|
+
printCheckNextSteps(name);
|
|
6596
6663
|
if (critical.length > 0 || high.length > 0)
|
|
6597
6664
|
process.exit(1);
|
|
6598
6665
|
}
|