@quantakrypto/core 0.4.4 → 0.5.0
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/README.md +76 -27
- package/dist/cbom-merge.d.ts.map +1 -1
- package/dist/cbom-merge.js +30 -8
- package/dist/cbom-merge.js.map +1 -1
- package/dist/cbom.d.ts +5 -0
- package/dist/cbom.d.ts.map +1 -1
- package/dist/cbom.js +132 -37
- package/dist/cbom.js.map +1 -1
- package/dist/comments.d.ts +2 -23
- package/dist/comments.d.ts.map +1 -1
- package/dist/comments.js +24 -5
- package/dist/comments.js.map +1 -1
- package/dist/detect-utils.d.ts +55 -21
- package/dist/detect-utils.d.ts.map +1 -1
- package/dist/detect-utils.js +133 -7
- package/dist/detect-utils.js.map +1 -1
- package/dist/detectors/age.d.ts +16 -0
- package/dist/detectors/age.d.ts.map +1 -0
- package/dist/detectors/age.js +40 -0
- package/dist/detectors/age.js.map +1 -0
- package/dist/detectors/ansible.d.ts +13 -0
- package/dist/detectors/ansible.d.ts.map +1 -0
- package/dist/detectors/ansible.js +108 -0
- package/dist/detectors/ansible.js.map +1 -0
- package/dist/detectors/bicep.d.ts +20 -0
- package/dist/detectors/bicep.d.ts.map +1 -0
- package/dist/detectors/bicep.js +74 -0
- package/dist/detectors/bicep.js.map +1 -0
- package/dist/detectors/c.d.ts.map +1 -1
- package/dist/detectors/c.js +30 -3
- package/dist/detectors/c.js.map +1 -1
- package/dist/detectors/cicd.d.ts.map +1 -1
- package/dist/detectors/cicd.js +18 -5
- package/dist/detectors/cicd.js.map +1 -1
- package/dist/detectors/cloud-kms.d.ts +8 -5
- package/dist/detectors/cloud-kms.d.ts.map +1 -1
- package/dist/detectors/cloud-kms.js +62 -20
- package/dist/detectors/cloud-kms.js.map +1 -1
- package/dist/detectors/cloudformation.d.ts +8 -0
- package/dist/detectors/cloudformation.d.ts.map +1 -1
- package/dist/detectors/cloudformation.js +41 -10
- package/dist/detectors/cloudformation.js.map +1 -1
- package/dist/detectors/codesign.d.ts +50 -0
- package/dist/detectors/codesign.d.ts.map +1 -0
- package/dist/detectors/codesign.js +143 -0
- package/dist/detectors/codesign.js.map +1 -0
- package/dist/detectors/csharp.d.ts.map +1 -1
- package/dist/detectors/csharp.js +13 -3
- package/dist/detectors/csharp.js.map +1 -1
- package/dist/detectors/dart.d.ts +35 -0
- package/dist/detectors/dart.d.ts.map +1 -0
- package/dist/detectors/dart.js +177 -0
- package/dist/detectors/dart.js.map +1 -0
- package/dist/detectors/database.d.ts +5 -3
- package/dist/detectors/database.d.ts.map +1 -1
- package/dist/detectors/database.js +45 -8
- package/dist/detectors/database.js.map +1 -1
- package/dist/detectors/dkim.d.ts +54 -0
- package/dist/detectors/dkim.d.ts.map +1 -0
- package/dist/detectors/dkim.js +92 -0
- package/dist/detectors/dkim.js.map +1 -0
- package/dist/detectors/dnssec.d.ts.map +1 -1
- package/dist/detectors/dnssec.js +19 -10
- package/dist/detectors/dnssec.js.map +1 -1
- package/dist/detectors/elixir.d.ts.map +1 -1
- package/dist/detectors/elixir.js +34 -2
- package/dist/detectors/elixir.js.map +1 -1
- package/dist/detectors/java.d.ts.map +1 -1
- package/dist/detectors/java.js +33 -3
- package/dist/detectors/java.js.map +1 -1
- package/dist/detectors/jose.d.ts.map +1 -1
- package/dist/detectors/jose.js +13 -3
- package/dist/detectors/jose.js.map +1 -1
- package/dist/detectors/jwk.d.ts +7 -7
- package/dist/detectors/jwk.d.ts.map +1 -1
- package/dist/detectors/jwk.js +44 -8
- package/dist/detectors/jwk.js.map +1 -1
- package/dist/detectors/k8s.d.ts.map +1 -1
- package/dist/detectors/k8s.js +5 -2
- package/dist/detectors/k8s.js.map +1 -1
- package/dist/detectors/keystore.d.ts +19 -0
- package/dist/detectors/keystore.d.ts.map +1 -0
- package/dist/detectors/keystore.js +76 -0
- package/dist/detectors/keystore.js.map +1 -0
- package/dist/detectors/mesh.d.ts +7 -8
- package/dist/detectors/mesh.d.ts.map +1 -1
- package/dist/detectors/mesh.js +17 -35
- package/dist/detectors/mesh.js.map +1 -1
- package/dist/detectors/messaging.d.ts +2 -2
- package/dist/detectors/messaging.d.ts.map +1 -1
- package/dist/detectors/messaging.js +19 -12
- package/dist/detectors/messaging.js.map +1 -1
- package/dist/detectors/objc.d.ts +41 -0
- package/dist/detectors/objc.d.ts.map +1 -0
- package/dist/detectors/objc.js +146 -0
- package/dist/detectors/objc.js.map +1 -0
- package/dist/detectors/openpgp.d.ts +20 -0
- package/dist/detectors/openpgp.d.ts.map +1 -0
- package/dist/detectors/openpgp.js +151 -0
- package/dist/detectors/openpgp.js.map +1 -0
- package/dist/detectors/pem.d.ts.map +1 -1
- package/dist/detectors/pem.js +32 -0
- package/dist/detectors/pem.js.map +1 -1
- package/dist/detectors/php.d.ts +2 -1
- package/dist/detectors/php.d.ts.map +1 -1
- package/dist/detectors/php.js +7 -3
- package/dist/detectors/php.js.map +1 -1
- package/dist/detectors/pkcs11.d.ts +17 -0
- package/dist/detectors/pkcs11.d.ts.map +1 -0
- package/dist/detectors/pkcs11.js +87 -0
- package/dist/detectors/pkcs11.js.map +1 -0
- package/dist/detectors/proxy.d.ts +52 -0
- package/dist/detectors/proxy.d.ts.map +1 -0
- package/dist/detectors/proxy.js +157 -0
- package/dist/detectors/proxy.js.map +1 -0
- package/dist/detectors/pulumi.d.ts +19 -0
- package/dist/detectors/pulumi.d.ts.map +1 -0
- package/dist/detectors/pulumi.js +80 -0
- package/dist/detectors/pulumi.js.map +1 -0
- package/dist/detectors/rust.d.ts.map +1 -1
- package/dist/detectors/rust.js +5 -0
- package/dist/detectors/rust.js.map +1 -1
- package/dist/detectors/secrets.d.ts +4 -1
- package/dist/detectors/secrets.d.ts.map +1 -1
- package/dist/detectors/secrets.js +5 -21
- package/dist/detectors/secrets.js.map +1 -1
- package/dist/detectors/solidity.d.ts +50 -0
- package/dist/detectors/solidity.d.ts.map +1 -0
- package/dist/detectors/solidity.js +108 -0
- package/dist/detectors/solidity.js.map +1 -0
- package/dist/detectors/source.d.ts.map +1 -1
- package/dist/detectors/source.js +53 -6
- package/dist/detectors/source.js.map +1 -1
- package/dist/detectors/spire.d.ts +49 -0
- package/dist/detectors/spire.d.ts.map +1 -0
- package/dist/detectors/spire.js +77 -0
- package/dist/detectors/spire.js.map +1 -0
- package/dist/detectors/ssh-ca.d.ts +72 -0
- package/dist/detectors/ssh-ca.d.ts.map +1 -0
- package/dist/detectors/ssh-ca.js +119 -0
- package/dist/detectors/ssh-ca.js.map +1 -0
- package/dist/detectors/supply-chain.d.ts +17 -0
- package/dist/detectors/supply-chain.d.ts.map +1 -0
- package/dist/detectors/supply-chain.js +89 -0
- package/dist/detectors/supply-chain.js.map +1 -0
- package/dist/detectors/swift.d.ts +22 -0
- package/dist/detectors/swift.d.ts.map +1 -0
- package/dist/detectors/swift.js +120 -0
- package/dist/detectors/swift.js.map +1 -0
- package/dist/detectors/terraform.d.ts.map +1 -1
- package/dist/detectors/terraform.js +35 -8
- package/dist/detectors/terraform.js.map +1 -1
- package/dist/detectors/vault.d.ts +15 -0
- package/dist/detectors/vault.d.ts.map +1 -0
- package/dist/detectors/vault.js +97 -0
- package/dist/detectors/vault.js.map +1 -0
- package/dist/detectors/vpn.d.ts +8 -8
- package/dist/detectors/vpn.d.ts.map +1 -1
- package/dist/detectors/vpn.js +12 -38
- package/dist/detectors/vpn.js.map +1 -1
- package/dist/detectors/weak-hash.d.ts +71 -0
- package/dist/detectors/weak-hash.d.ts.map +1 -0
- package/dist/detectors/weak-hash.js +116 -0
- package/dist/detectors/weak-hash.js.map +1 -0
- package/dist/detectors/webauthn.d.ts +61 -0
- package/dist/detectors/webauthn.d.ts.map +1 -0
- package/dist/detectors/webauthn.js +109 -0
- package/dist/detectors/webauthn.js.map +1 -0
- package/dist/detectors/xmldsig.d.ts +23 -0
- package/dist/detectors/xmldsig.d.ts.map +1 -0
- package/dist/detectors/xmldsig.js +84 -0
- package/dist/detectors/xmldsig.js.map +1 -0
- package/dist/evidence.d.ts +74 -4
- package/dist/evidence.d.ts.map +1 -1
- package/dist/evidence.js +71 -0
- package/dist/evidence.js.map +1 -1
- package/dist/index.d.ts +14 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -11
- package/dist/index.js.map +1 -1
- package/dist/inventory.d.ts +0 -2
- package/dist/inventory.d.ts.map +1 -1
- package/dist/inventory.js +15 -6
- package/dist/inventory.js.map +1 -1
- package/dist/registry.d.ts +9 -4
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +51 -4
- package/dist/registry.js.map +1 -1
- package/dist/remediate-pipeline.d.ts +0 -3
- package/dist/remediate-pipeline.d.ts.map +1 -1
- package/dist/remediate-pipeline.js +1 -1
- package/dist/remediate-pipeline.js.map +1 -1
- package/dist/remediation.d.ts +10 -0
- package/dist/remediation.d.ts.map +1 -1
- package/dist/remediation.js +32 -0
- package/dist/remediation.js.map +1 -1
- package/dist/report.d.ts +9 -0
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +31 -1
- package/dist/report.js.map +1 -1
- package/dist/scan-worker.js +5 -3
- package/dist/scan-worker.js.map +1 -1
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +35 -6
- package/dist/scan.js.map +1 -1
- package/dist/standards-profiles.d.ts +58 -0
- package/dist/standards-profiles.d.ts.map +1 -0
- package/dist/standards-profiles.js +100 -0
- package/dist/standards-profiles.js.map +1 -0
- package/dist/types.d.ts +6 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/vex.d.ts +39 -0
- package/dist/vex.d.ts.map +1 -0
- package/dist/vex.js +87 -0
- package/dist/vex.js.map +1 -0
- package/dist/walk.d.ts +8 -3
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +32 -1
- package/dist/walk.js.map +1 -1
- package/package.json +1 -1
- package/src/cbom-merge.ts +38 -8
- package/src/cbom.ts +153 -39
- package/src/comments.ts +25 -6
- package/src/detect-utils.ts +133 -11
- package/src/detectors/age.ts +60 -0
- package/src/detectors/ansible.ts +140 -0
- package/src/detectors/bicep.ts +107 -0
- package/src/detectors/c.ts +31 -3
- package/src/detectors/cicd.ts +18 -5
- package/src/detectors/cloud-kms.ts +79 -25
- package/src/detectors/cloudformation.ts +45 -10
- package/src/detectors/codesign.ts +214 -0
- package/src/detectors/csharp.ts +13 -3
- package/src/detectors/dart.ts +237 -0
- package/src/detectors/database.ts +64 -11
- package/src/detectors/dkim.ts +172 -0
- package/src/detectors/dnssec.ts +19 -10
- package/src/detectors/elixir.ts +33 -2
- package/src/detectors/java.ts +37 -3
- package/src/detectors/jose.ts +19 -3
- package/src/detectors/jwk.ts +59 -17
- package/src/detectors/k8s.ts +5 -2
- package/src/detectors/keystore.ts +109 -0
- package/src/detectors/mesh.ts +21 -45
- package/src/detectors/messaging.ts +22 -14
- package/src/detectors/objc.ts +209 -0
- package/src/detectors/openpgp.ts +194 -0
- package/src/detectors/pem.ts +30 -0
- package/src/detectors/php.ts +9 -4
- package/src/detectors/pkcs11.ts +113 -0
- package/src/detectors/proxy.ts +236 -0
- package/src/detectors/pulumi.ts +106 -0
- package/src/detectors/rust.ts +5 -0
- package/src/detectors/secrets.ts +15 -26
- package/src/detectors/solidity.ts +176 -0
- package/src/detectors/source.ts +48 -5
- package/src/detectors/spire.ts +143 -0
- package/src/detectors/ssh-ca.ts +215 -0
- package/src/detectors/supply-chain.ts +117 -0
- package/src/detectors/swift.ts +149 -0
- package/src/detectors/terraform.ts +42 -8
- package/src/detectors/vault.ts +120 -0
- package/src/detectors/vpn.ts +20 -48
- package/src/detectors/weak-hash.ts +217 -0
- package/src/detectors/webauthn.ts +205 -0
- package/src/detectors/xmldsig.ts +115 -0
- package/src/evidence.ts +127 -4
- package/src/index.ts +41 -13
- package/src/inventory.ts +15 -7
- package/src/registry.ts +51 -4
- package/src/remediate-pipeline.ts +1 -1
- package/src/remediation.ts +39 -0
- package/src/report.ts +36 -1
- package/src/scan-worker.ts +5 -3
- package/src/scan.ts +41 -6
- package/src/standards-profiles.ts +136 -0
- package/src/types.ts +9 -4
- package/src/version.ts +1 -1
- package/src/vex.ts +122 -0
- package/src/walk.ts +33 -2
package/dist/comments.d.ts
CHANGED
|
@@ -15,9 +15,7 @@
|
|
|
15
15
|
* detection benchmark's recall gate.
|
|
16
16
|
*/
|
|
17
17
|
import type { Finding } from "./types.js";
|
|
18
|
-
|
|
19
|
-
/** The comment style for a file path, or null when we don't strip comments for it. */
|
|
20
|
-
export declare function commentStyleForFile(file: string): CommentStyle | null;
|
|
18
|
+
type CommentStyle = "c" | "hash";
|
|
21
19
|
/**
|
|
22
20
|
* Compute the comment spans (`[start, end)` offsets) of `content`, skipping over
|
|
23
21
|
* string literals so a comment marker inside a string is not treated as a
|
|
@@ -25,26 +23,6 @@ export declare function commentStyleForFile(file: string): CommentStyle | null;
|
|
|
25
23
|
* makes backtick strings raw (no escapes).
|
|
26
24
|
*/
|
|
27
25
|
export declare function commentSpans(content: string, style: CommentStyle, rawBacktick?: boolean): Array<[number, number]>;
|
|
28
|
-
/**
|
|
29
|
-
* Compute Python triple-quoted string spans (`"""…"""` / `'''…'''`). These are
|
|
30
|
-
* docstrings / prose in practice, so a crypto *name* mentioned inside one
|
|
31
|
-
* (`:param key_type: eg "ssh-ed25519"`) should not fire a token finding — but a
|
|
32
|
-
* PEM key pasted into one is still real material, so {@link stripCommentFindings}
|
|
33
|
-
* exempts `pem-*` rules. Comments and normal strings are skipped so a `"""`
|
|
34
|
-
* delimiter inside them is not mis-detected.
|
|
35
|
-
*/
|
|
36
|
-
export declare function pythonDocstringSpans(content: string): Array<[number, number]>;
|
|
37
|
-
/** True if `offset` falls inside one of the (sorted, non-overlapping) spans. */
|
|
38
|
-
export declare function offsetInSpans(spans: ReadonlyArray<[number, number]>, offset: number): boolean;
|
|
39
|
-
/**
|
|
40
|
-
* Line numbers (1-based) suppressed by an inline ignore directive:
|
|
41
|
-
* - `qscan-ignore-line` on a line suppresses findings on THAT line.
|
|
42
|
-
* - `qscan-ignore-next-line` on a line suppresses findings on the NEXT line.
|
|
43
|
-
* The directive text is matched anywhere on the line (usually in a comment), so
|
|
44
|
-
* it is language-agnostic. `qscan-ignore-line` is not a substring of
|
|
45
|
-
* `qscan-ignore-next-line`, so the two never collide.
|
|
46
|
-
*/
|
|
47
|
-
export declare function ignoredLines(content: string): Set<number>;
|
|
48
26
|
/** Drop findings on lines suppressed by an inline `qscan-ignore` directive. */
|
|
49
27
|
export declare function stripIgnoredFindings(findings: Finding[], content: string): Finding[];
|
|
50
28
|
/**
|
|
@@ -69,4 +47,5 @@ export declare function stringSpans(content: string, style: CommentStyle, rawBac
|
|
|
69
47
|
* through untouched.
|
|
70
48
|
*/
|
|
71
49
|
export declare function stripStringLiteralFindings(findings: Finding[], content: string, file: string, ruleIds: ReadonlySet<string>): Finding[];
|
|
50
|
+
export {};
|
|
72
51
|
//# sourceMappingURL=comments.d.ts.map
|
package/dist/comments.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comments.d.ts","sourceRoot":"","sources":["../src/comments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,
|
|
1
|
+
{"version":3,"file":"comments.d.ts","sourceRoot":"","sources":["../src/comments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,KAAK,YAAY,GAAG,GAAG,GAAG,MAAM,CAAC;AAgGjC;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,YAAY,EACnB,WAAW,UAAQ,GAClB,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAoCzB;AAmFD,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,CAKpF;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GACX,OAAO,EAAE,CAyBX;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,YAAY,EACnB,WAAW,UAAQ,GAClB,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAiCzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAC3B,OAAO,EAAE,CAkBX"}
|
package/dist/comments.js
CHANGED
|
@@ -21,11 +21,30 @@ const C_LIKE = [
|
|
|
21
21
|
".cxx",
|
|
22
22
|
".hpp",
|
|
23
23
|
".hh",
|
|
24
|
+
// PHP and Scala also use C-style `//` + `/* */` comments (PHP additionally uses
|
|
25
|
+
// `#`, handled by the hash lexer running first would miss `//`; C-style covers both
|
|
26
|
+
// since PHP `//` is the common form and `#` lines are rare in modern PHP).
|
|
27
|
+
".php",
|
|
28
|
+
".php3",
|
|
29
|
+
".php4",
|
|
30
|
+
".php5",
|
|
31
|
+
".phtml",
|
|
32
|
+
".scala",
|
|
33
|
+
".sc",
|
|
34
|
+
".swift",
|
|
35
|
+
// Objective-C (.m/.mm) and Dart (.dart) both use C-style `//` + `/* */`.
|
|
36
|
+
".m",
|
|
37
|
+
".mm",
|
|
38
|
+
".dart",
|
|
39
|
+
// Smart-contract languages (Solidity/Move/Cairo) also use C-style comments.
|
|
40
|
+
".sol",
|
|
41
|
+
".move",
|
|
42
|
+
".cairo",
|
|
24
43
|
];
|
|
25
44
|
/** Hash-style (`#`) comment languages, by extension. */
|
|
26
|
-
const HASH_LIKE = [".py", ".pyi", ".pyw", ".rb"];
|
|
45
|
+
const HASH_LIKE = [".py", ".pyi", ".pyw", ".rb", ".ex", ".exs"];
|
|
27
46
|
/** The comment style for a file path, or null when we don't strip comments for it. */
|
|
28
|
-
|
|
47
|
+
function commentStyleForFile(file) {
|
|
29
48
|
const lower = file.toLowerCase();
|
|
30
49
|
if (C_LIKE.some((e) => lower.endsWith(e)))
|
|
31
50
|
return "c";
|
|
@@ -127,7 +146,7 @@ export function commentSpans(content, style, rawBacktick = false) {
|
|
|
127
146
|
* exempts `pem-*` rules. Comments and normal strings are skipped so a `"""`
|
|
128
147
|
* delimiter inside them is not mis-detected.
|
|
129
148
|
*/
|
|
130
|
-
|
|
149
|
+
function pythonDocstringSpans(content) {
|
|
131
150
|
const spans = [];
|
|
132
151
|
const n = content.length;
|
|
133
152
|
let i = 0;
|
|
@@ -169,7 +188,7 @@ export function pythonDocstringSpans(content) {
|
|
|
169
188
|
return spans;
|
|
170
189
|
}
|
|
171
190
|
/** True if `offset` falls inside one of the (sorted, non-overlapping) spans. */
|
|
172
|
-
|
|
191
|
+
function offsetInSpans(spans, offset) {
|
|
173
192
|
let lo = 0;
|
|
174
193
|
let hi = spans.length - 1;
|
|
175
194
|
while (lo <= hi) {
|
|
@@ -192,7 +211,7 @@ export function offsetInSpans(spans, offset) {
|
|
|
192
211
|
* it is language-agnostic. `qscan-ignore-line` is not a substring of
|
|
193
212
|
* `qscan-ignore-next-line`, so the two never collide.
|
|
194
213
|
*/
|
|
195
|
-
|
|
214
|
+
function ignoredLines(content) {
|
|
196
215
|
const ignored = new Set();
|
|
197
216
|
const lines = content.split("\n");
|
|
198
217
|
for (let i = 0; i < lines.length; i++) {
|
package/dist/comments.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comments.js","sourceRoot":"","sources":["../src/comments.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Comment-aware false-positive suppression.\n *\n * The detectors are lexical (regex over source text), so a crypto API name\n * written in a COMMENT — `// migrated off createECDH()` — fires a finding just\n * like real code. This module computes the comment spans of a file (respecting\n * string literals, so a `//` inside a string is not a comment) and drops any\n * finding whose match starts inside one.\n *\n * The lexer is deliberately conservative: it only ever ENTERS a comment on a\n * literal line comment, block comment, or `#` in code position (all of which ARE\n * comments), so it never mis-classifies code as a comment and can only reduce\n * false positives, not recall. The one inherent lexical ambiguity — a regex\n * literal that looks like a block-comment open — is rare and guarded by the\n * detection benchmark's recall gate.\n */\nimport type { Finding } from \"./types.js\";\n\nexport type CommentStyle = \"c\" | \"hash\";\n\n/** C-style (line + block) comment languages, by extension. */\nconst C_LIKE: readonly string[] = [\n \".js\",\n \".jsx\",\n \".ts\",\n \".tsx\",\n \".mjs\",\n \".cjs\",\n \".vue\",\n \".svelte\",\n \".go\",\n \".java\",\n \".kt\",\n \".kts\",\n \".cs\",\n \".rs\",\n \".c\",\n \".h\",\n \".cc\",\n \".cpp\",\n \".cxx\",\n \".hpp\",\n \".hh\",\n];\n\n/** Hash-style (`#`) comment languages, by extension. */\nconst HASH_LIKE: readonly string[] = [\".py\", \".pyi\", \".pyw\", \".rb\"];\n\n/** The comment style for a file path, or null when we don't strip comments for it. */\nexport function commentStyleForFile(file: string): CommentStyle | null {\n const lower = file.toLowerCase();\n if (C_LIKE.some((e) => lower.endsWith(e))) return \"c\";\n if (HASH_LIKE.some((e) => lower.endsWith(e))) return \"hash\";\n return null;\n}\n\n/**\n * Advance past a string / char literal starting at `content[i]` (a quote char);\n * returns the index just after it. Two correctness rules a naive escape-scan got\n * wrong (both verified as real bugs):\n * - a `'` char literal must close within a short window (`'\\u{10FFFF}'` is ~11\n * chars); otherwise it is a Rust lifetime (`'a`) or a stray apostrophe, NOT a\n * literal — return `i + 1` so the scan continues instead of consuming the\n * rest of the file.\n * - Go raw strings (backtick, when `rawBacktick`) do NOT process escapes, so a\n * trailing backslash (`` `C:\\` ``) must not swallow the closing delimiter.\n */\nfunction skipQuoted(content: string, i: number, n: number, rawBacktick: boolean): number {\n const quote = content[i];\n if (quote === \"'\") {\n const limit = Math.min(n, i + 1 + 12);\n let j = i + 1;\n while (j < limit) {\n if (content[j] === \"\\\\\") {\n j += 2;\n continue;\n }\n if (content[j] === \"'\") return j + 1;\n j++;\n }\n return i + 1; // a lifetime / stray apostrophe, not a char literal\n }\n const escapes = !(quote === \"`\" && rawBacktick);\n let j = i + 1;\n while (j < n) {\n if (escapes && content[j] === \"\\\\\") {\n j += 2;\n continue;\n }\n if (content[j] === quote) return j + 1;\n j++;\n }\n return n;\n}\n\n/**\n * Compute the comment spans (`[start, end)` offsets) of `content`, skipping over\n * string literals so a comment marker inside a string is not treated as a\n * comment. Spans are returned sorted and non-overlapping. `rawBacktick` (Go)\n * makes backtick strings raw (no escapes).\n */\nexport function commentSpans(\n content: string,\n style: CommentStyle,\n rawBacktick = false,\n): Array<[number, number]> {\n const spans: Array<[number, number]> = [];\n const n = content.length;\n let i = 0;\n while (i < n) {\n const c = content[i];\n // String / char literal: skip past it (lexically correct for lifetimes + raw strings).\n if (c === '\"' || c === \"'\" || c === \"`\") {\n i = skipQuoted(content, i, n, rawBacktick);\n continue;\n }\n if (style === \"c\" && c === \"/\" && content[i + 1] === \"/\") {\n const start = i;\n i += 2;\n while (i < n && content[i] !== \"\\n\") i++;\n spans.push([start, i]);\n continue;\n }\n if (style === \"c\" && c === \"/\" && content[i + 1] === \"*\") {\n const start = i;\n i += 2;\n while (i < n && !(content[i] === \"*\" && content[i + 1] === \"/\")) i++;\n i = Math.min(n, i + 2);\n spans.push([start, i]);\n continue;\n }\n if (style === \"hash\" && c === \"#\") {\n const start = i;\n i++;\n while (i < n && content[i] !== \"\\n\") i++;\n spans.push([start, i]);\n continue;\n }\n i++;\n }\n return spans;\n}\n\n/**\n * Compute Python triple-quoted string spans (`\"\"\"…\"\"\"` / `'''…'''`). These are\n * docstrings / prose in practice, so a crypto *name* mentioned inside one\n * (`:param key_type: eg \"ssh-ed25519\"`) should not fire a token finding — but a\n * PEM key pasted into one is still real material, so {@link stripCommentFindings}\n * exempts `pem-*` rules. Comments and normal strings are skipped so a `\"\"\"`\n * delimiter inside them is not mis-detected.\n */\nexport function pythonDocstringSpans(content: string): Array<[number, number]> {\n const spans: Array<[number, number]> = [];\n const n = content.length;\n let i = 0;\n while (i < n) {\n const c = content[i];\n if (c === \"#\") {\n i++;\n while (i < n && content[i] !== \"\\n\") i++;\n continue;\n }\n if ((c === '\"' || c === \"'\") && content[i + 1] === c && content[i + 2] === c) {\n const start = i;\n i += 3;\n while (i < n && !(content[i] === c && content[i + 1] === c && content[i + 2] === c)) i++;\n i = Math.min(n, i + 3);\n spans.push([start, i]);\n continue;\n }\n if (c === '\"' || c === \"'\") {\n const q = c;\n i++;\n while (i < n) {\n if (content[i] === \"\\\\\") {\n i += 2;\n continue;\n }\n if (content[i] === q) {\n i++;\n break;\n }\n i++;\n }\n continue;\n }\n i++;\n }\n return spans;\n}\n\n/** True if `offset` falls inside one of the (sorted, non-overlapping) spans. */\nexport function offsetInSpans(spans: ReadonlyArray<[number, number]>, offset: number): boolean {\n let lo = 0;\n let hi = spans.length - 1;\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1;\n const [s, e] = spans[mid];\n if (offset < s) hi = mid - 1;\n else if (offset >= e) lo = mid + 1;\n else return true;\n }\n return false;\n}\n\n/**\n * Line numbers (1-based) suppressed by an inline ignore directive:\n * - `qscan-ignore-line` on a line suppresses findings on THAT line.\n * - `qscan-ignore-next-line` on a line suppresses findings on the NEXT line.\n * The directive text is matched anywhere on the line (usually in a comment), so\n * it is language-agnostic. `qscan-ignore-line` is not a substring of\n * `qscan-ignore-next-line`, so the two never collide.\n */\nexport function ignoredLines(content: string): Set<number> {\n const ignored = new Set<number>();\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line.includes(\"qscan-ignore-next-line\")) ignored.add(i + 2);\n else if (line.includes(\"qscan-ignore-line\")) ignored.add(i + 1);\n }\n return ignored;\n}\n\n/** Drop findings on lines suppressed by an inline `qscan-ignore` directive. */\nexport function stripIgnoredFindings(findings: Finding[], content: string): Finding[] {\n if (findings.length === 0 || !content.includes(\"qscan-ignore\")) return findings;\n const ignored = ignoredLines(content);\n if (ignored.size === 0) return findings;\n return findings.filter((f) => !ignored.has(f.location.line));\n}\n\n/**\n * Drop findings whose match starts inside a comment. No-op when the file's\n * language has no comment style we handle, or when it has no comments.\n */\nexport function stripCommentFindings(\n findings: Finding[],\n content: string,\n file: string,\n): Finding[] {\n if (findings.length === 0) return findings;\n const style = commentStyleForFile(file);\n if (!style) return findings;\n const spans = commentSpans(content, style, /\\.go$/i.test(file));\n // Python docstrings suppress prose *token* rules but keep real PEM material.\n const docSpans = style === \"hash\" ? pythonDocstringSpans(content) : [];\n if (spans.length === 0 && docSpans.length === 0) return findings;\n\n // 1-based line → start offset, to turn a finding's (line, column) back into an\n // absolute offset (column is 1-based: offset = lineStart + column - 1).\n const lineStarts: number[] = [0];\n for (let i = 0; i < content.length; i++) {\n if (content[i] === \"\\n\") lineStarts.push(i + 1);\n }\n\n return findings.filter((f) => {\n const start = lineStarts[f.location.line - 1] ?? 0;\n const offset = start + ((f.location.column ?? 1) - 1);\n if (offsetInSpans(spans, offset)) return false;\n if (docSpans.length > 0 && !f.ruleId.startsWith(\"pem-\") && offsetInSpans(docSpans, offset)) {\n return false;\n }\n return true;\n });\n}\n\n/**\n * Compute the string-literal spans (`[start, end)`) of `content`, skipping over\n * comments so a quote inside a comment is not treated as a string. Used to\n * suppress findings from IDENTIFIER-only rules (e.g. a Go `SigningMethodRS256`\n * mentioned inside an error-message string) — the mirror of {@link commentSpans}.\n */\nexport function stringSpans(\n content: string,\n style: CommentStyle,\n rawBacktick = false,\n): Array<[number, number]> {\n const spans: Array<[number, number]> = [];\n const n = content.length;\n let i = 0;\n while (i < n) {\n const c = content[i];\n if (style === \"c\" && c === \"/\" && content[i + 1] === \"/\") {\n i += 2;\n while (i < n && content[i] !== \"\\n\") i++;\n continue;\n }\n if (style === \"c\" && c === \"/\" && content[i + 1] === \"*\") {\n i += 2;\n while (i < n && !(content[i] === \"*\" && content[i + 1] === \"/\")) i++;\n i = Math.min(n, i + 2);\n continue;\n }\n if (style === \"hash\" && c === \"#\") {\n i++;\n while (i < n && content[i] !== \"\\n\") i++;\n continue;\n }\n if (c === '\"' || c === \"'\" || c === \"`\") {\n const start = i;\n const end = skipQuoted(content, i, n, rawBacktick);\n // A lone `'` (Rust lifetime, end === start+1) is not a string; skip it.\n if (c !== \"'\" || end > start + 1) spans.push([start, end]);\n i = end;\n continue;\n }\n i++;\n }\n return spans;\n}\n\n/**\n * Drop findings of \"code-only\" rules (`ruleIds`) whose match starts inside a\n * string literal. Some rules — an identifier-form JWT signing method, a Go\n * `SigningMethodRS256` — are only meaningful as code; when the same token appears\n * inside a string (a test's `t.Error(\"SigningMethodPS256 …\")`) it is prose, not a\n * usage. Rules that legitimately match inside strings (quoted `\"RS256\"` alg\n * tokens, cipher-suite strings, ssh-key tokens) are NOT in `ruleIds` and pass\n * through untouched.\n */\nexport function stripStringLiteralFindings(\n findings: Finding[],\n content: string,\n file: string,\n ruleIds: ReadonlySet<string>,\n): Finding[] {\n if (findings.length === 0 || !findings.some((f) => ruleIds.has(f.ruleId))) return findings;\n const style = commentStyleForFile(file);\n if (!style) return findings;\n const spans = stringSpans(content, style, /\\.go$/i.test(file));\n if (spans.length === 0) return findings;\n\n const lineStarts: number[] = [0];\n for (let i = 0; i < content.length; i++) {\n if (content[i] === \"\\n\") lineStarts.push(i + 1);\n }\n\n return findings.filter((f) => {\n if (!ruleIds.has(f.ruleId)) return true;\n const start = lineStarts[f.location.line - 1] ?? 0;\n const offset = start + ((f.location.column ?? 1) - 1);\n return !offsetInSpans(spans, offset);\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"comments.js","sourceRoot":"","sources":["../src/comments.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Comment-aware false-positive suppression.\n *\n * The detectors are lexical (regex over source text), so a crypto API name\n * written in a COMMENT — `// migrated off createECDH()` — fires a finding just\n * like real code. This module computes the comment spans of a file (respecting\n * string literals, so a `//` inside a string is not a comment) and drops any\n * finding whose match starts inside one.\n *\n * The lexer is deliberately conservative: it only ever ENTERS a comment on a\n * literal line comment, block comment, or `#` in code position (all of which ARE\n * comments), so it never mis-classifies code as a comment and can only reduce\n * false positives, not recall. The one inherent lexical ambiguity — a regex\n * literal that looks like a block-comment open — is rare and guarded by the\n * detection benchmark's recall gate.\n */\nimport type { Finding } from \"./types.js\";\n\ntype CommentStyle = \"c\" | \"hash\";\n\n/** C-style (line + block) comment languages, by extension. */\nconst C_LIKE: readonly string[] = [\n \".js\",\n \".jsx\",\n \".ts\",\n \".tsx\",\n \".mjs\",\n \".cjs\",\n \".vue\",\n \".svelte\",\n \".go\",\n \".java\",\n \".kt\",\n \".kts\",\n \".cs\",\n \".rs\",\n \".c\",\n \".h\",\n \".cc\",\n \".cpp\",\n \".cxx\",\n \".hpp\",\n \".hh\",\n // PHP and Scala also use C-style `//` + `/* */` comments (PHP additionally uses\n // `#`, handled by the hash lexer running first would miss `//`; C-style covers both\n // since PHP `//` is the common form and `#` lines are rare in modern PHP).\n \".php\",\n \".php3\",\n \".php4\",\n \".php5\",\n \".phtml\",\n \".scala\",\n \".sc\",\n \".swift\",\n // Objective-C (.m/.mm) and Dart (.dart) both use C-style `//` + `/* */`.\n \".m\",\n \".mm\",\n \".dart\",\n // Smart-contract languages (Solidity/Move/Cairo) also use C-style comments.\n \".sol\",\n \".move\",\n \".cairo\",\n];\n\n/** Hash-style (`#`) comment languages, by extension. */\nconst HASH_LIKE: readonly string[] = [\".py\", \".pyi\", \".pyw\", \".rb\", \".ex\", \".exs\"];\n\n/** The comment style for a file path, or null when we don't strip comments for it. */\nfunction commentStyleForFile(file: string): CommentStyle | null {\n const lower = file.toLowerCase();\n if (C_LIKE.some((e) => lower.endsWith(e))) return \"c\";\n if (HASH_LIKE.some((e) => lower.endsWith(e))) return \"hash\";\n return null;\n}\n\n/**\n * Advance past a string / char literal starting at `content[i]` (a quote char);\n * returns the index just after it. Two correctness rules a naive escape-scan got\n * wrong (both verified as real bugs):\n * - a `'` char literal must close within a short window (`'\\u{10FFFF}'` is ~11\n * chars); otherwise it is a Rust lifetime (`'a`) or a stray apostrophe, NOT a\n * literal — return `i + 1` so the scan continues instead of consuming the\n * rest of the file.\n * - Go raw strings (backtick, when `rawBacktick`) do NOT process escapes, so a\n * trailing backslash (`` `C:\\` ``) must not swallow the closing delimiter.\n */\nfunction skipQuoted(content: string, i: number, n: number, rawBacktick: boolean): number {\n const quote = content[i];\n if (quote === \"'\") {\n const limit = Math.min(n, i + 1 + 12);\n let j = i + 1;\n while (j < limit) {\n if (content[j] === \"\\\\\") {\n j += 2;\n continue;\n }\n if (content[j] === \"'\") return j + 1;\n j++;\n }\n return i + 1; // a lifetime / stray apostrophe, not a char literal\n }\n const escapes = !(quote === \"`\" && rawBacktick);\n let j = i + 1;\n while (j < n) {\n if (escapes && content[j] === \"\\\\\") {\n j += 2;\n continue;\n }\n if (content[j] === quote) return j + 1;\n j++;\n }\n return n;\n}\n\n/**\n * Compute the comment spans (`[start, end)` offsets) of `content`, skipping over\n * string literals so a comment marker inside a string is not treated as a\n * comment. Spans are returned sorted and non-overlapping. `rawBacktick` (Go)\n * makes backtick strings raw (no escapes).\n */\nexport function commentSpans(\n content: string,\n style: CommentStyle,\n rawBacktick = false,\n): Array<[number, number]> {\n const spans: Array<[number, number]> = [];\n const n = content.length;\n let i = 0;\n while (i < n) {\n const c = content[i];\n // String / char literal: skip past it (lexically correct for lifetimes + raw strings).\n if (c === '\"' || c === \"'\" || c === \"`\") {\n i = skipQuoted(content, i, n, rawBacktick);\n continue;\n }\n if (style === \"c\" && c === \"/\" && content[i + 1] === \"/\") {\n const start = i;\n i += 2;\n while (i < n && content[i] !== \"\\n\") i++;\n spans.push([start, i]);\n continue;\n }\n if (style === \"c\" && c === \"/\" && content[i + 1] === \"*\") {\n const start = i;\n i += 2;\n while (i < n && !(content[i] === \"*\" && content[i + 1] === \"/\")) i++;\n i = Math.min(n, i + 2);\n spans.push([start, i]);\n continue;\n }\n if (style === \"hash\" && c === \"#\") {\n const start = i;\n i++;\n while (i < n && content[i] !== \"\\n\") i++;\n spans.push([start, i]);\n continue;\n }\n i++;\n }\n return spans;\n}\n\n/**\n * Compute Python triple-quoted string spans (`\"\"\"…\"\"\"` / `'''…'''`). These are\n * docstrings / prose in practice, so a crypto *name* mentioned inside one\n * (`:param key_type: eg \"ssh-ed25519\"`) should not fire a token finding — but a\n * PEM key pasted into one is still real material, so {@link stripCommentFindings}\n * exempts `pem-*` rules. Comments and normal strings are skipped so a `\"\"\"`\n * delimiter inside them is not mis-detected.\n */\nfunction pythonDocstringSpans(content: string): Array<[number, number]> {\n const spans: Array<[number, number]> = [];\n const n = content.length;\n let i = 0;\n while (i < n) {\n const c = content[i];\n if (c === \"#\") {\n i++;\n while (i < n && content[i] !== \"\\n\") i++;\n continue;\n }\n if ((c === '\"' || c === \"'\") && content[i + 1] === c && content[i + 2] === c) {\n const start = i;\n i += 3;\n while (i < n && !(content[i] === c && content[i + 1] === c && content[i + 2] === c)) i++;\n i = Math.min(n, i + 3);\n spans.push([start, i]);\n continue;\n }\n if (c === '\"' || c === \"'\") {\n const q = c;\n i++;\n while (i < n) {\n if (content[i] === \"\\\\\") {\n i += 2;\n continue;\n }\n if (content[i] === q) {\n i++;\n break;\n }\n i++;\n }\n continue;\n }\n i++;\n }\n return spans;\n}\n\n/** True if `offset` falls inside one of the (sorted, non-overlapping) spans. */\nfunction offsetInSpans(spans: ReadonlyArray<[number, number]>, offset: number): boolean {\n let lo = 0;\n let hi = spans.length - 1;\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1;\n const [s, e] = spans[mid];\n if (offset < s) hi = mid - 1;\n else if (offset >= e) lo = mid + 1;\n else return true;\n }\n return false;\n}\n\n/**\n * Line numbers (1-based) suppressed by an inline ignore directive:\n * - `qscan-ignore-line` on a line suppresses findings on THAT line.\n * - `qscan-ignore-next-line` on a line suppresses findings on the NEXT line.\n * The directive text is matched anywhere on the line (usually in a comment), so\n * it is language-agnostic. `qscan-ignore-line` is not a substring of\n * `qscan-ignore-next-line`, so the two never collide.\n */\nfunction ignoredLines(content: string): Set<number> {\n const ignored = new Set<number>();\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line.includes(\"qscan-ignore-next-line\")) ignored.add(i + 2);\n else if (line.includes(\"qscan-ignore-line\")) ignored.add(i + 1);\n }\n return ignored;\n}\n\n/** Drop findings on lines suppressed by an inline `qscan-ignore` directive. */\nexport function stripIgnoredFindings(findings: Finding[], content: string): Finding[] {\n if (findings.length === 0 || !content.includes(\"qscan-ignore\")) return findings;\n const ignored = ignoredLines(content);\n if (ignored.size === 0) return findings;\n return findings.filter((f) => !ignored.has(f.location.line));\n}\n\n/**\n * Drop findings whose match starts inside a comment. No-op when the file's\n * language has no comment style we handle, or when it has no comments.\n */\nexport function stripCommentFindings(\n findings: Finding[],\n content: string,\n file: string,\n): Finding[] {\n if (findings.length === 0) return findings;\n const style = commentStyleForFile(file);\n if (!style) return findings;\n const spans = commentSpans(content, style, /\\.go$/i.test(file));\n // Python docstrings suppress prose *token* rules but keep real PEM material.\n const docSpans = style === \"hash\" ? pythonDocstringSpans(content) : [];\n if (spans.length === 0 && docSpans.length === 0) return findings;\n\n // 1-based line → start offset, to turn a finding's (line, column) back into an\n // absolute offset (column is 1-based: offset = lineStart + column - 1).\n const lineStarts: number[] = [0];\n for (let i = 0; i < content.length; i++) {\n if (content[i] === \"\\n\") lineStarts.push(i + 1);\n }\n\n return findings.filter((f) => {\n const start = lineStarts[f.location.line - 1] ?? 0;\n const offset = start + ((f.location.column ?? 1) - 1);\n if (offsetInSpans(spans, offset)) return false;\n if (docSpans.length > 0 && !f.ruleId.startsWith(\"pem-\") && offsetInSpans(docSpans, offset)) {\n return false;\n }\n return true;\n });\n}\n\n/**\n * Compute the string-literal spans (`[start, end)`) of `content`, skipping over\n * comments so a quote inside a comment is not treated as a string. Used to\n * suppress findings from IDENTIFIER-only rules (e.g. a Go `SigningMethodRS256`\n * mentioned inside an error-message string) — the mirror of {@link commentSpans}.\n */\nexport function stringSpans(\n content: string,\n style: CommentStyle,\n rawBacktick = false,\n): Array<[number, number]> {\n const spans: Array<[number, number]> = [];\n const n = content.length;\n let i = 0;\n while (i < n) {\n const c = content[i];\n if (style === \"c\" && c === \"/\" && content[i + 1] === \"/\") {\n i += 2;\n while (i < n && content[i] !== \"\\n\") i++;\n continue;\n }\n if (style === \"c\" && c === \"/\" && content[i + 1] === \"*\") {\n i += 2;\n while (i < n && !(content[i] === \"*\" && content[i + 1] === \"/\")) i++;\n i = Math.min(n, i + 2);\n continue;\n }\n if (style === \"hash\" && c === \"#\") {\n i++;\n while (i < n && content[i] !== \"\\n\") i++;\n continue;\n }\n if (c === '\"' || c === \"'\" || c === \"`\") {\n const start = i;\n const end = skipQuoted(content, i, n, rawBacktick);\n // A lone `'` (Rust lifetime, end === start+1) is not a string; skip it.\n if (c !== \"'\" || end > start + 1) spans.push([start, end]);\n i = end;\n continue;\n }\n i++;\n }\n return spans;\n}\n\n/**\n * Drop findings of \"code-only\" rules (`ruleIds`) whose match starts inside a\n * string literal. Some rules — an identifier-form JWT signing method, a Go\n * `SigningMethodRS256` — are only meaningful as code; when the same token appears\n * inside a string (a test's `t.Error(\"SigningMethodPS256 …\")`) it is prose, not a\n * usage. Rules that legitimately match inside strings (quoted `\"RS256\"` alg\n * tokens, cipher-suite strings, ssh-key tokens) are NOT in `ruleIds` and pass\n * through untouched.\n */\nexport function stripStringLiteralFindings(\n findings: Finding[],\n content: string,\n file: string,\n ruleIds: ReadonlySet<string>,\n): Finding[] {\n if (findings.length === 0 || !findings.some((f) => ruleIds.has(f.ruleId))) return findings;\n const style = commentStyleForFile(file);\n if (!style) return findings;\n const spans = stringSpans(content, style, /\\.go$/i.test(file));\n if (spans.length === 0) return findings;\n\n const lineStarts: number[] = [0];\n for (let i = 0; i < content.length; i++) {\n if (content[i] === \"\\n\") lineStarts.push(i + 1);\n }\n\n return findings.filter((f) => {\n if (!ruleIds.has(f.ruleId)) return true;\n const start = lineStarts[f.location.line - 1] ?? 0;\n const offset = start + ((f.location.column ?? 1) - 1);\n return !offsetInSpans(spans, offset);\n });\n}\n"]}
|
package/dist/detect-utils.d.ts
CHANGED
|
@@ -4,21 +4,8 @@
|
|
|
4
4
|
* small factory for building Finding objects with consistent remediation text.
|
|
5
5
|
*/
|
|
6
6
|
import type { AlgorithmFamily, Confidence, Finding, FindingCategory, RuleMeta, Severity } from "./types.js";
|
|
7
|
-
/** A 1-based line/column position derived from a character offset. */
|
|
8
|
-
export interface LineCol {
|
|
9
|
-
line: number;
|
|
10
|
-
column: number;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Convert a 0-based character offset within `content` into a 1-based
|
|
14
|
-
* line/column. Newlines are LF; CR is treated as an ordinary character, so on
|
|
15
|
-
* CRLF files the column includes the trailing CR offset harmlessly.
|
|
16
|
-
*/
|
|
17
|
-
export declare function offsetToLineCol(content: string, offset: number): LineCol;
|
|
18
|
-
/** Extract the (trimmed) single source line containing `offset`. */
|
|
19
|
-
export declare function lineAt(content: string, offset: number): string;
|
|
20
7
|
/** Inputs for {@link makeFinding}. */
|
|
21
|
-
|
|
8
|
+
interface FindingSpec {
|
|
22
9
|
ruleId: string;
|
|
23
10
|
title: string;
|
|
24
11
|
category: FindingCategory;
|
|
@@ -47,7 +34,7 @@ export interface FindingSpec {
|
|
|
47
34
|
*/
|
|
48
35
|
export declare function makeFinding(spec: FindingSpec): Finding;
|
|
49
36
|
/** Where a match occurred, plus optional per-finding field overrides. */
|
|
50
|
-
|
|
37
|
+
interface RuleMatch {
|
|
51
38
|
file: string;
|
|
52
39
|
content: string;
|
|
53
40
|
/** Match start offset within `content`. */
|
|
@@ -60,7 +47,7 @@ export interface RuleMatch {
|
|
|
60
47
|
* rules (e.g. key generation across algorithm families) override these; fixed
|
|
61
48
|
* rules pass none and inherit the catalog metadata verbatim.
|
|
62
49
|
*/
|
|
63
|
-
|
|
50
|
+
type RuleOverrides = Partial<Pick<RuleMeta, "title" | "category" | "severity" | "confidence" | "algorithm" | "hndl" | "message" | "cwe">> & {
|
|
64
51
|
remediation?: string;
|
|
65
52
|
};
|
|
66
53
|
/**
|
|
@@ -97,6 +84,14 @@ export declare const ELIXIR_EXTENSIONS: readonly string[];
|
|
|
97
84
|
export declare const PHP_EXTENSIONS: readonly string[];
|
|
98
85
|
/** C / C++ source extensions handled by the OpenSSL detector. */
|
|
99
86
|
export declare const C_EXTENSIONS: readonly string[];
|
|
87
|
+
/** Swift source extensions handled by the CryptoKit / Security-framework detector. */
|
|
88
|
+
export declare const SWIFT_EXTENSIONS: readonly string[];
|
|
89
|
+
/** Objective-C implementation extensions (`.h` is owned by the C pack). */
|
|
90
|
+
export declare const OBJC_EXTENSIONS: readonly string[];
|
|
91
|
+
/** Dart / Flutter source extensions. */
|
|
92
|
+
export declare const DART_EXTENSIONS: readonly string[];
|
|
93
|
+
/** Smart-contract source extensions (Solidity/EVM, Move, Cairo/Starknet). */
|
|
94
|
+
export declare const SMART_CONTRACT_EXTENSIONS: readonly string[];
|
|
100
95
|
/**
|
|
101
96
|
* Prose/documentation extensions. The language-agnostic *token* detectors (SSH
|
|
102
97
|
* public keys, TLS cipher suites, certificate signature algorithms) must not run
|
|
@@ -130,14 +125,17 @@ export declare const ANALYZABLE_SOURCE_EXTENSIONS: readonly string[];
|
|
|
130
125
|
* crypto, shown in coverage output. Kept next to {@link
|
|
131
126
|
* ANALYZABLE_SOURCE_EXTENSIONS} so a new language pack updates one place.
|
|
132
127
|
*/
|
|
133
|
-
export declare const ANALYZABLE_LANGUAGES_LABEL = "JS/TS, Python, Go, Java/Kotlin/Scala, C#, Rust, Ruby, PHP, Elixir, C/C
|
|
128
|
+
export declare const ANALYZABLE_LANGUAGES_LABEL = "JS/TS, Python, Go, Java/Kotlin/Scala, C#, Rust, Ruby, PHP, Elixir, C/C++, Swift, Objective-C, Dart, Solidity/Move/Cairo";
|
|
134
129
|
/** True when a path is in a source language the scanner can analyze for crypto. */
|
|
135
130
|
export declare function isAnalyzableSource(filePath: string): boolean;
|
|
136
131
|
/**
|
|
137
|
-
* Given a SORTED ascending array of call offsets, return true when `idx` is at
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
132
|
+
* Given a SORTED ascending array of call offsets, return true when `idx` is at or
|
|
133
|
+
* after some call offset `c` with `idx - c < window`. Deliberately ONE-SIDED
|
|
134
|
+
* (forward only): the WebCrypto algorithm name follows the `subtle.*(` call it
|
|
135
|
+
* belongs to, and reaching backward would wrongly attribute an UNRELATED earlier
|
|
136
|
+
* call's argument (e.g. a Node `generateKeyPairSync('rsa-pss', …)` sitting a few
|
|
137
|
+
* lines above a subtle call) to the wrong API. Runs in O(log n) by binary-searching
|
|
138
|
+
* the largest call offset ≤ `idx` and checking the gap.
|
|
141
139
|
*/
|
|
142
140
|
export declare function nearSortedCall(sortedCalls: readonly number[], idx: number, window: number): boolean;
|
|
143
141
|
/**
|
|
@@ -145,4 +143,40 @@ export declare function nearSortedCall(sortedCalls: readonly number[], idx: numb
|
|
|
145
143
|
* lastIndex and guards against zero-width matches (which would loop forever).
|
|
146
144
|
*/
|
|
147
145
|
export declare function eachMatch(re: RegExp, content: string, onMatch: (match: RegExpExecArray) => void): void;
|
|
146
|
+
/**
|
|
147
|
+
* Return the innermost `{ … }` object that contains `index`, as a substring — so a
|
|
148
|
+
* per-match check (e.g. a JWK's own `use`/`alg`, or whether an `alg` sits inside a
|
|
149
|
+
* JWK) analyses ONLY that object and can't be contaminated by a neighbouring object
|
|
150
|
+
* in a packed array. The scan is STRING-AWARE (braces and quotes inside JSON string
|
|
151
|
+
* values, honouring `\"`, are ignored), so a value like `"a}b{c"` can't mis-scope the
|
|
152
|
+
* object. Bounded by `maxSpan` each way so a pathological input can't blow up; falls
|
|
153
|
+
* back to a bounded ±window when no enclosing object is found in range.
|
|
154
|
+
*/
|
|
155
|
+
export declare function enclosingObject(content: string, index: number, maxSpan?: number): string;
|
|
156
|
+
/**
|
|
157
|
+
* Blank out FULL-LINE comments so a commented-out directive isn't reported as an
|
|
158
|
+
* active setting. A line counts as a comment when its first non-whitespace characters
|
|
159
|
+
* match one of `markers` (e.g. `#`, `//`, `;`, `!`). Each such line's characters are
|
|
160
|
+
* replaced with spaces of the SAME length (newlines preserved), so every byte offset
|
|
161
|
+
* is unchanged — the finding line/column/snippet for the non-comment lines that remain
|
|
162
|
+
* stay exactly correct. Inline trailing comments are intentionally left alone (the
|
|
163
|
+
* directive before them is still active, so it should still be flagged).
|
|
164
|
+
*
|
|
165
|
+
* Config detectors run their rules over the masked content; matches can then only land
|
|
166
|
+
* on live config, not on commented examples (mosquitto.conf, CI YAML, ipsec.conf, and
|
|
167
|
+
* HCL all conventionally ship large blocks of commented-out directives).
|
|
168
|
+
*/
|
|
169
|
+
export declare function maskCommentLines(content: string, markers: readonly string[]): string;
|
|
170
|
+
/**
|
|
171
|
+
* Blank out C-style block comments (the `slash-star … star-slash` form, multi-line
|
|
172
|
+
* included), replacing every
|
|
173
|
+
* character with a space and preserving newlines so byte offsets — and therefore
|
|
174
|
+
* finding line/column — stay exact. Complements {@link maskCommentLines}, which only
|
|
175
|
+
* masks whole single-line comments: an HCL/Bicep resource wrapped in a block comment
|
|
176
|
+
* has its inner lines mid-block (they don't START with `/*`), so a line-only masker
|
|
177
|
+
* leaves them live. Not string-aware — a literal `/*` inside a config string value is
|
|
178
|
+
* rare and, for these token detectors, a tolerable over-mask.
|
|
179
|
+
*/
|
|
180
|
+
export declare function maskBlockComments(content: string): string;
|
|
181
|
+
export {};
|
|
148
182
|
//# sourceMappingURL=detect-utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-utils.d.ts","sourceRoot":"","sources":["../src/detect-utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EACV,OAAO,EACP,eAAe,EACf,QAAQ,EACR,QAAQ,EACT,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"detect-utils.d.ts","sourceRoot":"","sources":["../src/detect-utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EACV,OAAO,EACP,eAAe,EACf,QAAQ,EACR,QAAQ,EACT,MAAM,YAAY,CAAC;AAqEpB,sCAAsC;AACtC,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAmCtD;AAED,yEAAyE;AACzE,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,KAAK,aAAa,GAAG,OAAO,CAC1B,IAAI,CACF,QAAQ,EACR,OAAO,GAAG,UAAU,GAAG,UAAU,GAAG,YAAY,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,CAC5F,CACF,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7B;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,aAAa,GAAG,OAAO,CAkBjG;AAED,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAG/E;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAS7C,CAAC;AAEF,+DAA+D;AAC/D,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAA4B,CAAC;AAE5E,uDAAuD;AACvD,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAAY,CAAC;AAExD,mEAAmE;AAInE,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAA8C,CAAC;AAE5F,yDAAyD;AACzD,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAAY,CAAC;AAE5D,2DAA2D;AAC3D,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAAY,CAAC;AAE1D,2DAA2D;AAC3D,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAAY,CAAC;AAE1D,+DAA+D;AAC/D,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAAoB,CAAC;AAEpE,yDAAyD;AACzD,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAAkD,CAAC;AAE/F,iEAAiE;AACjE,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAAuD,CAAC;AAElG,sFAAsF;AACtF,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAAe,CAAC;AAE9D,2EAA2E;AAC3E,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAAkB,CAAC;AAEhE,wCAAwC;AACxC,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAAc,CAAC;AAE5D,6EAA6E;AAC7E,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EAAgC,CAAC;AAExF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAY3C,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAQhD,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,EAAE,SAAS,MAAM,EAezD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,4HACoF,CAAC;AAE5H,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,OAAO,CAgBT;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,GACxC,IAAI,CAQN;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,SAAO,GAAG,MAAM,CAkCtF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAWpF;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAiBzD"}
|
package/dist/detect-utils.js
CHANGED
|
@@ -43,13 +43,13 @@ function lineIndexFor(starts, offset) {
|
|
|
43
43
|
* line/column. Newlines are LF; CR is treated as an ordinary character, so on
|
|
44
44
|
* CRLF files the column includes the trailing CR offset harmlessly.
|
|
45
45
|
*/
|
|
46
|
-
|
|
46
|
+
function offsetToLineCol(content, offset) {
|
|
47
47
|
const starts = lineStartsFor(content);
|
|
48
48
|
const idx = lineIndexFor(starts, offset);
|
|
49
49
|
return { line: idx + 1, column: offset - starts[idx] + 1 };
|
|
50
50
|
}
|
|
51
51
|
/** Extract the (trimmed) single source line containing `offset`. */
|
|
52
|
-
|
|
52
|
+
function lineAt(content, offset) {
|
|
53
53
|
const starts = lineStartsFor(content);
|
|
54
54
|
const idx = lineIndexFor(starts, offset);
|
|
55
55
|
const start = starts[idx];
|
|
@@ -166,6 +166,14 @@ export const ELIXIR_EXTENSIONS = [".ex", ".exs"];
|
|
|
166
166
|
export const PHP_EXTENSIONS = [".php", ".phtml", ".php3", ".php4", ".php5"];
|
|
167
167
|
/** C / C++ source extensions handled by the OpenSSL detector. */
|
|
168
168
|
export const C_EXTENSIONS = [".c", ".h", ".cc", ".cpp", ".cxx", ".hpp", ".hh"];
|
|
169
|
+
/** Swift source extensions handled by the CryptoKit / Security-framework detector. */
|
|
170
|
+
export const SWIFT_EXTENSIONS = [".swift"];
|
|
171
|
+
/** Objective-C implementation extensions (`.h` is owned by the C pack). */
|
|
172
|
+
export const OBJC_EXTENSIONS = [".m", ".mm"];
|
|
173
|
+
/** Dart / Flutter source extensions. */
|
|
174
|
+
export const DART_EXTENSIONS = [".dart"];
|
|
175
|
+
/** Smart-contract source extensions (Solidity/EVM, Move, Cairo/Starknet). */
|
|
176
|
+
export const SMART_CONTRACT_EXTENSIONS = [".sol", ".move", ".cairo"];
|
|
169
177
|
/**
|
|
170
178
|
* Prose/documentation extensions. The language-agnostic *token* detectors (SSH
|
|
171
179
|
* public keys, TLS cipher suites, certificate signature algorithms) must not run
|
|
@@ -201,6 +209,9 @@ export const JWT_HOST_EXTENSIONS = [
|
|
|
201
209
|
...PYTHON_EXTENSIONS,
|
|
202
210
|
...GO_EXTENSIONS,
|
|
203
211
|
...RUBY_EXTENSIONS,
|
|
212
|
+
// PHP's firebase/php-jwt passes the alg as a quoted token (`'RS256'`, `'ES256'`),
|
|
213
|
+
// the same shape RE_JWT_ALG matches — so PHP files are in scope for it too.
|
|
214
|
+
...PHP_EXTENSIONS,
|
|
204
215
|
];
|
|
205
216
|
/**
|
|
206
217
|
* Extensions the scanner can actually analyze for inline crypto usage today
|
|
@@ -221,22 +232,29 @@ export const ANALYZABLE_SOURCE_EXTENSIONS = [
|
|
|
221
232
|
...PHP_EXTENSIONS,
|
|
222
233
|
...ELIXIR_EXTENSIONS,
|
|
223
234
|
...C_EXTENSIONS,
|
|
235
|
+
...SWIFT_EXTENSIONS,
|
|
236
|
+
...OBJC_EXTENSIONS,
|
|
237
|
+
...DART_EXTENSIONS,
|
|
238
|
+
...SMART_CONTRACT_EXTENSIONS,
|
|
224
239
|
];
|
|
225
240
|
/**
|
|
226
241
|
* Human label for the source languages the scanner can analyze for inline
|
|
227
242
|
* crypto, shown in coverage output. Kept next to {@link
|
|
228
243
|
* ANALYZABLE_SOURCE_EXTENSIONS} so a new language pack updates one place.
|
|
229
244
|
*/
|
|
230
|
-
export const ANALYZABLE_LANGUAGES_LABEL = "JS/TS, Python, Go, Java/Kotlin/Scala, C#, Rust, Ruby, PHP, Elixir, C/C
|
|
245
|
+
export const ANALYZABLE_LANGUAGES_LABEL = "JS/TS, Python, Go, Java/Kotlin/Scala, C#, Rust, Ruby, PHP, Elixir, C/C++, Swift, Objective-C, Dart, Solidity/Move/Cairo";
|
|
231
246
|
/** True when a path is in a source language the scanner can analyze for crypto. */
|
|
232
247
|
export function isAnalyzableSource(filePath) {
|
|
233
248
|
return hasExtension(filePath, ANALYZABLE_SOURCE_EXTENSIONS);
|
|
234
249
|
}
|
|
235
250
|
/**
|
|
236
|
-
* Given a SORTED ascending array of call offsets, return true when `idx` is at
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
251
|
+
* Given a SORTED ascending array of call offsets, return true when `idx` is at or
|
|
252
|
+
* after some call offset `c` with `idx - c < window`. Deliberately ONE-SIDED
|
|
253
|
+
* (forward only): the WebCrypto algorithm name follows the `subtle.*(` call it
|
|
254
|
+
* belongs to, and reaching backward would wrongly attribute an UNRELATED earlier
|
|
255
|
+
* call's argument (e.g. a Node `generateKeyPairSync('rsa-pss', …)` sitting a few
|
|
256
|
+
* lines above a subtle call) to the wrong API. Runs in O(log n) by binary-searching
|
|
257
|
+
* the largest call offset ≤ `idx` and checking the gap.
|
|
240
258
|
*/
|
|
241
259
|
export function nearSortedCall(sortedCalls, idx, window) {
|
|
242
260
|
// Find the rightmost element <= idx.
|
|
@@ -271,4 +289,112 @@ export function eachMatch(re, content, onMatch) {
|
|
|
271
289
|
g.lastIndex++; // avoid infinite loop on empty match
|
|
272
290
|
}
|
|
273
291
|
}
|
|
292
|
+
/**
|
|
293
|
+
* Return the innermost `{ … }` object that contains `index`, as a substring — so a
|
|
294
|
+
* per-match check (e.g. a JWK's own `use`/`alg`, or whether an `alg` sits inside a
|
|
295
|
+
* JWK) analyses ONLY that object and can't be contaminated by a neighbouring object
|
|
296
|
+
* in a packed array. The scan is STRING-AWARE (braces and quotes inside JSON string
|
|
297
|
+
* values, honouring `\"`, are ignored), so a value like `"a}b{c"` can't mis-scope the
|
|
298
|
+
* object. Bounded by `maxSpan` each way so a pathological input can't blow up; falls
|
|
299
|
+
* back to a bounded ±window when no enclosing object is found in range.
|
|
300
|
+
*/
|
|
301
|
+
export function enclosingObject(content, index, maxSpan = 4000) {
|
|
302
|
+
const lo = Math.max(0, index - maxSpan);
|
|
303
|
+
const hi = Math.min(content.length, index + maxSpan);
|
|
304
|
+
const stack = []; // positions of currently-open `{`
|
|
305
|
+
let inStr = false;
|
|
306
|
+
let esc = false;
|
|
307
|
+
let enclosingOpen = -1; // innermost `{` open at `index`
|
|
308
|
+
for (let i = lo; i < hi; i++) {
|
|
309
|
+
const c = content[i];
|
|
310
|
+
if (inStr) {
|
|
311
|
+
if (esc)
|
|
312
|
+
esc = false;
|
|
313
|
+
else if (c === "\\")
|
|
314
|
+
esc = true;
|
|
315
|
+
else if (c === '"')
|
|
316
|
+
inStr = false;
|
|
317
|
+
}
|
|
318
|
+
else if (c === '"') {
|
|
319
|
+
inStr = true;
|
|
320
|
+
}
|
|
321
|
+
else if (c === "{") {
|
|
322
|
+
stack.push(i);
|
|
323
|
+
}
|
|
324
|
+
else if (c === "}") {
|
|
325
|
+
const open = stack.pop();
|
|
326
|
+
if (open !== undefined && open === enclosingOpen) {
|
|
327
|
+
return content.slice(enclosingOpen, i + 1); // closed the enclosing object
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (i === index && enclosingOpen < 0 && stack.length > 0) {
|
|
331
|
+
enclosingOpen = stack[stack.length - 1];
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// No complete enclosing object in range: if we found an (unclosed) enclosing open
|
|
335
|
+
// brace, return from it to hi. Otherwise the token is not inside any object (top-level
|
|
336
|
+
// token / brace-less config like YAML) — return a bounded ±250 window, NOT the full
|
|
337
|
+
// forward span, so a `"kty"` far AFTER the token can't pull an unrelated JWK into
|
|
338
|
+
// scope and over-suppress a legitimate standalone finding.
|
|
339
|
+
if (enclosingOpen >= 0)
|
|
340
|
+
return content.slice(enclosingOpen, hi);
|
|
341
|
+
return content.slice(Math.max(0, index - 250), Math.min(content.length, index + 250));
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Blank out FULL-LINE comments so a commented-out directive isn't reported as an
|
|
345
|
+
* active setting. A line counts as a comment when its first non-whitespace characters
|
|
346
|
+
* match one of `markers` (e.g. `#`, `//`, `;`, `!`). Each such line's characters are
|
|
347
|
+
* replaced with spaces of the SAME length (newlines preserved), so every byte offset
|
|
348
|
+
* is unchanged — the finding line/column/snippet for the non-comment lines that remain
|
|
349
|
+
* stay exactly correct. Inline trailing comments are intentionally left alone (the
|
|
350
|
+
* directive before them is still active, so it should still be flagged).
|
|
351
|
+
*
|
|
352
|
+
* Config detectors run their rules over the masked content; matches can then only land
|
|
353
|
+
* on live config, not on commented examples (mosquitto.conf, CI YAML, ipsec.conf, and
|
|
354
|
+
* HCL all conventionally ship large blocks of commented-out directives).
|
|
355
|
+
*/
|
|
356
|
+
export function maskCommentLines(content, markers) {
|
|
357
|
+
if (markers.length === 0)
|
|
358
|
+
return content;
|
|
359
|
+
const lines = content.split("\n");
|
|
360
|
+
for (let i = 0; i < lines.length; i++) {
|
|
361
|
+
const line = lines[i];
|
|
362
|
+
const lead = line.trimStart();
|
|
363
|
+
if (lead !== "" && markers.some((mk) => lead.startsWith(mk))) {
|
|
364
|
+
lines[i] = " ".repeat(line.length);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return lines.join("\n");
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Blank out C-style block comments (the `slash-star … star-slash` form, multi-line
|
|
371
|
+
* included), replacing every
|
|
372
|
+
* character with a space and preserving newlines so byte offsets — and therefore
|
|
373
|
+
* finding line/column — stay exact. Complements {@link maskCommentLines}, which only
|
|
374
|
+
* masks whole single-line comments: an HCL/Bicep resource wrapped in a block comment
|
|
375
|
+
* has its inner lines mid-block (they don't START with `/*`), so a line-only masker
|
|
376
|
+
* leaves them live. Not string-aware — a literal `/*` inside a config string value is
|
|
377
|
+
* rare and, for these token detectors, a tolerable over-mask.
|
|
378
|
+
*/
|
|
379
|
+
export function maskBlockComments(content) {
|
|
380
|
+
if (!content.includes("/*"))
|
|
381
|
+
return content;
|
|
382
|
+
const out = [];
|
|
383
|
+
let i = 0;
|
|
384
|
+
const n = content.length;
|
|
385
|
+
while (i < n) {
|
|
386
|
+
if (content[i] === "/" && content[i + 1] === "*") {
|
|
387
|
+
const end = content.indexOf("*/", i + 2);
|
|
388
|
+
const stop = end === -1 ? n : end + 2;
|
|
389
|
+
for (let j = i; j < stop; j++)
|
|
390
|
+
out.push(content[j] === "\n" ? "\n" : " ");
|
|
391
|
+
i = stop;
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
out.push(content[i]);
|
|
395
|
+
i++;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return out.join("");
|
|
399
|
+
}
|
|
274
400
|
//# sourceMappingURL=detect-utils.js.map
|