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.
Files changed (64) hide show
  1. package/dist/.integrity-manifest.json +1 -1
  2. package/dist/arp/crypto/hybrid-signing.d.ts +107 -0
  3. package/dist/arp/crypto/hybrid-signing.d.ts.map +1 -0
  4. package/dist/arp/crypto/hybrid-signing.js +321 -0
  5. package/dist/arp/crypto/hybrid-signing.js.map +1 -0
  6. package/dist/arp/crypto/index.d.ts +13 -0
  7. package/dist/arp/crypto/index.d.ts.map +1 -0
  8. package/dist/arp/crypto/index.js +33 -0
  9. package/dist/arp/crypto/index.js.map +1 -0
  10. package/dist/arp/crypto/manifest-loader.d.ts +117 -0
  11. package/dist/arp/crypto/manifest-loader.d.ts.map +1 -0
  12. package/dist/arp/crypto/manifest-loader.js +361 -0
  13. package/dist/arp/crypto/manifest-loader.js.map +1 -0
  14. package/dist/arp/crypto/types.d.ts +69 -0
  15. package/dist/arp/crypto/types.d.ts.map +1 -0
  16. package/dist/arp/crypto/types.js +11 -0
  17. package/dist/arp/crypto/types.js.map +1 -0
  18. package/dist/arp/index.d.ts +27 -0
  19. package/dist/arp/index.d.ts.map +1 -1
  20. package/dist/arp/index.js +94 -1
  21. package/dist/arp/index.js.map +1 -1
  22. package/dist/arp/intelligence/behavioral-risk-server.d.ts +82 -0
  23. package/dist/arp/intelligence/behavioral-risk-server.d.ts.map +1 -0
  24. package/dist/arp/intelligence/behavioral-risk-server.js +258 -0
  25. package/dist/arp/intelligence/behavioral-risk-server.js.map +1 -0
  26. package/dist/arp/intelligence/behavioral-risk.d.ts +217 -0
  27. package/dist/arp/intelligence/behavioral-risk.d.ts.map +1 -0
  28. package/dist/arp/intelligence/behavioral-risk.js +429 -0
  29. package/dist/arp/intelligence/behavioral-risk.js.map +1 -0
  30. package/dist/arp/intelligence/coordinator.d.ts +93 -2
  31. package/dist/arp/intelligence/coordinator.d.ts.map +1 -1
  32. package/dist/arp/intelligence/coordinator.js +281 -1
  33. package/dist/arp/intelligence/coordinator.js.map +1 -1
  34. package/dist/arp/intelligence/guard-anomaly.d.ts +349 -0
  35. package/dist/arp/intelligence/guard-anomaly.d.ts.map +1 -0
  36. package/dist/arp/intelligence/guard-anomaly.js +399 -0
  37. package/dist/arp/intelligence/guard-anomaly.js.map +1 -0
  38. package/dist/arp/intelligence/nanomind-l1.d.ts +37 -0
  39. package/dist/arp/intelligence/nanomind-l1.d.ts.map +1 -1
  40. package/dist/arp/intelligence/nanomind-l1.js +78 -0
  41. package/dist/arp/intelligence/nanomind-l1.js.map +1 -1
  42. package/dist/arp/intelligence/verify-classification.d.ts +124 -0
  43. package/dist/arp/intelligence/verify-classification.d.ts.map +1 -0
  44. package/dist/arp/intelligence/verify-classification.js +329 -0
  45. package/dist/arp/intelligence/verify-classification.js.map +1 -0
  46. package/dist/arp/proxy/server.d.ts +38 -8
  47. package/dist/arp/proxy/server.d.ts.map +1 -1
  48. package/dist/arp/proxy/server.js +89 -0
  49. package/dist/arp/proxy/server.js.map +1 -1
  50. package/dist/arp/types.d.ts +228 -1
  51. package/dist/arp/types.d.ts.map +1 -1
  52. package/dist/cli.js +85 -18
  53. package/dist/cli.js.map +1 -1
  54. package/dist/nanomind-core/compiler/semantic-compiler.d.ts.map +1 -1
  55. package/dist/nanomind-core/compiler/semantic-compiler.js +170 -10
  56. package/dist/nanomind-core/compiler/semantic-compiler.js.map +1 -1
  57. package/dist/nanomind-core/compiler/source-code-preprocessor.d.ts +64 -0
  58. package/dist/nanomind-core/compiler/source-code-preprocessor.d.ts.map +1 -0
  59. package/dist/nanomind-core/compiler/source-code-preprocessor.js +656 -0
  60. package/dist/nanomind-core/compiler/source-code-preprocessor.js.map +1 -0
  61. package/dist/nanomind-core/ingestion/artifact-parser.d.ts.map +1 -1
  62. package/dist/nanomind-core/ingestion/artifact-parser.js +15 -6
  63. package/dist/nanomind-core/ingestion/artifact-parser.js.map +1 -1
  64. 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: downloads and runs full security analysis (${CHECK_COUNT} checks + NanoMind)
175
- GitHub repo: shallow clones and runs full security analysis
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: ${CLI_PREFIX.replace(' scan', '')} check ${skill} --offline`)), 10000));
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
- console.log(`\n Full project scan: ${CLI_PREFIX} secure <dir>`);
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(` ${CLI_PREFIX} check ./${repo}/`);
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
- console.log(`\n Full project scan: ${CLI_PREFIX} secure <dir>`);
6297
- console.log();
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
- console.log(`\n Full project scan: ${CLI_PREFIX} secure <dir>`);
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(` ${CLI_PREFIX} check ./downloaded-dir/`);
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
- console.log(`\n Full project scan: ${CLI_PREFIX} secure <dir>`);
6595
- console.log();
6662
+ printCheckNextSteps(name);
6596
6663
  if (critical.length > 0 || high.length > 0)
6597
6664
  process.exit(1);
6598
6665
  }