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.
- package/dist/cli.js +22 -59
- package/dist/cli.js.map +1 -1
- package/dist/hardening/scanner.d.ts.map +1 -1
- package/dist/hardening/scanner.js +12 -146
- package/dist/hardening/scanner.js.map +1 -1
- package/dist/hardening/taxonomy.d.ts.map +1 -1
- package/dist/hardening/taxonomy.js +0 -1
- package/dist/hardening/taxonomy.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- 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,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;
|
|
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
|
-
|
|
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
|
-
//
|
|
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 (
|
|
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 (
|
|
6357
|
-
category: '
|
|
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:
|
|
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 -
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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
|
}
|