hackmyagent 0.16.0 → 0.16.2

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 (49) hide show
  1. package/dist/.integrity-manifest.json +1 -1
  2. package/dist/arp/intelligence/nanomind-l1.d.ts +30 -0
  3. package/dist/arp/intelligence/nanomind-l1.d.ts.map +1 -1
  4. package/dist/arp/intelligence/nanomind-l1.js +115 -0
  5. package/dist/arp/intelligence/nanomind-l1.js.map +1 -1
  6. package/dist/cli.js +220 -19
  7. package/dist/cli.js.map +1 -1
  8. package/dist/hardening/scanner.d.ts.map +1 -1
  9. package/dist/hardening/scanner.js +148 -7
  10. package/dist/hardening/scanner.js.map +1 -1
  11. package/dist/hardening/taxonomy.d.ts +2 -0
  12. package/dist/hardening/taxonomy.d.ts.map +1 -1
  13. package/dist/hardening/taxonomy.js +5 -0
  14. package/dist/hardening/taxonomy.js.map +1 -1
  15. package/dist/nanomind-core/analyzers/credential-analyzer.js +12 -3
  16. package/dist/nanomind-core/analyzers/credential-analyzer.js.map +1 -1
  17. package/dist/nanomind-core/analyzers/stego-analyzer.d.ts +30 -0
  18. package/dist/nanomind-core/analyzers/stego-analyzer.d.ts.map +1 -0
  19. package/dist/nanomind-core/analyzers/stego-analyzer.js +533 -0
  20. package/dist/nanomind-core/analyzers/stego-analyzer.js.map +1 -0
  21. package/dist/nanomind-core/daemon-lifecycle.d.ts +28 -0
  22. package/dist/nanomind-core/daemon-lifecycle.d.ts.map +1 -0
  23. package/dist/nanomind-core/daemon-lifecycle.js +142 -0
  24. package/dist/nanomind-core/daemon-lifecycle.js.map +1 -0
  25. package/dist/nanomind-core/inference/tme-classifier.d.ts +3 -2
  26. package/dist/nanomind-core/inference/tme-classifier.d.ts.map +1 -1
  27. package/dist/nanomind-core/inference/tme-classifier.js +26 -16
  28. package/dist/nanomind-core/inference/tme-classifier.js.map +1 -1
  29. package/dist/nanomind-core/orchestrate.d.ts.map +1 -1
  30. package/dist/nanomind-core/orchestrate.js +11 -1
  31. package/dist/nanomind-core/orchestrate.js.map +1 -1
  32. package/dist/nanomind-core/scanner-bridge.d.ts.map +1 -1
  33. package/dist/nanomind-core/scanner-bridge.js +6 -0
  34. package/dist/nanomind-core/scanner-bridge.js.map +1 -1
  35. package/dist/plugins/credvault.d.ts.map +1 -1
  36. package/dist/plugins/credvault.js +25 -0
  37. package/dist/plugins/credvault.js.map +1 -1
  38. package/dist/semantic/nanomind-enhancer.d.ts.map +1 -1
  39. package/dist/semantic/nanomind-enhancer.js +206 -0
  40. package/dist/semantic/nanomind-enhancer.js.map +1 -1
  41. package/dist/telemetry/nanomind-feedback.d.ts +43 -0
  42. package/dist/telemetry/nanomind-feedback.d.ts.map +1 -0
  43. package/dist/telemetry/nanomind-feedback.js +104 -0
  44. package/dist/telemetry/nanomind-feedback.js.map +1 -0
  45. package/dist/telemetry/nanomind-telemetry.d.ts +48 -0
  46. package/dist/telemetry/nanomind-telemetry.d.ts.map +1 -0
  47. package/dist/telemetry/nanomind-telemetry.js +123 -0
  48. package/dist/telemetry/nanomind-telemetry.js.map +1 -0
  49. package/package.json +1 -1
@@ -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;AAoID,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;YA+ZvC,cAAc;IAwE5B;;OAEG;YACW,iBAAiB;IA+F/B;;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;IAmBD;;OAEG;YACW,YAAY;IAmE1B;;OAEG;IACG,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6DhD;;;OAGG;YACW,cAAc;IAgD5B;;OAEG;YACW,mBAAmB;IA6pBjC;;;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;IA0WvC;;;;;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"}
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/hardening/scanner.ts"],"names":[],"mappings":"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"}
@@ -172,7 +172,9 @@ const SKILL_CREDENTIAL_ACCESS_PATTERNS = [
172
172
  /wallet.*\.json/gi,
173
173
  /seed.*phrase/gi,
174
174
  /private.*key/gi,
175
- /\.env/gi,
175
+ // Match .env as a standalone file reference, not as part of process.env or documentation
176
+ // like ".env.example in sync" or "set in .env.local"
177
+ /(?:^|[\s"'`(])\.env(?:\.local|\.production|\.development)?(?:[\s"'`)]|$)/gi,
176
178
  /credentials\.json/gi,
177
179
  ];
178
180
  const SKILL_EXFILTRATION_PATTERNS = [
@@ -246,6 +248,101 @@ function shellEscape(s) {
246
248
  // Wrap in single quotes and escape embedded single quotes: ' -> '\''
247
249
  return "'" + s.replace(/'/g, "'\\''") + "'";
248
250
  }
251
+ /**
252
+ * Check if a variation selector at position i in rawBuffer is a legitimate
253
+ * emoji presentation selector (U+FE0F following an emoji base character).
254
+ *
255
+ * Emoji base characters that commonly precede FE0F:
256
+ * - Keycap digits/symbols: 0-9, #, * (encoded as single ASCII bytes)
257
+ * - BMP symbols: U+2600-27BF range (encoded as 3-byte UTF-8: E2 XX XX or E2 XX XX)
258
+ * - SMP emoji: U+1F300-1FAFF (encoded as 4-byte UTF-8: F0 9F XX XX)
259
+ */
260
+ function isEmojiVariationSelector(buf, vsStart) {
261
+ // Walk backward to find the preceding character
262
+ // The variation selector is at vsStart (3 bytes: EF B8 8F)
263
+ // We need to check what character precedes it
264
+ if (vsStart === 0)
265
+ return false;
266
+ // Check for 4-byte SMP emoji before (F0 9F XX XX) — most common case
267
+ if (vsStart >= 4) {
268
+ const b0 = buf[vsStart - 4];
269
+ const b1 = buf[vsStart - 3];
270
+ if (b0 === 0xF0 && b1 === 0x9F)
271
+ return true; // U+1F000-1FFFF (emoji range)
272
+ }
273
+ // Check for 3-byte BMP symbol before (E2 XX XX) — symbols like warning, gear, etc.
274
+ if (vsStart >= 3) {
275
+ const b0 = buf[vsStart - 3];
276
+ const b1 = buf[vsStart - 2];
277
+ if (b0 === 0xE2) {
278
+ // U+2600-27BF: Misc Symbols, Dingbats (E2 98 80 through E2 9E BF)
279
+ if (b1 >= 0x98 && b1 <= 0x9E)
280
+ return true;
281
+ // U+2300-23FF: Misc Technical (E2 8C 80 through E2 8F BF) — includes hourglass, etc.
282
+ if (b1 >= 0x8C && b1 <= 0x8F)
283
+ return true;
284
+ }
285
+ // U+2700-27BF also encoded as E2 9C XX - E2 9E XX
286
+ if (b0 === 0xE2 && b1 >= 0x9C && b1 <= 0x9E)
287
+ return true;
288
+ }
289
+ // Check for 1-byte ASCII keycap base: #, *, 0-9
290
+ if (vsStart >= 1) {
291
+ const prev = buf[vsStart - 1];
292
+ if (prev === 0x23 || prev === 0x2A)
293
+ return true; // # or *
294
+ if (prev >= 0x30 && prev <= 0x39)
295
+ return true; // 0-9
296
+ }
297
+ return false;
298
+ }
299
+ /**
300
+ * Check if a Cyrillic character at position ci in chars[] is in a Cyrillic
301
+ * text context (legitimate i18n) rather than mixed into a Latin word (attack).
302
+ *
303
+ * Looks at a window of nearby characters. If the neighborhood contains
304
+ * mostly Cyrillic or other non-Latin chars, it's i18n. If surrounded by
305
+ * Latin chars, it's a homoglyph attack.
306
+ */
307
+ function isCyrillicInCyrillicContext(chars, ci) {
308
+ // Look at a window of 10 chars in each direction
309
+ const windowSize = 10;
310
+ const start = Math.max(0, ci - windowSize);
311
+ const end = Math.min(chars.length, ci + windowSize + 1);
312
+ let latinCount = 0;
313
+ let cyrillicCount = 0;
314
+ for (let j = start; j < end; j++) {
315
+ if (j === ci)
316
+ continue;
317
+ const cp = chars[j].codePointAt(0);
318
+ // Latin letter
319
+ if ((cp >= 0x41 && cp <= 0x5A) || (cp >= 0x61 && cp <= 0x7A)) {
320
+ latinCount++;
321
+ }
322
+ // Any Cyrillic (U+0400-052F)
323
+ if (cp >= 0x0400 && cp <= 0x052F) {
324
+ cyrillicCount++;
325
+ }
326
+ }
327
+ // If there are at least 3 other Cyrillic chars nearby, this is i18n text
328
+ // (translations, i18n badges, etc. always have multiple Cyrillic chars together)
329
+ if (cyrillicCount >= 3)
330
+ return true;
331
+ // If the immediate neighbors are both Latin, this is a homoglyph attack
332
+ const prevLatin = ci > 0 && (() => {
333
+ const cp = chars[ci - 1].codePointAt(0);
334
+ return (cp >= 0x41 && cp <= 0x5A) || (cp >= 0x61 && cp <= 0x7A);
335
+ })();
336
+ const nextLatin = ci < chars.length - 1 && (() => {
337
+ const cp = chars[ci + 1].codePointAt(0);
338
+ return (cp >= 0x41 && cp <= 0x5A) || (cp >= 0x61 && cp <= 0x7A);
339
+ })();
340
+ if (prevLatin && nextLatin)
341
+ return false; // Sandwiched in Latin = attack
342
+ // Ambiguous case: not enough context. If there are ANY other Cyrillic
343
+ // chars nearby, give benefit of the doubt (i18n).
344
+ return cyrillicCount > 0;
345
+ }
249
346
  class HardeningScanner {
250
347
  constructor() {
251
348
  this.cliName = 'hackmyagent';
@@ -4330,17 +4427,35 @@ dist/
4330
4427
  await fs.writeFile(skillFile, content);
4331
4428
  }
4332
4429
  // SKILL-005: Credential File Access
4430
+ // Only flag as CRITICAL inside frontmatter (capabilities section).
4431
+ // Body text often describes credential handling in documentation,
4432
+ // which is informational, not an actual access pattern.
4433
+ let inSkill005Frontmatter = false;
4434
+ let skill005FrontmatterDelimiters = 0;
4333
4435
  for (let i = 0; i < lines.length; i++) {
4436
+ const trimmed = lines[i].trim();
4437
+ if (trimmed === '---') {
4438
+ skill005FrontmatterDelimiters++;
4439
+ inSkill005Frontmatter = skill005FrontmatterDelimiters === 1;
4440
+ if (skill005FrontmatterDelimiters >= 2)
4441
+ inSkill005Frontmatter = false;
4442
+ continue;
4443
+ }
4334
4444
  const line = lines[i];
4335
4445
  for (const pattern of SKILL_CREDENTIAL_ACCESS_PATTERNS) {
4336
4446
  pattern.lastIndex = 0;
4337
4447
  if (pattern.test(line)) {
4448
+ // Frontmatter = actual capability declaration (CRITICAL)
4449
+ // Body = still suspicious but lower severity (MEDIUM)
4450
+ const severity = inSkill005Frontmatter ? 'critical' : 'medium';
4338
4451
  findings.push({
4339
4452
  checkId: 'SKILL-005',
4340
4453
  name: 'Credential File Access',
4341
- description: 'Skill attempts to access credential or sensitive configuration files',
4454
+ description: inSkill005Frontmatter
4455
+ ? 'Skill declares access to credential or sensitive configuration files'
4456
+ : 'Skill body mentions credential file patterns',
4342
4457
  category: 'skill',
4343
- severity: 'critical',
4458
+ severity,
4344
4459
  passed: false,
4345
4460
  message: `Credential file access pattern detected: "${line.trim().substring(0, 80)}..."`,
4346
4461
  file: relativePath,
@@ -6872,13 +6987,19 @@ dist/
6872
6987
  currentLine++;
6873
6988
  continue;
6874
6989
  }
6875
- // Variation selectors: EF B8 80-8F
6990
+ // Variation selectors: EF B8 80-8F (U+FE00-FE0F)
6991
+ // Skip U+FE0F (EF B8 8F) when preceded by an emoji base character,
6992
+ // as it's the standard emoji presentation selector (not steganography).
6876
6993
  if (rawBuffer[i] === 0xEF &&
6877
6994
  i + 2 < rawBuffer.length &&
6878
6995
  rawBuffer[i + 1] === 0xB8 &&
6879
6996
  rawBuffer[i + 2] >= 0x80 &&
6880
6997
  rawBuffer[i + 2] <= 0x8F) {
6881
- if (!hasVariationSelectors) {
6998
+ // Check if this is an emoji presentation selector (FE0F after emoji base)
6999
+ if (rawBuffer[i + 2] === 0x8F && isEmojiVariationSelector(rawBuffer, i)) {
7000
+ // Legitimate emoji — skip
7001
+ }
7002
+ else if (!hasVariationSelectors) {
6882
7003
  hasVariationSelectors = true;
6883
7004
  variationSelectorLine = currentLine;
6884
7005
  }
@@ -7129,17 +7250,37 @@ dist/
7129
7250
  let homoglyphChar = '';
7130
7251
  const contentForHomoglyph = content || rawBuffer.toString('utf-8');
7131
7252
  const homoglyphLines = contentForHomoglyph.split('\n');
7253
+ // Track markdown code fences: homoglyphs inside ```...``` blocks in .md files
7254
+ // are documentation examples, not executable code — skip them.
7255
+ const isMarkdown = relativePath.endsWith('.md') || relativePath.endsWith('.txt');
7256
+ let inCodeFence = false;
7132
7257
  for (let lineIdx = 0; lineIdx < homoglyphLines.length; lineIdx++) {
7133
7258
  const line = homoglyphLines[lineIdx];
7134
7259
  if (line.length > MAX_LINE_LENGTH)
7135
7260
  continue;
7261
+ // Track code fence boundaries in markdown files
7262
+ if (isMarkdown && line.trimStart().startsWith('```')) {
7263
+ inCodeFence = !inCodeFence;
7264
+ continue;
7265
+ }
7266
+ // Skip lines inside markdown code fences (documentation examples)
7267
+ if (isMarkdown && inCodeFence)
7268
+ continue;
7136
7269
  // Skip comment lines
7137
7270
  const trimmed = line.trimStart();
7138
7271
  if (trimmed.startsWith('//') || trimmed.startsWith('#') || trimmed.startsWith('*'))
7139
7272
  continue;
7140
- for (const ch of line) {
7141
- const cp = ch.codePointAt(0);
7273
+ const chars = [...line];
7274
+ for (let ci = 0; ci < chars.length; ci++) {
7275
+ const cp = chars[ci].codePointAt(0);
7142
7276
  if (homoglyphCodepoints.has(cp)) {
7277
+ // Check if this Cyrillic char is in a Cyrillic text block (i18n)
7278
+ // vs mixed into a Latin word (homoglyph attack).
7279
+ // Look at neighboring characters: if surrounded by other Cyrillic
7280
+ // or non-Latin chars, it's legitimate i18n text.
7281
+ if (isCyrillicInCyrillicContext(chars, ci)) {
7282
+ continue; // Legitimate i18n — skip
7283
+ }
7143
7284
  homoglyphFound = true;
7144
7285
  homoglyphLine = lineIdx + 1;
7145
7286
  homoglyphChar = `U+${cp.toString(16).toUpperCase().padStart(4, '0')}`;