@prodcycle/prodcycle 0.6.3 → 0.6.4
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/utils/fs.js +31 -12
- package/package.json +1 -1
package/dist/utils/fs.js
CHANGED
|
@@ -38,6 +38,12 @@ const fs = __importStar(require("fs"));
|
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const minimatch_1 = require("minimatch");
|
|
40
40
|
const MAX_FILE_SIZE = 256 * 1024; // 256 KB
|
|
41
|
+
// Reusable strict UTF-8 decoder. `TextDecoder` is stateless on the same
|
|
42
|
+
// instance (encoding + fatal are fixed at construction), so we allocate
|
|
43
|
+
// once at module load instead of once per file in the walk loop —
|
|
44
|
+
// repos with thousands of files would otherwise produce thousands of
|
|
45
|
+
// short-lived decoder objects per scan.
|
|
46
|
+
const UTF8_DECODER = new TextDecoder('utf-8', { fatal: true });
|
|
41
47
|
/**
|
|
42
48
|
* Total file ceiling per scan. Hit on the OSS-CLI benchmark scanning
|
|
43
49
|
* `hapifhir/hapi-fhir` (~13k files) — the CLI silently dropped ~3k files
|
|
@@ -397,18 +403,31 @@ function walk(dir, repoRoot, gitignores, prodcycleIgnores, includePatterns, user
|
|
|
397
403
|
}
|
|
398
404
|
if (isBinary(buffer))
|
|
399
405
|
continue;
|
|
400
|
-
|
|
401
|
-
//
|
|
402
|
-
//
|
|
403
|
-
//
|
|
404
|
-
//
|
|
405
|
-
//
|
|
406
|
-
//
|
|
407
|
-
//
|
|
408
|
-
//
|
|
409
|
-
//
|
|
410
|
-
//
|
|
411
|
-
//
|
|
406
|
+
// Strict UTF-8 decode: throw (and skip the file) on invalid byte
|
|
407
|
+
// sequences. Pre-fix this was `buffer.toString('utf8')`, which
|
|
408
|
+
// silently replaces invalid bytes with U+FFFD and includes the file
|
|
409
|
+
// anyway. Python's `open(encoding='utf-8')` raises UnicodeDecodeError
|
|
410
|
+
// on the same input and skips the file via its except clause, so
|
|
411
|
+
// Node ended up sending files Python wouldn't. Those files were
|
|
412
|
+
// overwhelmingly garbage (U+FFFD soup with no real content), but
|
|
413
|
+
// the inflated payload pushed many scans over the sync /validate
|
|
414
|
+
// limit and into the chunked-session fallback — Node ran 5–75x
|
|
415
|
+
// slower than Python on the same repos (dexidp-dex: npm=525s vs
|
|
416
|
+
// py=7s, frappe-erpnext: 1448s vs 42s, both 0 finding differences)
|
|
417
|
+
// during the 2026-05-12 GA-validation sweep. Catch the decode
|
|
418
|
+
// error and treat exactly like Python.
|
|
419
|
+
let content;
|
|
420
|
+
try {
|
|
421
|
+
content = UTF8_DECODER.decode(buffer);
|
|
422
|
+
}
|
|
423
|
+
catch {
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
// Post-decode size filter mirrors the service's 256 KB per-file
|
|
427
|
+
// enforcement. Without invalid-UTF-8 inflation (now skipped above),
|
|
428
|
+
// post-decode byte length usually matches `stats.size`, but a BOM
|
|
429
|
+
// or rare normalization edge case can still differ — keep the
|
|
430
|
+
// re-measure as defense-in-depth.
|
|
412
431
|
if (Buffer.byteLength(content, 'utf8') > MAX_FILE_SIZE)
|
|
413
432
|
continue;
|
|
414
433
|
files[relPath] = content;
|
package/package.json
CHANGED