hackmyagent 0.11.2 → 0.11.3

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;CAuWxC"}
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"}
@@ -6224,12 +6224,7 @@ dist/
6224
6224
  */
6225
6225
  async checkUnicodeSteganography(targetDir, _autoFix) {
6226
6226
  const findings = [];
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);
6227
+ const sourceFiles = await this.findSourceFiles(targetDir, targetDir);
6233
6228
  for (const filePath of sourceFiles) {
6234
6229
  const relativePath = path.relative(targetDir, filePath);
6235
6230
  let rawBuffer;
@@ -6243,69 +6238,18 @@ dist/
6243
6238
  if (rawBuffer.length > MAX_FILE_SIZE)
6244
6239
  continue;
6245
6240
  // UNICODE-STEGO-001: Invisible Codepoint Detection
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
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)
6251
6243
  let hasVariationSelectors = false;
6252
6244
  let variationSelectorLine = 1;
6253
6245
  let hasTagCharsIn001 = false;
6254
6246
  let tagCharLine001 = 1;
6255
- let hasZeroWidth = false;
6256
- let zeroWidthLine = 1;
6257
- let hasBidiOverride = false;
6258
- let bidiOverrideLine = 1;
6259
6247
  let currentLine = 1;
6260
6248
  for (let i = 0; i < rawBuffer.length; i++) {
6261
6249
  if (rawBuffer[i] === 0x0A) {
6262
6250
  currentLine++;
6263
6251
  continue;
6264
6252
  }
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
- }
6309
6253
  // Variation selectors: EF B8 80-8F
6310
6254
  if (rawBuffer[i] === 0xEF &&
6311
6255
  i + 2 < rawBuffer.length &&
@@ -6329,39 +6273,24 @@ dist/
6329
6273
  }
6330
6274
  }
6331
6275
  }
6332
- if (hasZeroWidth || hasVariationSelectors || hasTagCharsIn001 || hasBidiOverride) {
6276
+ if (hasVariationSelectors || hasTagCharsIn001) {
6333
6277
  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)');
6338
6278
  if (hasVariationSelectors)
6339
6279
  detectedTypes.push('variation selectors (U+FE00-FE0F)');
6340
6280
  if (hasTagCharsIn001)
6341
6281
  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';
6353
6282
  findings.push({
6354
6283
  checkId: 'UNICODE-STEGO-001',
6355
6284
  name: 'Invisible Unicode Codepoints Detected',
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,
6285
+ description: 'Source file contains invisible Unicode codepoints that can hide malicious payloads (GlassWorm attack vector)',
6286
+ category: 'unicode-stego',
6287
+ severity: 'critical',
6359
6288
  passed: false,
6360
6289
  message: `Found ${detectedTypes.join(' and ')} in ${relativePath}`,
6361
6290
  file: relativePath,
6362
- line: earliestLine,
6291
+ line: hasVariationSelectors ? variationSelectorLine : tagCharLine001,
6363
6292
  fixable: false,
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"',
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"',
6365
6294
  });
6366
6295
  }
6367
6296
  // UNICODE-STEGO-002: GlassWorm Decoder Pattern
@@ -6390,7 +6319,7 @@ dist/
6390
6319
  checkId: 'UNICODE-STEGO-002',
6391
6320
  name: 'GlassWorm Decoder Pattern Detected',
6392
6321
  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',
6393
- category: 'supply-chain',
6322
+ category: 'unicode-stego',
6394
6323
  severity: 'critical',
6395
6324
  passed: false,
6396
6325
  message: `Found GlassWorm decoder pattern (.codePointAt + hex range literals) in ${relativePath}`,
@@ -6444,7 +6373,7 @@ dist/
6444
6373
  checkId: 'UNICODE-STEGO-003',
6445
6374
  name: 'Eval on String with Hidden Payload',
6446
6375
  description: 'eval() or Function() is called with a string that has very few visible characters but a large byte footprint - indicates invisible Unicode payload',
6447
- category: 'supply-chain',
6376
+ category: 'unicode-stego',
6448
6377
  severity: 'critical',
6449
6378
  passed: false,
6450
6379
  message: `Found eval/Function with ${visibleChars} visible chars but ${byteLength} bytes in ${relativePath}`,
@@ -6486,7 +6415,7 @@ dist/
6486
6415
  checkId: 'UNICODE-STEGO-004',
6487
6416
  name: 'Unicode Tag Character Block Detected',
6488
6417
  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',
6489
- category: 'supply-chain',
6418
+ category: 'unicode-stego',
6490
6419
  severity: 'high',
6491
6420
  passed: false,
6492
6421
  message: `Found Unicode tag block characters in ${relativePath}`,
@@ -6498,69 +6427,6 @@ dist/
6498
6427
  }
6499
6428
  }
6500
6429
  }
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
- }
6564
6430
  return findings;
6565
6431
  }
6566
6432
  }