@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.
Files changed (284) hide show
  1. package/README.md +76 -27
  2. package/dist/cbom-merge.d.ts.map +1 -1
  3. package/dist/cbom-merge.js +30 -8
  4. package/dist/cbom-merge.js.map +1 -1
  5. package/dist/cbom.d.ts +5 -0
  6. package/dist/cbom.d.ts.map +1 -1
  7. package/dist/cbom.js +132 -37
  8. package/dist/cbom.js.map +1 -1
  9. package/dist/comments.d.ts +2 -23
  10. package/dist/comments.d.ts.map +1 -1
  11. package/dist/comments.js +24 -5
  12. package/dist/comments.js.map +1 -1
  13. package/dist/detect-utils.d.ts +55 -21
  14. package/dist/detect-utils.d.ts.map +1 -1
  15. package/dist/detect-utils.js +133 -7
  16. package/dist/detect-utils.js.map +1 -1
  17. package/dist/detectors/age.d.ts +16 -0
  18. package/dist/detectors/age.d.ts.map +1 -0
  19. package/dist/detectors/age.js +40 -0
  20. package/dist/detectors/age.js.map +1 -0
  21. package/dist/detectors/ansible.d.ts +13 -0
  22. package/dist/detectors/ansible.d.ts.map +1 -0
  23. package/dist/detectors/ansible.js +108 -0
  24. package/dist/detectors/ansible.js.map +1 -0
  25. package/dist/detectors/bicep.d.ts +20 -0
  26. package/dist/detectors/bicep.d.ts.map +1 -0
  27. package/dist/detectors/bicep.js +74 -0
  28. package/dist/detectors/bicep.js.map +1 -0
  29. package/dist/detectors/c.d.ts.map +1 -1
  30. package/dist/detectors/c.js +30 -3
  31. package/dist/detectors/c.js.map +1 -1
  32. package/dist/detectors/cicd.d.ts.map +1 -1
  33. package/dist/detectors/cicd.js +18 -5
  34. package/dist/detectors/cicd.js.map +1 -1
  35. package/dist/detectors/cloud-kms.d.ts +8 -5
  36. package/dist/detectors/cloud-kms.d.ts.map +1 -1
  37. package/dist/detectors/cloud-kms.js +62 -20
  38. package/dist/detectors/cloud-kms.js.map +1 -1
  39. package/dist/detectors/cloudformation.d.ts +8 -0
  40. package/dist/detectors/cloudformation.d.ts.map +1 -1
  41. package/dist/detectors/cloudformation.js +41 -10
  42. package/dist/detectors/cloudformation.js.map +1 -1
  43. package/dist/detectors/codesign.d.ts +50 -0
  44. package/dist/detectors/codesign.d.ts.map +1 -0
  45. package/dist/detectors/codesign.js +143 -0
  46. package/dist/detectors/codesign.js.map +1 -0
  47. package/dist/detectors/csharp.d.ts.map +1 -1
  48. package/dist/detectors/csharp.js +13 -3
  49. package/dist/detectors/csharp.js.map +1 -1
  50. package/dist/detectors/dart.d.ts +35 -0
  51. package/dist/detectors/dart.d.ts.map +1 -0
  52. package/dist/detectors/dart.js +177 -0
  53. package/dist/detectors/dart.js.map +1 -0
  54. package/dist/detectors/database.d.ts +5 -3
  55. package/dist/detectors/database.d.ts.map +1 -1
  56. package/dist/detectors/database.js +45 -8
  57. package/dist/detectors/database.js.map +1 -1
  58. package/dist/detectors/dkim.d.ts +54 -0
  59. package/dist/detectors/dkim.d.ts.map +1 -0
  60. package/dist/detectors/dkim.js +92 -0
  61. package/dist/detectors/dkim.js.map +1 -0
  62. package/dist/detectors/dnssec.d.ts.map +1 -1
  63. package/dist/detectors/dnssec.js +19 -10
  64. package/dist/detectors/dnssec.js.map +1 -1
  65. package/dist/detectors/elixir.d.ts.map +1 -1
  66. package/dist/detectors/elixir.js +34 -2
  67. package/dist/detectors/elixir.js.map +1 -1
  68. package/dist/detectors/java.d.ts.map +1 -1
  69. package/dist/detectors/java.js +33 -3
  70. package/dist/detectors/java.js.map +1 -1
  71. package/dist/detectors/jose.d.ts.map +1 -1
  72. package/dist/detectors/jose.js +13 -3
  73. package/dist/detectors/jose.js.map +1 -1
  74. package/dist/detectors/jwk.d.ts +7 -7
  75. package/dist/detectors/jwk.d.ts.map +1 -1
  76. package/dist/detectors/jwk.js +44 -8
  77. package/dist/detectors/jwk.js.map +1 -1
  78. package/dist/detectors/k8s.d.ts.map +1 -1
  79. package/dist/detectors/k8s.js +5 -2
  80. package/dist/detectors/k8s.js.map +1 -1
  81. package/dist/detectors/keystore.d.ts +19 -0
  82. package/dist/detectors/keystore.d.ts.map +1 -0
  83. package/dist/detectors/keystore.js +76 -0
  84. package/dist/detectors/keystore.js.map +1 -0
  85. package/dist/detectors/mesh.d.ts +7 -8
  86. package/dist/detectors/mesh.d.ts.map +1 -1
  87. package/dist/detectors/mesh.js +17 -35
  88. package/dist/detectors/mesh.js.map +1 -1
  89. package/dist/detectors/messaging.d.ts +2 -2
  90. package/dist/detectors/messaging.d.ts.map +1 -1
  91. package/dist/detectors/messaging.js +19 -12
  92. package/dist/detectors/messaging.js.map +1 -1
  93. package/dist/detectors/objc.d.ts +41 -0
  94. package/dist/detectors/objc.d.ts.map +1 -0
  95. package/dist/detectors/objc.js +146 -0
  96. package/dist/detectors/objc.js.map +1 -0
  97. package/dist/detectors/openpgp.d.ts +20 -0
  98. package/dist/detectors/openpgp.d.ts.map +1 -0
  99. package/dist/detectors/openpgp.js +151 -0
  100. package/dist/detectors/openpgp.js.map +1 -0
  101. package/dist/detectors/pem.d.ts.map +1 -1
  102. package/dist/detectors/pem.js +32 -0
  103. package/dist/detectors/pem.js.map +1 -1
  104. package/dist/detectors/php.d.ts +2 -1
  105. package/dist/detectors/php.d.ts.map +1 -1
  106. package/dist/detectors/php.js +7 -3
  107. package/dist/detectors/php.js.map +1 -1
  108. package/dist/detectors/pkcs11.d.ts +17 -0
  109. package/dist/detectors/pkcs11.d.ts.map +1 -0
  110. package/dist/detectors/pkcs11.js +87 -0
  111. package/dist/detectors/pkcs11.js.map +1 -0
  112. package/dist/detectors/proxy.d.ts +52 -0
  113. package/dist/detectors/proxy.d.ts.map +1 -0
  114. package/dist/detectors/proxy.js +157 -0
  115. package/dist/detectors/proxy.js.map +1 -0
  116. package/dist/detectors/pulumi.d.ts +19 -0
  117. package/dist/detectors/pulumi.d.ts.map +1 -0
  118. package/dist/detectors/pulumi.js +80 -0
  119. package/dist/detectors/pulumi.js.map +1 -0
  120. package/dist/detectors/rust.d.ts.map +1 -1
  121. package/dist/detectors/rust.js +5 -0
  122. package/dist/detectors/rust.js.map +1 -1
  123. package/dist/detectors/secrets.d.ts +4 -1
  124. package/dist/detectors/secrets.d.ts.map +1 -1
  125. package/dist/detectors/secrets.js +5 -21
  126. package/dist/detectors/secrets.js.map +1 -1
  127. package/dist/detectors/solidity.d.ts +50 -0
  128. package/dist/detectors/solidity.d.ts.map +1 -0
  129. package/dist/detectors/solidity.js +108 -0
  130. package/dist/detectors/solidity.js.map +1 -0
  131. package/dist/detectors/source.d.ts.map +1 -1
  132. package/dist/detectors/source.js +53 -6
  133. package/dist/detectors/source.js.map +1 -1
  134. package/dist/detectors/spire.d.ts +49 -0
  135. package/dist/detectors/spire.d.ts.map +1 -0
  136. package/dist/detectors/spire.js +77 -0
  137. package/dist/detectors/spire.js.map +1 -0
  138. package/dist/detectors/ssh-ca.d.ts +72 -0
  139. package/dist/detectors/ssh-ca.d.ts.map +1 -0
  140. package/dist/detectors/ssh-ca.js +119 -0
  141. package/dist/detectors/ssh-ca.js.map +1 -0
  142. package/dist/detectors/supply-chain.d.ts +17 -0
  143. package/dist/detectors/supply-chain.d.ts.map +1 -0
  144. package/dist/detectors/supply-chain.js +89 -0
  145. package/dist/detectors/supply-chain.js.map +1 -0
  146. package/dist/detectors/swift.d.ts +22 -0
  147. package/dist/detectors/swift.d.ts.map +1 -0
  148. package/dist/detectors/swift.js +120 -0
  149. package/dist/detectors/swift.js.map +1 -0
  150. package/dist/detectors/terraform.d.ts.map +1 -1
  151. package/dist/detectors/terraform.js +35 -8
  152. package/dist/detectors/terraform.js.map +1 -1
  153. package/dist/detectors/vault.d.ts +15 -0
  154. package/dist/detectors/vault.d.ts.map +1 -0
  155. package/dist/detectors/vault.js +97 -0
  156. package/dist/detectors/vault.js.map +1 -0
  157. package/dist/detectors/vpn.d.ts +8 -8
  158. package/dist/detectors/vpn.d.ts.map +1 -1
  159. package/dist/detectors/vpn.js +12 -38
  160. package/dist/detectors/vpn.js.map +1 -1
  161. package/dist/detectors/weak-hash.d.ts +71 -0
  162. package/dist/detectors/weak-hash.d.ts.map +1 -0
  163. package/dist/detectors/weak-hash.js +116 -0
  164. package/dist/detectors/weak-hash.js.map +1 -0
  165. package/dist/detectors/webauthn.d.ts +61 -0
  166. package/dist/detectors/webauthn.d.ts.map +1 -0
  167. package/dist/detectors/webauthn.js +109 -0
  168. package/dist/detectors/webauthn.js.map +1 -0
  169. package/dist/detectors/xmldsig.d.ts +23 -0
  170. package/dist/detectors/xmldsig.d.ts.map +1 -0
  171. package/dist/detectors/xmldsig.js +84 -0
  172. package/dist/detectors/xmldsig.js.map +1 -0
  173. package/dist/evidence.d.ts +74 -4
  174. package/dist/evidence.d.ts.map +1 -1
  175. package/dist/evidence.js +71 -0
  176. package/dist/evidence.js.map +1 -1
  177. package/dist/index.d.ts +14 -11
  178. package/dist/index.d.ts.map +1 -1
  179. package/dist/index.js +17 -11
  180. package/dist/index.js.map +1 -1
  181. package/dist/inventory.d.ts +0 -2
  182. package/dist/inventory.d.ts.map +1 -1
  183. package/dist/inventory.js +15 -6
  184. package/dist/inventory.js.map +1 -1
  185. package/dist/registry.d.ts +9 -4
  186. package/dist/registry.d.ts.map +1 -1
  187. package/dist/registry.js +51 -4
  188. package/dist/registry.js.map +1 -1
  189. package/dist/remediate-pipeline.d.ts +0 -3
  190. package/dist/remediate-pipeline.d.ts.map +1 -1
  191. package/dist/remediate-pipeline.js +1 -1
  192. package/dist/remediate-pipeline.js.map +1 -1
  193. package/dist/remediation.d.ts +10 -0
  194. package/dist/remediation.d.ts.map +1 -1
  195. package/dist/remediation.js +32 -0
  196. package/dist/remediation.js.map +1 -1
  197. package/dist/report.d.ts +9 -0
  198. package/dist/report.d.ts.map +1 -1
  199. package/dist/report.js +31 -1
  200. package/dist/report.js.map +1 -1
  201. package/dist/scan-worker.js +5 -3
  202. package/dist/scan-worker.js.map +1 -1
  203. package/dist/scan.d.ts.map +1 -1
  204. package/dist/scan.js +35 -6
  205. package/dist/scan.js.map +1 -1
  206. package/dist/standards-profiles.d.ts +58 -0
  207. package/dist/standards-profiles.d.ts.map +1 -0
  208. package/dist/standards-profiles.js +100 -0
  209. package/dist/standards-profiles.js.map +1 -0
  210. package/dist/types.d.ts +6 -5
  211. package/dist/types.d.ts.map +1 -1
  212. package/dist/types.js.map +1 -1
  213. package/dist/version.d.ts +1 -1
  214. package/dist/version.js +1 -1
  215. package/dist/version.js.map +1 -1
  216. package/dist/vex.d.ts +39 -0
  217. package/dist/vex.d.ts.map +1 -0
  218. package/dist/vex.js +87 -0
  219. package/dist/vex.js.map +1 -0
  220. package/dist/walk.d.ts +8 -3
  221. package/dist/walk.d.ts.map +1 -1
  222. package/dist/walk.js +32 -1
  223. package/dist/walk.js.map +1 -1
  224. package/package.json +1 -1
  225. package/src/cbom-merge.ts +38 -8
  226. package/src/cbom.ts +153 -39
  227. package/src/comments.ts +25 -6
  228. package/src/detect-utils.ts +133 -11
  229. package/src/detectors/age.ts +60 -0
  230. package/src/detectors/ansible.ts +140 -0
  231. package/src/detectors/bicep.ts +107 -0
  232. package/src/detectors/c.ts +31 -3
  233. package/src/detectors/cicd.ts +18 -5
  234. package/src/detectors/cloud-kms.ts +79 -25
  235. package/src/detectors/cloudformation.ts +45 -10
  236. package/src/detectors/codesign.ts +214 -0
  237. package/src/detectors/csharp.ts +13 -3
  238. package/src/detectors/dart.ts +237 -0
  239. package/src/detectors/database.ts +64 -11
  240. package/src/detectors/dkim.ts +172 -0
  241. package/src/detectors/dnssec.ts +19 -10
  242. package/src/detectors/elixir.ts +33 -2
  243. package/src/detectors/java.ts +37 -3
  244. package/src/detectors/jose.ts +19 -3
  245. package/src/detectors/jwk.ts +59 -17
  246. package/src/detectors/k8s.ts +5 -2
  247. package/src/detectors/keystore.ts +109 -0
  248. package/src/detectors/mesh.ts +21 -45
  249. package/src/detectors/messaging.ts +22 -14
  250. package/src/detectors/objc.ts +209 -0
  251. package/src/detectors/openpgp.ts +194 -0
  252. package/src/detectors/pem.ts +30 -0
  253. package/src/detectors/php.ts +9 -4
  254. package/src/detectors/pkcs11.ts +113 -0
  255. package/src/detectors/proxy.ts +236 -0
  256. package/src/detectors/pulumi.ts +106 -0
  257. package/src/detectors/rust.ts +5 -0
  258. package/src/detectors/secrets.ts +15 -26
  259. package/src/detectors/solidity.ts +176 -0
  260. package/src/detectors/source.ts +48 -5
  261. package/src/detectors/spire.ts +143 -0
  262. package/src/detectors/ssh-ca.ts +215 -0
  263. package/src/detectors/supply-chain.ts +117 -0
  264. package/src/detectors/swift.ts +149 -0
  265. package/src/detectors/terraform.ts +42 -8
  266. package/src/detectors/vault.ts +120 -0
  267. package/src/detectors/vpn.ts +20 -48
  268. package/src/detectors/weak-hash.ts +217 -0
  269. package/src/detectors/webauthn.ts +205 -0
  270. package/src/detectors/xmldsig.ts +115 -0
  271. package/src/evidence.ts +127 -4
  272. package/src/index.ts +41 -13
  273. package/src/inventory.ts +15 -7
  274. package/src/registry.ts +51 -4
  275. package/src/remediate-pipeline.ts +1 -1
  276. package/src/remediation.ts +39 -0
  277. package/src/report.ts +36 -1
  278. package/src/scan-worker.ts +5 -3
  279. package/src/scan.ts +41 -6
  280. package/src/standards-profiles.ts +136 -0
  281. package/src/types.ts +9 -4
  282. package/src/version.ts +1 -1
  283. package/src/vex.ts +122 -0
  284. package/src/walk.ts +33 -2
@@ -15,9 +15,7 @@
15
15
  * detection benchmark's recall gate.
16
16
  */
17
17
  import type { Finding } from "./types.js";
18
- export type CommentStyle = "c" | "hash";
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
@@ -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,MAAM,MAAM,YAAY,GAAG,GAAG,GAAG,MAAM,CAAC;AA8BxC,sFAAsF;AACtF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAKrE;AAyCD;;;;;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;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAsC7E;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAW7F;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CASzD;AAED,+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"}
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
- export function commentStyleForFile(file) {
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
- export function pythonDocstringSpans(content) {
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
- export function offsetInSpans(spans, offset) {
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
- export function ignoredLines(content) {
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++) {
@@ -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":"AAoBA,8DAA8D;AAC9D,MAAM,MAAM,GAAsB;IAChC,KAAK;IACL,MAAM;IACN,KAAK;IACL,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,SAAS;IACT,KAAK;IACL,OAAO;IACP,KAAK;IACL,MAAM;IACN,KAAK;IACL,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,MAAM;IACN,MAAM;IACN,MAAM;IACN,KAAK;IACL,gFAAgF;IAChF,oFAAoF;IACpF,2EAA2E;IAC3E,MAAM;IACN,OAAO;IACP,OAAO;IACP,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,KAAK;IACL,QAAQ;IACR,yEAAyE;IACzE,IAAI;IACJ,KAAK;IACL,OAAO;IACP,4EAA4E;IAC5E,MAAM;IACN,OAAO;IACP,QAAQ;CACT,CAAC;AAEF,wDAAwD;AACxD,MAAM,SAAS,GAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;AAEnF,sFAAsF;AACtF,SAAS,mBAAmB,CAAC,IAAY;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,GAAG,CAAC;IACtD,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IAC5D,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,UAAU,CAAC,OAAe,EAAE,CAAS,EAAE,CAAS,EAAE,WAAoB;IAC7E,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACzB,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,OAAO,CAAC,GAAG,KAAK,EAAE,CAAC;YACjB,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBACxB,CAAC,IAAI,CAAC,CAAC;gBACP,SAAS;YACX,CAAC;YACD,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG;gBAAE,OAAO,CAAC,GAAG,CAAC,CAAC;YACrC,CAAC,EAAE,CAAC;QACN,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,oDAAoD;IACpE,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,KAAK,GAAG,IAAI,WAAW,CAAC,CAAC;IAChD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACd,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACb,IAAI,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YACnC,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC,EAAE,CAAC;IACN,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC1B,OAAe,EACf,KAAmB,EACnB,WAAW,GAAG,KAAK;IAEnB,MAAM,KAAK,GAA4B,EAAE,CAAC;IAC1C,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IACzB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACrB,uFAAuF;QACvF,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACxC,CAAC,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,CAAC;YAC3C,SAAS;QACX,CAAC;QACD,IAAI,KAAK,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACzD,MAAM,KAAK,GAAG,CAAC,CAAC;YAChB,CAAC,IAAI,CAAC,CAAC;YACP,OAAO,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI;gBAAE,CAAC,EAAE,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YACvB,SAAS;QACX,CAAC;QACD,IAAI,KAAK,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACzD,MAAM,KAAK,GAAG,CAAC,CAAC;YAChB,CAAC,IAAI,CAAC,CAAC;YACP,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC;gBAAE,CAAC,EAAE,CAAC;YACrE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YACvB,SAAS;QACX,CAAC;QACD,IAAI,KAAK,KAAK,MAAM,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,CAAC,CAAC;YAChB,CAAC,EAAE,CAAC;YACJ,OAAO,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI;gBAAE,CAAC,EAAE,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YACvB,SAAS;QACX,CAAC;QACD,CAAC,EAAE,CAAC;IACN,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,oBAAoB,CAAC,OAAe;IAC3C,MAAM,KAAK,GAA4B,EAAE,CAAC;IAC1C,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IACzB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACd,CAAC,EAAE,CAAC;YACJ,OAAO,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI;gBAAE,CAAC,EAAE,CAAC;YACzC,SAAS;QACX,CAAC;QACD,IAAI,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7E,MAAM,KAAK,GAAG,CAAC,CAAC;YAChB,CAAC,IAAI,CAAC,CAAC;YACP,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;gBAAE,CAAC,EAAE,CAAC;YACzF,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YACvB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YAC3B,MAAM,CAAC,GAAG,CAAC,CAAC;YACZ,CAAC,EAAE,CAAC;YACJ,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBACb,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;oBACxB,CAAC,IAAI,CAAC,CAAC;oBACP,SAAS;gBACX,CAAC;gBACD,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;oBACrB,CAAC,EAAE,CAAC;oBACJ,MAAM;gBACR,CAAC;gBACD,CAAC,EAAE,CAAC;YACN,CAAC;YACD,SAAS;QACX,CAAC;QACD,CAAC,EAAE,CAAC;IACN,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,gFAAgF;AAChF,SAAS,aAAa,CAAC,KAAsC,EAAE,MAAc;IAC3E,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,IAAI,EAAE,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAC1B,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;QAChB,MAAM,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;QAC5B,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,MAAM,GAAG,CAAC;YAAE,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;aACxB,IAAI,MAAM,IAAI,CAAC;YAAE,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;;YAC9B,OAAO,IAAI,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,YAAY,CAAC,OAAe;IACnC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAI,CAAC,QAAQ,CAAC,wBAAwB,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;aAC3D,IAAI,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,oBAAoB,CAAC,QAAmB,EAAE,OAAe;IACvE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;QAAE,OAAO,QAAQ,CAAC;IAChF,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IACxC,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAmB,EACnB,OAAe,EACf,IAAY;IAEZ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC3C,MAAM,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,CAAC,KAAK;QAAE,OAAO,QAAQ,CAAC;IAC5B,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAChE,6EAA6E;IAC7E,MAAM,QAAQ,GAAG,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACvE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAEjE,+EAA+E;IAC/E,wEAAwE;IACxE,MAAM,UAAU,GAAa,CAAC,CAAC,CAAC,CAAC;IACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI;YAAE,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QAC3B,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACtD,IAAI,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC;YAAE,OAAO,KAAK,CAAC;QAC/C,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC;YAC3F,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,OAAe,EACf,KAAmB,EACnB,WAAW,GAAG,KAAK;IAEnB,MAAM,KAAK,GAA4B,EAAE,CAAC;IAC1C,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IACzB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,KAAK,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACzD,CAAC,IAAI,CAAC,CAAC;YACP,OAAO,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI;gBAAE,CAAC,EAAE,CAAC;YACzC,SAAS;QACX,CAAC;QACD,IAAI,KAAK,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACzD,CAAC,IAAI,CAAC,CAAC;YACP,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC;gBAAE,CAAC,EAAE,CAAC;YACrE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;YACvB,SAAS;QACX,CAAC;QACD,IAAI,KAAK,KAAK,MAAM,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YAClC,CAAC,EAAE,CAAC;YACJ,OAAO,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI;gBAAE,CAAC,EAAE,CAAC;YACzC,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACxC,MAAM,KAAK,GAAG,CAAC,CAAC;YAChB,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,CAAC;YACnD,wEAAwE;YACxE,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,GAAG,KAAK,GAAG,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;YAC3D,CAAC,GAAG,GAAG,CAAC;YACR,SAAS;QACX,CAAC;QACD,CAAC,EAAE,CAAC;IACN,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,0BAA0B,CACxC,QAAmB,EACnB,OAAe,EACf,IAAY,EACZ,OAA4B;IAE5B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC3F,MAAM,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,CAAC,KAAK;QAAE,OAAO,QAAQ,CAAC;IAC5B,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAExC,MAAM,UAAU,GAAa,CAAC,CAAC,CAAC,CAAC;IACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI;YAAE,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QAC3B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC;QACxC,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACtD,OAAO,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC","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"]}
@@ -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
- export interface FindingSpec {
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
- export interface RuleMatch {
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
- export type RuleOverrides = Partial<Pick<RuleMeta, "title" | "category" | "severity" | "confidence" | "algorithm" | "hndl" | "message" | "cwe">> & {
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
- * or after some call offset `c` with `idx - c < window`. Runs in O(log n) by
139
- * binary-searching the largest call offset `idx` and checking the gap. This
140
- * replaces the previous O(matches × calls) linear scan (`nearCall`).
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":"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"}
1
+ {"version":3,"file":"detect-utils.d.ts","sourceRoot":"","sources":["../src/detect-utils.ts"],"names":[],"mappings":"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"}
@@ -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
- export function offsetToLineCol(content, offset) {
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
- export function lineAt(content, offset) {
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
- * or after some call offset `c` with `idx - c < window`. Runs in O(log n) by
238
- * binary-searching the largest call offset `idx` and checking the gap. This
239
- * replaces the previous O(matches × calls) linear scan (`nearCall`).
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