hackmyagent 0.11.1 → 0.11.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.
@@ -1 +1 @@
1
- {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/hardening/scanner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,UAAU,EAA0C,MAAM,kBAAkB,CAAC;AAkF3F,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,oDAAoD;IACpD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA8HD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,OAAO,CAAiB;IAEhC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAiBlC;IAEF;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAMvB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;YAmSvC,cAAc;IAwE5B;;OAEG;YACW,iBAAiB;IA+F/B;;OAEG;IACH,OAAO,CAAC,gBAAgB;YAeV,uBAAuB;YAmGvB,aAAa;YAgDb,cAAc;YA+Fd,oBAAoB;YAwDpB,gBAAgB;YA0IhB,oBAAoB;YAgFpB,gBAAgB;YA2IhB,mBAAmB;YA4EnB,iBAAiB;YAyCjB,iBAAiB;YA+DjB,wBAAwB;YA0FxB,wBAAwB;YAmExB,wBAAwB;YAqHxB,oBAAoB;YA+GpB,uBAAuB;YAwIvB,iBAAiB;YA8GjB,oBAAoB;YAsHpB,mBAAmB;YAiGnB,gBAAgB;YAmIhB,oBAAoB;YAoIpB,gBAAgB;YAyHhB,qBAAqB;YA+GrB,eAAe;IAiI7B;;OAEG;YACW,mBAAmB;IA8GjC;;OAEG;YACW,oBAAoB;IAiKlC;;OAEG;YACW,iBAAiB;IA4I/B;;OAEG;YACW,oBAAoB;IAwIlC;;OAEG;YACW,eAAe;IAqJ7B;;OAEG;YACW,eAAe;IAuI7B;;OAEG;YACW,eAAe;IAyG7B;;OAEG;YACW,mBAAmB;IAmHjC,OAAO,CAAC,cAAc;IAsBtB;;OAEG;YACW,YAAY;IAkD1B;;OAEG;IACG,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6DhD;;;OAGG;YACW,cAAc;IAgD5B;;OAEG;YACW,mBAAmB;IAycjC;;;OAGG;YACW,kBAAkB;IAgDhC;;OAEG;YACW,sBAAsB;IA2LpC;;OAEG;YACW,sBAAsB;IA+BpC;;OAEG;YACW,oBAAoB;IAqVlC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA4B3B;;OAEG;YACW,iBAAiB;IA8D/B;;OAEG;YACW,mBAAmB;IA6VjC;;OAEG;YACW,wBAAwB;IA4OtC;;OAEG;YACW,gBAAgB;IA6J9B;;;OAGG;YACW,eAAe;IAoD7B;;;OAGG;YACW,aAAa;IAwC3B;;;OAGG;YACW,oBAAoB;IA+JlC;;;OAGG;YACW,iBAAiB;IA6H/B;;;OAGG;YACW,kBAAkB;IA+EhC;;;OAGG;YACW,aAAa;IAuF3B;;OAEG;YACW,gBAAgB;IA+D9B;;;;OAIG;YACW,yBAAyB;CA4NxC"}
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/hardening/scanner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,UAAU,EAA0C,MAAM,kBAAkB,CAAC;AAkF3F,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,oDAAoD;IACpD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA8HD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,OAAO,CAAiB;IAEhC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAiBlC;IAEF;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAMvB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;YAmSvC,cAAc;IAwE5B;;OAEG;YACW,iBAAiB;IA+F/B;;OAEG;IACH,OAAO,CAAC,gBAAgB;YAeV,uBAAuB;YAmGvB,aAAa;YAgDb,cAAc;YA+Fd,oBAAoB;YAwDpB,gBAAgB;YA0IhB,oBAAoB;YAgFpB,gBAAgB;YA2IhB,mBAAmB;YA4EnB,iBAAiB;YAyCjB,iBAAiB;YA+DjB,wBAAwB;YA0FxB,wBAAwB;YAmExB,wBAAwB;YAqHxB,oBAAoB;YA+GpB,uBAAuB;YAwIvB,iBAAiB;YA8GjB,oBAAoB;YAsHpB,mBAAmB;YAiGnB,gBAAgB;YAmIhB,oBAAoB;YAoIpB,gBAAgB;YAyHhB,qBAAqB;YA+GrB,eAAe;IAiI7B;;OAEG;YACW,mBAAmB;IA8GjC;;OAEG;YACW,oBAAoB;IAiKlC;;OAEG;YACW,iBAAiB;IA4I/B;;OAEG;YACW,oBAAoB;IAwIlC;;OAEG;YACW,eAAe;IAqJ7B;;OAEG;YACW,eAAe;IAuI7B;;OAEG;YACW,eAAe;IAyG7B;;OAEG;YACW,mBAAmB;IAmHjC,OAAO,CAAC,cAAc;IAsBtB;;OAEG;YACW,YAAY;IAkD1B;;OAEG;IACG,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6DhD;;;OAGG;YACW,cAAc;IAgD5B;;OAEG;YACW,mBAAmB;IAycjC;;;OAGG;YACW,kBAAkB;IAgDhC;;OAEG;YACW,sBAAsB;IA2LpC;;OAEG;YACW,sBAAsB;IA+BpC;;OAEG;YACW,oBAAoB;IAqVlC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA4B3B;;OAEG;YACW,iBAAiB;IA8D/B;;OAEG;YACW,mBAAmB;IA6VjC;;OAEG;YACW,wBAAwB;IA4OtC;;OAEG;YACW,gBAAgB;IA6J9B;;;OAGG;YACW,eAAe;IAoD7B;;;OAGG;YACW,aAAa;IAwC3B;;;OAGG;YACW,oBAAoB;IA+JlC;;;OAGG;YACW,iBAAiB;IA6H/B;;;OAGG;YACW,kBAAkB;IA+EhC;;;OAGG;YACW,aAAa;IAuF3B;;OAEG;YACW,gBAAgB;IA+D9B;;;;OAIG;YACW,yBAAyB;CAuWxC"}
@@ -6224,7 +6224,12 @@ dist/
6224
6224
  */
6225
6225
  async checkUnicodeSteganography(targetDir, _autoFix) {
6226
6226
  const findings = [];
6227
- const sourceFiles = await this.findSourceFiles(targetDir, targetDir);
6227
+ // Scan a broad set of file types for unicode stego -- not just JS/TS
6228
+ const stegoExtensions = [
6229
+ '.js', '.ts', '.mjs', '.cjs', '.tsx', '.jsx',
6230
+ '.py', '.md', '.txt', '.yaml', '.yml', '.json', '.toml',
6231
+ ];
6232
+ const sourceFiles = await this.walkDirectory(targetDir, stegoExtensions);
6228
6233
  for (const filePath of sourceFiles) {
6229
6234
  const relativePath = path.relative(targetDir, filePath);
6230
6235
  let rawBuffer;
@@ -6238,18 +6243,69 @@ dist/
6238
6243
  if (rawBuffer.length > MAX_FILE_SIZE)
6239
6244
  continue;
6240
6245
  // UNICODE-STEGO-001: Invisible Codepoint Detection
6241
- // Scan for variation selectors U+FE00-FE0F (UTF-8: EF B8 80-8F)
6242
- // and tag characters U+E0100-E01EF (UTF-8: F3 A0 84 80 - F3 A0 87 AF)
6246
+ // Scan for:
6247
+ // - Zero-width characters: U+200B (ZWSP), U+200C (ZWNJ), U+200D (ZWJ), U+FEFF (BOM mid-file)
6248
+ // - Variation selectors U+FE00-FE0F
6249
+ // - Tag characters U+E0100-E01EF
6250
+ // - Bidirectional override chars U+202A-U+202E and U+2066-U+2069
6243
6251
  let hasVariationSelectors = false;
6244
6252
  let variationSelectorLine = 1;
6245
6253
  let hasTagCharsIn001 = false;
6246
6254
  let tagCharLine001 = 1;
6255
+ let hasZeroWidth = false;
6256
+ let zeroWidthLine = 1;
6257
+ let hasBidiOverride = false;
6258
+ let bidiOverrideLine = 1;
6247
6259
  let currentLine = 1;
6248
6260
  for (let i = 0; i < rawBuffer.length; i++) {
6249
6261
  if (rawBuffer[i] === 0x0A) {
6250
6262
  currentLine++;
6251
6263
  continue;
6252
6264
  }
6265
+ // Zero-width characters: E2 80 8B (ZWSP), E2 80 8C (ZWNJ), E2 80 8D (ZWJ)
6266
+ if (rawBuffer[i] === 0xE2 &&
6267
+ i + 2 < rawBuffer.length &&
6268
+ rawBuffer[i + 1] === 0x80 &&
6269
+ rawBuffer[i + 2] >= 0x8B &&
6270
+ rawBuffer[i + 2] <= 0x8D) {
6271
+ if (!hasZeroWidth) {
6272
+ hasZeroWidth = true;
6273
+ zeroWidthLine = currentLine;
6274
+ }
6275
+ }
6276
+ // BOM / Zero-width no-break space U+FEFF: EF BB BF (only suspicious mid-file)
6277
+ if (rawBuffer[i] === 0xEF &&
6278
+ i + 2 < rawBuffer.length &&
6279
+ rawBuffer[i + 1] === 0xBB &&
6280
+ rawBuffer[i + 2] === 0xBF &&
6281
+ i > 0) {
6282
+ if (!hasZeroWidth) {
6283
+ hasZeroWidth = true;
6284
+ zeroWidthLine = currentLine;
6285
+ }
6286
+ }
6287
+ // Bidirectional override chars U+202A-U+202E: E2 80 AA-AE
6288
+ if (rawBuffer[i] === 0xE2 &&
6289
+ i + 2 < rawBuffer.length &&
6290
+ rawBuffer[i + 1] === 0x80 &&
6291
+ rawBuffer[i + 2] >= 0xAA &&
6292
+ rawBuffer[i + 2] <= 0xAE) {
6293
+ if (!hasBidiOverride) {
6294
+ hasBidiOverride = true;
6295
+ bidiOverrideLine = currentLine;
6296
+ }
6297
+ }
6298
+ // Bidirectional isolate chars U+2066-U+2069: E2 81 A6-A9
6299
+ if (rawBuffer[i] === 0xE2 &&
6300
+ i + 2 < rawBuffer.length &&
6301
+ rawBuffer[i + 1] === 0x81 &&
6302
+ rawBuffer[i + 2] >= 0xA6 &&
6303
+ rawBuffer[i + 2] <= 0xA9) {
6304
+ if (!hasBidiOverride) {
6305
+ hasBidiOverride = true;
6306
+ bidiOverrideLine = currentLine;
6307
+ }
6308
+ }
6253
6309
  // Variation selectors: EF B8 80-8F
6254
6310
  if (rawBuffer[i] === 0xEF &&
6255
6311
  i + 2 < rawBuffer.length &&
@@ -6273,24 +6329,39 @@ dist/
6273
6329
  }
6274
6330
  }
6275
6331
  }
6276
- if (hasVariationSelectors || hasTagCharsIn001) {
6332
+ if (hasZeroWidth || hasVariationSelectors || hasTagCharsIn001 || hasBidiOverride) {
6277
6333
  const detectedTypes = [];
6334
+ if (hasZeroWidth)
6335
+ detectedTypes.push('zero-width characters (U+200B-U+200D, U+FEFF)');
6336
+ if (hasBidiOverride)
6337
+ detectedTypes.push('bidirectional overrides (U+202A-U+202E, U+2066-U+2069)');
6278
6338
  if (hasVariationSelectors)
6279
6339
  detectedTypes.push('variation selectors (U+FE00-FE0F)');
6280
6340
  if (hasTagCharsIn001)
6281
6341
  detectedTypes.push('tag characters (U+E0100-E01EF)');
6342
+ const lineNumbers = [];
6343
+ if (hasZeroWidth)
6344
+ lineNumbers.push(zeroWidthLine);
6345
+ if (hasBidiOverride)
6346
+ lineNumbers.push(bidiOverrideLine);
6347
+ if (hasVariationSelectors)
6348
+ lineNumbers.push(variationSelectorLine);
6349
+ if (hasTagCharsIn001)
6350
+ lineNumbers.push(tagCharLine001);
6351
+ const earliestLine = Math.min(...lineNumbers);
6352
+ const severity = hasBidiOverride || hasVariationSelectors || hasTagCharsIn001 ? 'critical' : 'high';
6282
6353
  findings.push({
6283
6354
  checkId: 'UNICODE-STEGO-001',
6284
6355
  name: 'Invisible Unicode Codepoints Detected',
6285
- description: 'Source file contains invisible Unicode codepoints that can hide malicious payloads (GlassWorm attack vector)',
6286
- category: 'unicode-stego',
6287
- severity: 'critical',
6356
+ description: 'Source file contains invisible Unicode codepoints that can hide malicious payloads (zero-width characters, bidirectional overrides, variation selectors, or tag characters)',
6357
+ category: 'supply-chain',
6358
+ severity,
6288
6359
  passed: false,
6289
6360
  message: `Found ${detectedTypes.join(' and ')} in ${relativePath}`,
6290
6361
  file: relativePath,
6291
- line: hasVariationSelectors ? variationSelectorLine : tagCharLine001,
6362
+ line: earliestLine,
6292
6363
  fixable: false,
6293
- fix: 'Inspect the file with a hex editor (e.g., xxd) to identify and remove invisible Unicode codepoints. Run: xxd ' + relativePath + ' | grep -E "fe0[0-9a-f]|f3a0"',
6364
+ fix: 'Inspect the file with a hex editor (e.g., xxd) to identify and remove invisible Unicode codepoints. Run: xxd ' + relativePath + ' | grep -iE "e280 8[bcd]|efbb bf|e280 a[a-e]|e281 a[6-9]|efb8 8|f3a0"',
6294
6365
  });
6295
6366
  }
6296
6367
  // UNICODE-STEGO-002: GlassWorm Decoder Pattern
@@ -6319,7 +6390,7 @@ dist/
6319
6390
  checkId: 'UNICODE-STEGO-002',
6320
6391
  name: 'GlassWorm Decoder Pattern Detected',
6321
6392
  description: 'Source file contains .codePointAt() usage combined with Unicode variation selector or tag character hex literals - this is the decoder half of a GlassWorm attack',
6322
- category: 'unicode-stego',
6393
+ category: 'supply-chain',
6323
6394
  severity: 'critical',
6324
6395
  passed: false,
6325
6396
  message: `Found GlassWorm decoder pattern (.codePointAt + hex range literals) in ${relativePath}`,
@@ -6373,7 +6444,7 @@ dist/
6373
6444
  checkId: 'UNICODE-STEGO-003',
6374
6445
  name: 'Eval on String with Hidden Payload',
6375
6446
  description: 'eval() or Function() is called with a string that has very few visible characters but a large byte footprint - indicates invisible Unicode payload',
6376
- category: 'unicode-stego',
6447
+ category: 'supply-chain',
6377
6448
  severity: 'critical',
6378
6449
  passed: false,
6379
6450
  message: `Found eval/Function with ${visibleChars} visible chars but ${byteLength} bytes in ${relativePath}`,
@@ -6415,7 +6486,7 @@ dist/
6415
6486
  checkId: 'UNICODE-STEGO-004',
6416
6487
  name: 'Unicode Tag Character Block Detected',
6417
6488
  description: 'Source file contains characters from the Unicode Tag block (U+E0000-U+E01EF) which have no visible rendering and can be used to hide data',
6418
- category: 'unicode-stego',
6489
+ category: 'supply-chain',
6419
6490
  severity: 'high',
6420
6491
  passed: false,
6421
6492
  message: `Found Unicode tag block characters in ${relativePath}`,
@@ -6427,6 +6498,69 @@ dist/
6427
6498
  }
6428
6499
  }
6429
6500
  }
6501
+ // UNICODE-STEGO-005: Homoglyph Substitution Detection
6502
+ // Detect Cyrillic/Greek/fullwidth letters that look identical to Latin letters.
6503
+ // Only scan code files where identifiers matter.
6504
+ const codeExtensions = new Set(['.js', '.ts', '.mjs', '.cjs', '.tsx', '.jsx', '.py']);
6505
+ const codeFiles = sourceFiles.filter((f) => codeExtensions.has(path.extname(f).toLowerCase()));
6506
+ const HOMOGLYPHS = {
6507
+ 0x0430: 'a', 0x0435: 'e', 0x043E: 'o', 0x0440: 'p',
6508
+ 0x0441: 'c', 0x0443: 'y', 0x0445: 'x', 0x0456: 'i',
6509
+ 0x0455: 's', 0x04BB: 'h', 0x0501: 'd', 0x051B: 'q',
6510
+ 0x03B1: 'a', 0x03BF: 'o', 0x03C1: 'p',
6511
+ 0xFF41: 'a', 0xFF45: 'e', 0xFF4F: 'o',
6512
+ };
6513
+ const homoglyphCodes = new Set(Object.keys(HOMOGLYPHS).map(Number));
6514
+ for (const filePath of codeFiles) {
6515
+ const relativePath = path.relative(targetDir, filePath);
6516
+ let content;
6517
+ try {
6518
+ content = await fs.readFile(filePath, 'utf-8');
6519
+ }
6520
+ catch {
6521
+ continue;
6522
+ }
6523
+ if (content.length > MAX_FILE_SIZE)
6524
+ continue;
6525
+ const lines = content.split('\n');
6526
+ const foundHomoglyphs = [];
6527
+ for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
6528
+ const line = lines[lineIdx];
6529
+ if (line.length > MAX_LINE_LENGTH)
6530
+ continue;
6531
+ const trimmed = line.trimStart();
6532
+ if (trimmed.startsWith('//') || trimmed.startsWith('#') || trimmed.startsWith('*'))
6533
+ continue;
6534
+ for (const ch of line) {
6535
+ const cp = ch.codePointAt(0);
6536
+ if (homoglyphCodes.has(cp)) {
6537
+ foundHomoglyphs.push({ line: lineIdx + 1, lookalike: HOMOGLYPHS[cp], cp });
6538
+ break;
6539
+ }
6540
+ }
6541
+ if (foundHomoglyphs.length >= 5)
6542
+ break;
6543
+ }
6544
+ if (foundHomoglyphs.length > 0) {
6545
+ const examples = foundHomoglyphs
6546
+ .slice(0, 3)
6547
+ .map((h) => `U+${h.cp.toString(16).toUpperCase().padStart(4, '0')} (looks like '${h.lookalike}') at line ${h.line}`)
6548
+ .join(', ');
6549
+ findings.push({
6550
+ checkId: 'UNICODE-STEGO-005',
6551
+ name: 'Homoglyph Character Substitution Detected',
6552
+ description: 'Source file contains non-Latin characters (Cyrillic, Greek, or fullwidth) that visually mimic Latin letters. This can disguise malicious identifiers or imports.',
6553
+ category: 'supply-chain',
6554
+ severity: 'high',
6555
+ passed: false,
6556
+ message: `Found ${foundHomoglyphs.length} homoglyph substitution(s) in ${relativePath}: ${examples}`,
6557
+ file: relativePath,
6558
+ line: foundHomoglyphs[0].line,
6559
+ fixable: false,
6560
+ fix: 'Replace non-Latin lookalike characters with their ASCII equivalents. Run: node -e "const s=require(\'fs\').readFileSync(\'' + relativePath + '\',\'utf8\');[...s].forEach((c,i)=>{const cp=c.codePointAt(0);if(cp>0x7F&&cp<0xFFFF)console.log(\'offset\',i,\'U+\'+cp.toString(16),JSON.stringify(c))})"',
6561
+ });
6562
+ }
6563
+ }
6430
6564
  return findings;
6431
6565
  }
6432
6566
  }