insa-check-core 0.1.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 (258) hide show
  1. package/LICENSE +24 -0
  2. package/dist/annex-b.d.ts +6 -0
  3. package/dist/annex-b.d.ts.map +1 -0
  4. package/dist/annex-b.js +152 -0
  5. package/dist/annex-b.js.map +1 -0
  6. package/dist/checks/content/page.d.ts +7 -0
  7. package/dist/checks/content/page.d.ts.map +1 -0
  8. package/dist/checks/content/page.js +133 -0
  9. package/dist/checks/content/page.js.map +1 -0
  10. package/dist/checks/cookies/cookie-flags.d.ts +3 -0
  11. package/dist/checks/cookies/cookie-flags.d.ts.map +1 -0
  12. package/dist/checks/cookies/cookie-flags.js +41 -0
  13. package/dist/checks/cookies/cookie-flags.js.map +1 -0
  14. package/dist/checks/cookies/cookie-scope.d.ts +6 -0
  15. package/dist/checks/cookies/cookie-scope.d.ts.map +1 -0
  16. package/dist/checks/cookies/cookie-scope.js +60 -0
  17. package/dist/checks/cookies/cookie-scope.js.map +1 -0
  18. package/dist/checks/deployment/server-banner.d.ts +4 -0
  19. package/dist/checks/deployment/server-banner.d.ts.map +1 -0
  20. package/dist/checks/deployment/server-banner.js +60 -0
  21. package/dist/checks/deployment/server-banner.js.map +1 -0
  22. package/dist/checks/deployment/trace.d.ts +3 -0
  23. package/dist/checks/deployment/trace.d.ts.map +1 -0
  24. package/dist/checks/deployment/trace.js +30 -0
  25. package/dist/checks/deployment/trace.js.map +1 -0
  26. package/dist/checks/headers/clickjacking.d.ts +5 -0
  27. package/dist/checks/headers/clickjacking.d.ts.map +1 -0
  28. package/dist/checks/headers/clickjacking.js +57 -0
  29. package/dist/checks/headers/clickjacking.js.map +1 -0
  30. package/dist/checks/headers/content-type-options.d.ts +3 -0
  31. package/dist/checks/headers/content-type-options.d.ts.map +1 -0
  32. package/dist/checks/headers/content-type-options.js +17 -0
  33. package/dist/checks/headers/content-type-options.js.map +1 -0
  34. package/dist/checks/headers/cors.d.ts +4 -0
  35. package/dist/checks/headers/cors.d.ts.map +1 -0
  36. package/dist/checks/headers/cors.js +38 -0
  37. package/dist/checks/headers/cors.js.map +1 -0
  38. package/dist/checks/html.d.ts +11 -0
  39. package/dist/checks/html.d.ts.map +1 -0
  40. package/dist/checks/html.js +30 -0
  41. package/dist/checks/html.js.map +1 -0
  42. package/dist/checks/index.d.ts +7 -0
  43. package/dist/checks/index.d.ts.map +1 -0
  44. package/dist/checks/index.js +48 -0
  45. package/dist/checks/index.js.map +1 -0
  46. package/dist/checks/info-disclosure/directory-listing.d.ts +6 -0
  47. package/dist/checks/info-disclosure/directory-listing.d.ts.map +1 -0
  48. package/dist/checks/info-disclosure/directory-listing.js +60 -0
  49. package/dist/checks/info-disclosure/directory-listing.js.map +1 -0
  50. package/dist/checks/info-disclosure/exposed-files.d.ts +4 -0
  51. package/dist/checks/info-disclosure/exposed-files.d.ts.map +1 -0
  52. package/dist/checks/info-disclosure/exposed-files.js +56 -0
  53. package/dist/checks/info-disclosure/exposed-files.js.map +1 -0
  54. package/dist/checks/info-disclosure/more.d.ts +8 -0
  55. package/dist/checks/info-disclosure/more.d.ts.map +1 -0
  56. package/dist/checks/info-disclosure/more.js +186 -0
  57. package/dist/checks/info-disclosure/more.js.map +1 -0
  58. package/dist/checks/tls/certificate.d.ts +3 -0
  59. package/dist/checks/tls/certificate.d.ts.map +1 -0
  60. package/dist/checks/tls/certificate.js +49 -0
  61. package/dist/checks/tls/certificate.js.map +1 -0
  62. package/dist/checks/tls/hsts.d.ts +5 -0
  63. package/dist/checks/tls/hsts.d.ts.map +1 -0
  64. package/dist/checks/tls/hsts.js +35 -0
  65. package/dist/checks/tls/hsts.js.map +1 -0
  66. package/dist/checks/tls/https-redirect.d.ts +3 -0
  67. package/dist/checks/tls/https-redirect.d.ts.map +1 -0
  68. package/dist/checks/tls/https-redirect.js +46 -0
  69. package/dist/checks/tls/https-redirect.js.map +1 -0
  70. package/dist/checks/tls/legacy-protocols.d.ts +3 -0
  71. package/dist/checks/tls/legacy-protocols.d.ts.map +1 -0
  72. package/dist/checks/tls/legacy-protocols.js +27 -0
  73. package/dist/checks/tls/legacy-protocols.js.map +1 -0
  74. package/dist/checks/util.d.ts +22 -0
  75. package/dist/checks/util.d.ts.map +1 -0
  76. package/dist/checks/util.js +63 -0
  77. package/dist/checks/util.js.map +1 -0
  78. package/dist/code/checks/crypto.d.ts +5 -0
  79. package/dist/code/checks/crypto.d.ts.map +1 -0
  80. package/dist/code/checks/crypto.js +100 -0
  81. package/dist/code/checks/crypto.js.map +1 -0
  82. package/dist/code/checks/django.d.ts +14 -0
  83. package/dist/code/checks/django.d.ts.map +1 -0
  84. package/dist/code/checks/django.js +313 -0
  85. package/dist/code/checks/django.js.map +1 -0
  86. package/dist/code/checks/flask.d.ts +6 -0
  87. package/dist/code/checks/flask.d.ts.map +1 -0
  88. package/dist/code/checks/flask.js +100 -0
  89. package/dist/code/checks/flask.js.map +1 -0
  90. package/dist/code/checks/index.d.ts +9 -0
  91. package/dist/code/checks/index.d.ts.map +1 -0
  92. package/dist/code/checks/index.js +126 -0
  93. package/dist/code/checks/index.js.map +1 -0
  94. package/dist/code/checks/injection.d.ts +27 -0
  95. package/dist/code/checks/injection.d.ts.map +1 -0
  96. package/dist/code/checks/injection.js +189 -0
  97. package/dist/code/checks/injection.js.map +1 -0
  98. package/dist/code/checks/javascript-config.d.ts +14 -0
  99. package/dist/code/checks/javascript-config.d.ts.map +1 -0
  100. package/dist/code/checks/javascript-config.js +251 -0
  101. package/dist/code/checks/javascript-config.js.map +1 -0
  102. package/dist/code/checks/javascript.d.ts +34 -0
  103. package/dist/code/checks/javascript.d.ts.map +1 -0
  104. package/dist/code/checks/javascript.js +333 -0
  105. package/dist/code/checks/javascript.js.map +1 -0
  106. package/dist/code/checks/php.d.ts +27 -0
  107. package/dist/code/checks/php.d.ts.map +1 -0
  108. package/dist/code/checks/php.js +433 -0
  109. package/dist/code/checks/php.js.map +1 -0
  110. package/dist/code/checks/secrets.d.ts +10 -0
  111. package/dist/code/checks/secrets.d.ts.map +1 -0
  112. package/dist/code/checks/secrets.js +140 -0
  113. package/dist/code/checks/secrets.js.map +1 -0
  114. package/dist/code/checks/shared.d.ts +25 -0
  115. package/dist/code/checks/shared.d.ts.map +1 -0
  116. package/dist/code/checks/shared.js +57 -0
  117. package/dist/code/checks/shared.js.map +1 -0
  118. package/dist/code/checks/treesitter.d.ts +15 -0
  119. package/dist/code/checks/treesitter.d.ts.map +1 -0
  120. package/dist/code/checks/treesitter.js +322 -0
  121. package/dist/code/checks/treesitter.js.map +1 -0
  122. package/dist/code/checks/xml.d.ts +9 -0
  123. package/dist/code/checks/xml.d.ts.map +1 -0
  124. package/dist/code/checks/xml.js +78 -0
  125. package/dist/code/checks/xml.js.map +1 -0
  126. package/dist/code/context.d.ts +6 -0
  127. package/dist/code/context.d.ts.map +1 -0
  128. package/dist/code/context.js +92 -0
  129. package/dist/code/context.js.map +1 -0
  130. package/dist/code/detect.d.ts +3 -0
  131. package/dist/code/detect.d.ts.map +1 -0
  132. package/dist/code/detect.js +118 -0
  133. package/dist/code/detect.js.map +1 -0
  134. package/dist/code/django/settings.d.ts +46 -0
  135. package/dist/code/django/settings.d.ts.map +1 -0
  136. package/dist/code/django/settings.js +154 -0
  137. package/dist/code/django/settings.js.map +1 -0
  138. package/dist/code/findings.d.ts +18 -0
  139. package/dist/code/findings.d.ts.map +1 -0
  140. package/dist/code/findings.js +63 -0
  141. package/dist/code/findings.js.map +1 -0
  142. package/dist/code/javascript/ast.d.ts +46 -0
  143. package/dist/code/javascript/ast.d.ts.map +1 -0
  144. package/dist/code/javascript/ast.js +166 -0
  145. package/dist/code/javascript/ast.js.map +1 -0
  146. package/dist/code/javascript/index.d.ts +3 -0
  147. package/dist/code/javascript/index.d.ts.map +1 -0
  148. package/dist/code/javascript/index.js +3 -0
  149. package/dist/code/javascript/index.js.map +1 -0
  150. package/dist/code/javascript/module.d.ts +59 -0
  151. package/dist/code/javascript/module.d.ts.map +1 -0
  152. package/dist/code/javascript/module.js +250 -0
  153. package/dist/code/javascript/module.js.map +1 -0
  154. package/dist/code/php/index.d.ts +92 -0
  155. package/dist/code/php/index.d.ts.map +1 -0
  156. package/dist/code/php/index.js +316 -0
  157. package/dist/code/php/index.js.map +1 -0
  158. package/dist/code/project.d.ts +13 -0
  159. package/dist/code/project.d.ts.map +1 -0
  160. package/dist/code/project.js +69 -0
  161. package/dist/code/project.js.map +1 -0
  162. package/dist/code/python/ast.d.ts +47 -0
  163. package/dist/code/python/ast.d.ts.map +1 -0
  164. package/dist/code/python/ast.js +193 -0
  165. package/dist/code/python/ast.js.map +1 -0
  166. package/dist/code/python/index.d.ts +3 -0
  167. package/dist/code/python/index.d.ts.map +1 -0
  168. package/dist/code/python/index.js +3 -0
  169. package/dist/code/python/index.js.map +1 -0
  170. package/dist/code/python/module.d.ts +55 -0
  171. package/dist/code/python/module.d.ts.map +1 -0
  172. package/dist/code/python/module.js +215 -0
  173. package/dist/code/python/module.js.map +1 -0
  174. package/dist/code/scan.d.ts +24 -0
  175. package/dist/code/scan.d.ts.map +1 -0
  176. package/dist/code/scan.js +159 -0
  177. package/dist/code/scan.js.map +1 -0
  178. package/dist/code/treesitter/index.d.ts +83 -0
  179. package/dist/code/treesitter/index.d.ts.map +1 -0
  180. package/dist/code/treesitter/index.js +243 -0
  181. package/dist/code/treesitter/index.js.map +1 -0
  182. package/dist/code/types.d.ts +48 -0
  183. package/dist/code/types.d.ts.map +1 -0
  184. package/dist/code/types.js +2 -0
  185. package/dist/code/types.js.map +1 -0
  186. package/dist/content/en.d.ts +3 -0
  187. package/dist/content/en.d.ts.map +1 -0
  188. package/dist/content/en.js +722 -0
  189. package/dist/content/en.js.map +1 -0
  190. package/dist/content/index.d.ts +15 -0
  191. package/dist/content/index.d.ts.map +1 -0
  192. package/dist/content/index.js +21 -0
  193. package/dist/content/index.js.map +1 -0
  194. package/dist/content/types.d.ts +87 -0
  195. package/dist/content/types.d.ts.map +1 -0
  196. package/dist/content/types.js +2 -0
  197. package/dist/content/types.js.map +1 -0
  198. package/dist/engine.d.ts +52 -0
  199. package/dist/engine.d.ts.map +1 -0
  200. package/dist/engine.js +120 -0
  201. package/dist/engine.js.map +1 -0
  202. package/dist/index.d.ts +12 -0
  203. package/dist/index.d.ts.map +1 -0
  204. package/dist/index.js +11 -0
  205. package/dist/index.js.map +1 -0
  206. package/dist/meta.d.ts +4 -0
  207. package/dist/meta.d.ts.map +1 -0
  208. package/dist/meta.js +4 -0
  209. package/dist/meta.js.map +1 -0
  210. package/dist/net/context.d.ts +9 -0
  211. package/dist/net/context.d.ts.map +1 -0
  212. package/dist/net/context.js +62 -0
  213. package/dist/net/context.js.map +1 -0
  214. package/dist/net/guard.d.ts +16 -0
  215. package/dist/net/guard.d.ts.map +1 -0
  216. package/dist/net/guard.js +70 -0
  217. package/dist/net/guard.js.map +1 -0
  218. package/dist/net/http.d.ts +22 -0
  219. package/dist/net/http.d.ts.map +1 -0
  220. package/dist/net/http.js +114 -0
  221. package/dist/net/http.js.map +1 -0
  222. package/dist/net/tls.d.ts +15 -0
  223. package/dist/net/tls.d.ts.map +1 -0
  224. package/dist/net/tls.js +103 -0
  225. package/dist/net/tls.js.map +1 -0
  226. package/dist/report/html.d.ts +14 -0
  227. package/dist/report/html.d.ts.map +1 -0
  228. package/dist/report/html.js +210 -0
  229. package/dist/report/html.js.map +1 -0
  230. package/dist/report/index.d.ts +10 -0
  231. package/dist/report/index.d.ts.map +1 -0
  232. package/dist/report/index.js +10 -0
  233. package/dist/report/index.js.map +1 -0
  234. package/dist/report/manual.d.ts +62 -0
  235. package/dist/report/manual.d.ts.map +1 -0
  236. package/dist/report/manual.js +190 -0
  237. package/dist/report/manual.js.map +1 -0
  238. package/dist/report/merge.d.ts +8 -0
  239. package/dist/report/merge.d.ts.map +1 -0
  240. package/dist/report/merge.js +135 -0
  241. package/dist/report/merge.js.map +1 -0
  242. package/dist/report/summary.d.ts +10 -0
  243. package/dist/report/summary.d.ts.map +1 -0
  244. package/dist/report/summary.js +27 -0
  245. package/dist/report/summary.js.map +1 -0
  246. package/dist/report/validate.d.ts +3 -0
  247. package/dist/report/validate.d.ts.map +1 -0
  248. package/dist/report/validate.js +78 -0
  249. package/dist/report/validate.js.map +1 -0
  250. package/dist/types.d.ts +244 -0
  251. package/dist/types.d.ts.map +1 -0
  252. package/dist/types.js +2 -0
  253. package/dist/types.js.map +1 -0
  254. package/dist/validate.d.ts +4 -0
  255. package/dist/validate.d.ts.map +1 -0
  256. package/dist/validate.js +24 -0
  257. package/dist/validate.js.map +1 -0
  258. package/package.json +40 -0
package/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ insa-check
2
+
3
+ Copyright (c) 2026 the insa-check author (https://github.com/zikra-abdusemed-cmd).
4
+ All rights reserved.
5
+
6
+ You may install and run unmodified copies of this software that you obtained from
7
+ the author or from the author's official package (https://www.npmjs.com/package/insa-check),
8
+ for the purpose of auditing websites and source code that you own or are authorized
9
+ to test.
10
+
11
+ You may not copy, modify, merge, publish, distribute, sublicense or sell this
12
+ software, in whole or in part, without the author's prior written permission.
13
+
14
+ Third-party packages that this software depends on are licensed separately under
15
+ their own terms.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
18
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
19
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
20
+ ANY CLAIM, DAMAGES OR OTHER LIABILITY ARISING FROM THE USE OF THE SOFTWARE. A REPORT
21
+ PRODUCED BY THIS SOFTWARE IS NOT A CERTIFICATION OF SECURITY OR COMPLIANCE.
22
+
23
+ This software is not affiliated with or endorsed by the Information Network Security
24
+ Administration (INSA).
@@ -0,0 +1,6 @@
1
+ import type { AnnexItem } from "./types.js";
2
+ /** Edition of the standard this registry was transcribed from. */
3
+ export declare const STANDARD = "INSA Secure Website Management Standard, Version 1.0 (2014 EC), Annex B: Minimum security testing of the website";
4
+ /** The registry with English guided-review content applied (content/en.ts). Use localizeItem() for other locales. */
5
+ export declare const ANNEX_B: readonly AnnexItem[];
6
+ //# sourceMappingURL=annex-b.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"annex-b.d.ts","sourceRoot":"","sources":["../src/annex-b.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAQ,MAAM,YAAY,CAAC;AAElD,kEAAkE;AAClE,eAAO,MAAM,QAAQ,qHAC+F,CAAC;AAgQrH,qHAAqH;AACrH,eAAO,MAAM,OAAO,EAAE,SAAS,SAAS,EAA4C,CAAC"}
@@ -0,0 +1,152 @@
1
+ import { localizeItem } from "./content/index.js";
2
+ /** Edition of the standard this registry was transcribed from. */
3
+ export const STANDARD = "INSA Secure Website Management Standard, Version 1.0 (2014 EC), Annex B: Minimum security testing of the website";
4
+ /*
5
+ * Every Annex B item, in the standard's order. Section headings, numbers and
6
+ * titles are transcribed verbatim from docs/checklist.pdf (pages 10–16),
7
+ * including the original wording and capitalisation.
8
+ *
9
+ * `tier` is the intended automation route:
10
+ * 1 = passive check from the URL
11
+ * 2 = source-code / configuration analysis
12
+ * 3 = manual, authenticated or active testing by a person
13
+ * `checks` names the automated checks that decide the item, per kind:
14
+ * url -> checks/index.ts (Tier 1, live site)
15
+ * code -> code/checks/index.ts (Tier 2, static analysis; some Tier 1 items are also checkable in code)
16
+ * Items a scan has no check for are reported as "manual" with `manualNote`.
17
+ */
18
+ const S = {
19
+ info: "Information Disclosure",
20
+ privacy: "Privacy and Confidentiality",
21
+ state: "State Management",
22
+ auth: "Authentication and Authorization Process",
23
+ input: "User Input Management",
24
+ session: "Session Management",
25
+ upload: "File Upload",
26
+ content: "Content",
27
+ xml: "XML Processing",
28
+ deploy: "Deployment",
29
+ misc: "Miscellaneous",
30
+ };
31
+ function item(id, section, title, tier, manualNote, checks) {
32
+ return { id: String(id), section, title, tier, manualNote, ...(checks ? { checks } : {}) };
33
+ }
34
+ const INJECTION_NOTE = "Active test, not performed by this passive tool. Test inputs with an authorised DAST tool or manual payloads, and review the code that handles them.";
35
+ const ITEMS = [
36
+ // Information Disclosure
37
+ item(1, S.info, "Test for extraneous files in the document root", 1, "Look for backups, VCS metadata, config and temp files (e.g. .git/, .env, *.bak, *.old) in the web root.", { url: "exposed-files" }),
38
+ item(2, S.info, "Test for extraneous directory listings", 1, "Request directories without an index file and confirm the server does not list their contents.", { url: "directory-listing" }),
39
+ item(3, S.info, "Test for accessible debug functionality", 1, "Check for debug/status endpoints (phpinfo, server-status, framework debug toolbars, actuator, elmah) and debug mode in production.", { url: "debug-pages", code: ["django-debug", "js-debug", "flask-debug", "laravel-debug"] }),
40
+ item(4, S.info, "Test for sensitive information in log and error messages", 1, "Trigger errors (bad parameters, missing pages) and confirm no stack traces, SQL, paths or versions are shown; review log contents.", { url: "error-messages" }),
41
+ item(5, S.info, "Test for sensitive information in robots.txt", 1, "Read /robots.txt and confirm Disallow entries do not reveal hidden admin areas or sensitive paths.", { url: "robots-txt" }),
42
+ item(6, S.info, "Test for sensitive information in source code", 1, "Inspect page source, comments and client-side JavaScript for credentials, API keys, internal notes or hostnames.", { url: "page-source-secrets", code: ["py-hardcoded-secrets", "js-hardcoded-secrets", "php-hardcoded-secrets", "ts-hardcoded-secrets"] }),
43
+ item(7, S.info, "Test for disclosure of internal addresses", 1, "Check headers, bodies and error pages for private IP addresses or internal hostnames.", { url: "internal-addresses" }),
44
+ // Privacy and Confidentiality
45
+ item(8, S.privacy, "Test for sensitive information stored in URLs", 3, "Walk authenticated flows and confirm tokens, passwords or personal data never appear in URLs or query strings."),
46
+ item(9, S.privacy, "Test for unencrypted sensitive information stored at the client-side", 3, "Inspect cookies, localStorage, sessionStorage and IndexedDB after login for unencrypted sensitive data."),
47
+ item(10, S.privacy, "Test for sensitive information stored in (externally) archived pages", 1, "Search web archives (e.g. web.archive.org) and search-engine caches for old pages exposing sensitive content.", { url: "archived-pages" }),
48
+ item(11, S.privacy, "Test for content included from untrusted sources", 1, "List third-party scripts, styles and frames; confirm each origin is trusted and uses Subresource Integrity where possible.", { url: "third-party-content" }),
49
+ item(12, S.privacy, "Test for caching of pages with sensitive information", 3, "After login, confirm sensitive pages send Cache-Control: no-store and are not cached by browsers or proxies."),
50
+ item(13, S.privacy, "Test for insecure transmission of sensitive information", 1, "Confirm forms, logins and APIs that carry sensitive data submit only over HTTPS.", { url: "insecure-forms" }),
51
+ item(14, S.privacy, "Test for non-SSL/TLS pages on sites processing sensitive information", 1, "Request the site over plain HTTP and confirm it redirects to HTTPS.", { url: "https-redirect" }),
52
+ item(15, S.privacy, "Test for SSL/TLS pages served with mixed content", 1, "Load HTTPS pages and check for sub-resources (scripts, images, frames) fetched over plain HTTP.", { url: "mixed-content" }),
53
+ item(16, S.privacy, "Test for missing HSTS header on full SSL sites", 1, "Confirm HTTPS responses include Strict-Transport-Security with an adequate max-age.", { url: "hsts" }),
54
+ item(17, S.privacy, "Test for known vulnerabilities in SSL/TLS", 1, "Check that SSLv3/TLS 1.0/1.1 and weak cipher suites are disabled (e.g. with testssl.sh or SSL Labs).", { url: "legacy-tls" }),
55
+ item(18, S.privacy, "Test for weak, untrusted or expired SSL certificates", 1, "Verify the certificate chain is trusted, matches the hostname, is unexpired and uses a strong key.", { url: "certificate" }),
56
+ item(19, S.privacy, "Test for the usage of unproven cryptographic primitives", 2, "Review source and configuration for custom or deprecated algorithms (MD5, SHA-1, DES, RC4, home-made crypto).", { code: ["py-weak-crypto", "js-weak-crypto", "php-weak-crypto", "ts-weak-crypto"] }),
57
+ item(20, S.privacy, "Test for the incorrect usage of cryptographic primitives", 2, "Review code for misuse: static IVs/keys, ECB mode, unsalted password hashes, weak random number generators.", { code: ["py-crypto-misuse", "js-crypto-misuse", "php-crypto-misuse", "ts-crypto-misuse"] }),
58
+ // State Management
59
+ item(21, S.state, "Test for client-side state management", 3, "Check whether security-relevant state (prices, roles, workflow step) is kept client-side in hidden fields or cookies and can be tampered with."),
60
+ item(22, S.state, "Test for invalid state transitions", 3, "Attempt to skip, repeat or reorder steps in multi-step workflows and confirm the server rejects invalid transitions."),
61
+ // Authentication and Authorization Process
62
+ item(23, S.auth, "Test for missing authentication or authorization", 3, "Request protected pages and APIs without a session, and with a low-privilege session, and confirm access is denied."),
63
+ item(24, S.auth, "Test for client-side authentication", 3, "Confirm authentication decisions are made on the server, not in JavaScript or other client-side code."),
64
+ item(25, S.auth, "Test for predictable and default credentials", 3, "Try vendor defaults and common credentials on all login interfaces, including admin panels and CMS back-ends."),
65
+ item(26, S.auth, "Test for predictable authentication or authorization tokens", 3, "Collect several tokens and analyse them for predictability (sequential, time-based, low entropy)."),
66
+ item(27, S.auth, "Test for authentication or authorization based on obscurity", 3, "Check whether protection relies on hidden URLs or parameters instead of real access control."),
67
+ item(28, S.auth, "Test for identifier-based authorization/ Privilege escalation citation", 3, "Change object identifiers and role parameters (IDOR) and confirm users cannot reach other users' data or higher privileges."),
68
+ item(29, S.auth, "Test for acceptance of weak passwords", 3, "Try setting short, common or trivial passwords and confirm the password policy rejects them."),
69
+ item(30, S.auth, "Test for account recovery process", 3, "Review password reset: token strength and expiry, no account enumeration, no secret questions with guessable answers."),
70
+ item(31, S.auth, "Test any “remember me” function", 3, "Inspect persistent-login tokens for strength, expiry, HttpOnly/Secure flags and server-side revocation."),
71
+ item(32, S.auth, "Test for fail-open conditions", 3, "Force errors during authentication/authorisation (malformed input, back-end failure) and confirm access is denied."),
72
+ item(33, S.auth, "Test any impersonation function/ re CAPTCHA", 3, "Review impersonation/“login as” features and CAPTCHA implementation for bypasses."),
73
+ item(34, S.auth, "Test for plaintext retrieval of passwords", 2, "Confirm the application never displays or emails existing passwords (implies reversible storage).", { code: ["py-plaintext-passwords", "js-plaintext-passwords", "php-plaintext-passwords"] }),
74
+ item(35, S.auth, "Test for username enumeration/ Test username uniqueness", 3, "Compare responses for valid and invalid usernames on login, registration and reset; confirm usernames are unique."),
75
+ item(36, S.auth, "Check for unsafe distribution of credentials", 3, "Review how initial credentials are delivered (not in plaintext email/URL; forced change on first use)."),
76
+ item(37, S.auth, "Test for missing rate limiting on authentication functionality", 2, "Confirm repeated failed logins trigger throttling, lockout or CAPTCHA (test only with permission).", { code: ["django-auth-rate-limit", "js-auth-rate-limit", "flask-auth-rate-limit", "laravel-auth-rate-limit"] }),
77
+ item(38, S.auth, "Test for missing re-authentication when changing credentials", 3, "Confirm changing password, e-mail or MFA settings requires the current password."),
78
+ item(39, S.auth, "Test for missing logout functionality", 3, "Confirm every authenticated page offers logout and that logout ends the session server-side."),
79
+ item(40, S.auth, "Test any multi-stage mechanisms", 3, "For multi-step login/MFA, confirm steps cannot be skipped or completed out of order."),
80
+ // User Input Management
81
+ item(41, S.input, "Test for SQL injection", 2, INJECTION_NOTE, { code: ["py-sql-injection", "js-sql-injection", "php-sql-injection", "ts-sql-injection"] }),
82
+ item(42, S.input, "Test for path traversal and filename injection", 2, INJECTION_NOTE, { code: ["py-path-traversal", "js-path-traversal", "php-path-traversal", "ts-path-traversal"] }),
83
+ item(43, S.input, "Test for cross-site scripting (Stored, DOM and reflected)", 2, INJECTION_NOTE, { code: ["py-xss", "js-xss", "php-xss", "ts-xss"] }),
84
+ item(44, S.input, "Test for system command injection", 2, INJECTION_NOTE, { code: ["py-command-injection", "js-command-injection", "php-command-injection", "ts-command-injection"] }),
85
+ item(45, S.input, "Test for XML injection", 2, INJECTION_NOTE),
86
+ item(46, S.input, "Test for XPath injection", 2, INJECTION_NOTE, { code: "py-xpath-injection" }),
87
+ item(47, S.input, "Test for XSL(T) injection", 2, INJECTION_NOTE),
88
+ item(48, S.input, "Test for SMTP injection", 2, INJECTION_NOTE),
89
+ item(49, S.input, "Test for SSI injection", 2, INJECTION_NOTE),
90
+ item(50, S.input, "Test for HTTP header injection", 2, INJECTION_NOTE),
91
+ item(51, S.input, "Test for HTTP parameter injection", 2, INJECTION_NOTE),
92
+ item(52, S.input, "Test for native software flaws (buffer overflow, integer bugs, format strings)", 2, "Review native components and extensions with static analysis; fuzz only in an authorised test environment."),
93
+ item(53, S.input, "Test for LDAP injection", 2, INJECTION_NOTE, { code: "py-ldap-injection" }),
94
+ item(54, S.input, "Test for dynamic scripting injection", 2, INJECTION_NOTE, { code: ["py-code-injection", "js-code-injection", "php-code-injection", "ts-code-injection"] }),
95
+ item(55, S.input, "Test for regular expression injection", 2, INJECTION_NOTE, { code: ["py-regex-injection", "js-regex-injection", "php-regex-injection"] }),
96
+ item(56, S.input, "Test for data property/field injection", 2, INJECTION_NOTE, { code: ["py-mass-assignment", "js-mass-assignment", "php-mass-assignment"] }),
97
+ item(57, S.input, "Test for protocol-specific injection", 2, INJECTION_NOTE),
98
+ item(58, S.input, "Test for expression language injection", 2, INJECTION_NOTE, { code: ["py-template-injection", "js-template-injection"] }),
99
+ item(59, S.input, "Fuzz all request parameters", 3, "Active test: fuzz every parameter with an authorised DAST tool in a test environment and review anomalies."),
100
+ item(60, S.input, "Test for arbitrary redirection", 2, "Find parameters that control redirects and confirm they cannot send users to arbitrary external sites.", { code: ["py-open-redirect", "js-open-redirect", "php-open-redirect", "ts-open-redirect"] }),
101
+ // Session Management
102
+ item(61, S.session, "Test for cross-site request forgery (CSRF)", 2, "Confirm state-changing requests require an unpredictable anti-CSRF token or SameSite cookies, and reject cross-site requests.", { code: ["django-csrf", "js-csrf", "flask-csrf", "laravel-csrf", "ts-csrf"] }),
103
+ item(62, S.session, "Test for predictable CSRF tokens", 3, "Collect several CSRF tokens and confirm they are random and bound to the session."),
104
+ item(63, S.session, "Test for missing session revocation on logout", 3, "After logout, replay the old session cookie and confirm it is rejected."),
105
+ item(64, S.session, "Test for missing session regeneration on login", 2, "Compare the session ID before and after login; it must change.", { code: ["django-login-session-rotation", "js-login-session-rotation", "laravel-login-session-rotation"] }),
106
+ item(65, S.session, "Test for missing session regeneration when changing credentials", 3, "Confirm the session ID changes after a password change."),
107
+ item(66, S.session, "Test for missing revocation of other sessions when changing credentials", 3, "Log in from two browsers, change the password in one, and confirm the other session is ended."),
108
+ item(67, S.session, "Test for missing Secure flag on session cookies", 1, "After login, inspect session cookies for the Secure attribute.", { url: "cookie-secure", code: ["django-session-cookie-secure", "js-session-cookie-secure", "flask-session-cookie-secure", "laravel-session-cookie-secure"] }),
109
+ item(68, S.session, "Test for missing HttpOnly Flag on session cookies", 1, "After login, inspect session cookies for the HttpOnly attribute.", { url: "cookie-httponly", code: ["django-session-cookie-httponly", "js-session-cookie-httponly", "flask-session-cookie-httponly", "laravel-session-cookie-httponly"] }),
110
+ item(69, S.session, "Test for non-restrictive domain on session cookies", 1, "Check the Domain attribute of session cookies is not broader than necessary (e.g. not the parent domain).", { url: "cookie-domain", code: ["django-session-cookie-domain", "js-session-cookie-domain"] }),
111
+ item(70, S.session, "Test for non-restrictive or missing path on session cookies", 1, "Check the Path attribute of session cookies limits them to the application's path.", { url: "cookie-path", code: ["django-session-cookie-path", "js-session-cookie-path"] }),
112
+ item(71, S.session, "Test for predictable session identifiers", 3, "Collect many session IDs and analyse their randomness (e.g. Burp Sequencer)."),
113
+ item(72, S.session, "Test for session identifier collisions", 3, "Check session IDs are long enough and generated so that two users cannot receive the same ID."),
114
+ item(73, S.session, "Test for session fixation", 2, "Set a known session ID before login and confirm it is not accepted after authentication.", { code: ["django-login-session-rotation", "js-login-session-rotation", "laravel-login-session-rotation"] }),
115
+ item(74, S.session, "Test for insecure transmission of session identifiers", 3, "Confirm session IDs are sent only over HTTPS and never in URLs."),
116
+ item(75, S.session, "Test for external session hijacking", 3, "Assess whether a stolen session can be reused from another client and whether binding or anomaly detection exists."),
117
+ item(76, S.session, "Test for missing periodic expiration of sessions", 2, "Confirm idle and absolute session timeouts are enforced server-side.", { code: ["django-session-expiry", "js-session-expiry", "laravel-session-expiry"] }),
118
+ item(77, S.session, "Check for disclosure of tokens in logs", 2, "Review application, proxy and server logs for session IDs, tokens or passwords.", { code: ["py-secrets-in-logs", "js-secrets-in-logs", "php-secrets-in-logs", "ts-secrets-in-logs"] }),
119
+ // File Upload
120
+ item(78, S.upload, "Test for storage of uploaded files in the document root", 2, "Confirm uploaded files are stored outside the web root or served through a controlled handler.", { code: "django-media-root" }),
121
+ item(79, S.upload, "Test for execution or interpretation of uploaded files", 3, "Upload harmless test files with script extensions (in a test environment) and confirm they are not executed."),
122
+ item(80, S.upload, "Test for uploading outside of designated upload directory", 2, "Try path-traversal file names on upload and confirm files land only in the upload directory.", { code: ["django-upload-path", "js-upload-path", "php-upload-path"] }),
123
+ item(81, S.upload, "Test for missing size restrictions on uploaded files", 2, "Confirm the server enforces a maximum upload size.", { code: ["django-upload-size", "js-upload-size", "php-upload-size"] }),
124
+ item(82, S.upload, "Test for missing type validation on uploaded files", 2, "Confirm file type is validated server-side by content, not only by extension or Content-Type.", { code: ["django-upload-type", "js-upload-type", "php-upload-type"] }),
125
+ // Content
126
+ item(83, S.content, "Test for missing or non-specific content type definitions", 1, "Confirm every response has an accurate Content-Type header (not missing, not generic).", { url: "content-type" }),
127
+ item(84, S.content, "Test for missing character set definitions", 1, "Confirm text responses declare a charset (Content-Type header or <meta charset>).", { url: "charset" }),
128
+ item(85, S.content, "Test for missing anti content sniffing measures", 1, "Confirm responses send X-Content-Type-Options: nosniff.", { url: "x-content-type-options" }),
129
+ // XML Processing
130
+ item(86, S.xml, "Test for XML external entity expansion", 2, "Review XML parser configuration to ensure external entities are disabled; actively test only with permission.", { code: ["py-xml-xxe", "js-xml-xxe", "php-xml-xxe", "ts-xml-xxe"] }),
131
+ item(87, S.xml, "Test for external DTD parsing", 2, "Confirm XML parsers do not load external DTDs.", { code: ["py-xml-dtd", "js-xml-dtd", "php-xml-dtd"] }),
132
+ item(88, S.xml, "Test for extraneous or dangerous XML extensions", 2, "Review XML processing for unneeded features such as XInclude or XSLT extensions.", { code: "py-xml-extensions" }),
133
+ item(89, S.xml, "Test for recursive entity expansion", 2, "Confirm parsers limit entity expansion (billion-laughs protection).", { code: "py-xml-entity-expansion" }),
134
+ // Deployment
135
+ item(90, S.deploy, "Test for missing security updates", 3, "Compare installed versions of OS, web server, CMS, plugins and libraries with vendor security advisories."),
136
+ item(91, S.deploy, "Test for unsupported or end-of-life software versions", 1, "Identify server and framework versions (headers, banners, generator tags) and confirm none are end-of-life.", { url: "server-banner" }),
137
+ item(92, S.deploy, "Test for HTTP TRACK and TRACE methods/ dangerous Methods", 1, "Send TRACE/TRACK and OPTIONS requests; confirm TRACE/TRACK are disabled and no unneeded methods (PUT, DELETE) are allowed.", { url: "http-trace" }),
138
+ item(93, S.deploy, "Test for extraneous functionality", 3, "Identify sample apps, unused modules, admin consoles and test pages that should be removed."),
139
+ item(94, S.deploy, "Test for default credentials", 3, "Try default credentials on server, CMS, database and management interfaces."),
140
+ item(95, S.deploy, "Test for Bugs in web server software", 3, "Check the identified web server version against known vulnerabilities (CVE databases)."),
141
+ // Miscellaneous
142
+ item(96, S.misc, "Test for missing anti-clickjacking measures", 1, "Confirm pages send X-Frame-Options or CSP frame-ancestors that prevent framing by other sites.", { url: "anti-clickjacking" }),
143
+ item(97, S.misc, "Test for open redirection", 2, "Test redirect parameters with external URLs and confirm they are rejected or restricted to an allow-list.", { code: ["py-open-redirect", "js-open-redirect", "php-open-redirect", "ts-open-redirect"] }),
144
+ item(98, S.misc, "Test for insecure cross-domain access policy", 1, "Check CORS headers (no wildcard with credentials, no reflected Origin) and /crossdomain.xml / clientaccesspolicy.xml.", { url: "cross-domain-policy" }),
145
+ item(99, S.misc, "Test for missing rate limiting on e-mail functionality", 3, "Confirm contact, invite and reset forms throttle repeated submissions (test only with permission)."),
146
+ item(100, S.misc, "Test for missing rate limiting on resource intensive functionality", 3, "Identify expensive operations (search, export, report generation) and confirm they are rate limited."),
147
+ item(101, S.misc, "Test for inappropriate rate limiting resulting in a denial of service", 3, "Confirm rate limiting or lockout cannot be abused to lock out legitimate users."),
148
+ item(102, S.misc, "Test for application- or setup-specific problems", 3, "Review business logic and deployment specifics not covered above."),
149
+ ];
150
+ /** The registry with English guided-review content applied (content/en.ts). Use localizeItem() for other locales. */
151
+ export const ANNEX_B = ITEMS.map((item) => localizeItem(item));
152
+ //# sourceMappingURL=annex-b.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"annex-b.js","sourceRoot":"","sources":["../src/annex-b.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { Check } from "../../types.js";
2
+ export declare const thirdPartyContent: Check;
3
+ export declare const insecureForms: Check;
4
+ export declare const mixedContent: Check;
5
+ export declare const contentType: Check;
6
+ export declare const charset: Check;
7
+ //# sourceMappingURL=page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../../src/checks/content/page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAO5C,eAAO,MAAM,iBAAiB,EAAE,KA6B/B,CAAC;AAGF,eAAO,MAAM,aAAa,EAAE,KAc3B,CAAC;AAGF,eAAO,MAAM,YAAY,EAAE,KAiB1B,CAAC;AAKF,eAAO,MAAM,WAAW,EAAE,KA+BzB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAarB,CAAC"}
@@ -0,0 +1,133 @@
1
+ import { findTags, resolveUrl } from "../html.js";
2
+ import { landingPage } from "../util.js";
3
+ const LIMITED = "Only the landing page was examined; pages behind the login need manual review.";
4
+ // --- #11 Content included from untrusted sources ------------------------------------
5
+ export const thirdPartyContent = {
6
+ key: "third-party-content",
7
+ kind: "url",
8
+ async run(ctx) {
9
+ const page = await landingPage(ctx);
10
+ const origin = new URL(page.url).origin;
11
+ const insecure = [];
12
+ const external = new Set();
13
+ const noIntegrity = [];
14
+ for (const t of findTags(page.body, ["script", "iframe", "link"])) {
15
+ if (t.name === "link" && !/stylesheet/i.test(t.attrs["rel"] ?? ""))
16
+ continue;
17
+ const url = resolveUrl(t.name === "link" ? t.attrs["href"] : t.attrs["src"], page.url);
18
+ if (!url || url.origin === origin)
19
+ continue;
20
+ external.add(url.origin);
21
+ if (url.protocol === "http:")
22
+ insecure.push(url.href);
23
+ else if (t.name !== "iframe" && !t.attrs["integrity"])
24
+ noIntegrity.push(url.href);
25
+ }
26
+ if (insecure.length) {
27
+ return { status: "fail", detail: `${insecure.length} third-party resource(s) are loaded over plain HTTP and can be tampered with.`, evidence: insecure };
28
+ }
29
+ if (external.size) {
30
+ return {
31
+ status: "manual",
32
+ detail: `Content is loaded from ${external.size} external origin(s); ${noIntegrity.length} script/style file(s) have no integrity attribute. Confirm each origin is trusted. ${LIMITED}`,
33
+ evidence: [...external].sort(),
34
+ };
35
+ }
36
+ return { status: "pass", detail: `The landing page loads no scripts, styles or frames from other origins. ${LIMITED}` };
37
+ },
38
+ };
39
+ // --- #13 Insecure transmission of sensitive information --------------------------------
40
+ export const insecureForms = {
41
+ key: "insecure-forms",
42
+ kind: "url",
43
+ async run(ctx) {
44
+ const page = await landingPage(ctx);
45
+ const forms = findTags(page.body, ["form"]);
46
+ const overHttp = forms.map((f) => resolveUrl(f.attrs["action"] || page.url, page.url)).filter((u) => u?.protocol === "http:");
47
+ const hasPassword = findTags(page.body, ["input"]).some((i) => (i.attrs["type"] ?? "").toLowerCase() === "password");
48
+ if (overHttp.length) {
49
+ return { status: "fail", detail: `${overHttp.length} form(s) submit over plain HTTP${hasPassword ? ", including a password field" : ""}.`, evidence: overHttp.map((u) => u.href) };
50
+ }
51
+ if (new URL(page.url).protocol !== "https:")
52
+ return { status: "fail", detail: "The page is served over plain HTTP, so anything submitted from it is unencrypted." };
53
+ return { status: "pass", detail: `${forms.length} form(s) on the landing page submit over HTTPS. ${LIMITED}` };
54
+ },
55
+ };
56
+ // --- #15 Mixed content ------------------------------------------------------------------
57
+ export const mixedContent = {
58
+ key: "mixed-content",
59
+ kind: "url",
60
+ async run(ctx) {
61
+ const page = await landingPage(ctx);
62
+ if (new URL(page.url).protocol !== "https:") {
63
+ return { status: "manual", detail: "The site is not served over HTTPS (see item 14), so mixed content does not apply yet." };
64
+ }
65
+ const mixed = new Set();
66
+ for (const t of findTags(page.body, ["script", "img", "iframe", "link", "audio", "video", "source", "embed", "object"])) {
67
+ if (t.name === "link" && !/stylesheet|icon|preload|manifest/i.test(t.attrs["rel"] ?? ""))
68
+ continue;
69
+ const raw = t.attrs["src"] ?? t.attrs["href"] ?? t.attrs["data"];
70
+ if (raw && /^http:\/\//i.test(raw.trim()))
71
+ mixed.add(`<${t.name}> ${raw.trim()}`);
72
+ }
73
+ if (mixed.size)
74
+ return { status: "fail", detail: `${mixed.size} resource(s) on the HTTPS page are loaded over plain HTTP.`, evidence: [...mixed] };
75
+ return { status: "pass", detail: `No HTTP resources are referenced by the HTTPS landing page. ${LIMITED}` };
76
+ },
77
+ };
78
+ // --- #83 Content type / #84 character set ------------------------------------------------
79
+ const GENERIC_TYPES = /^(application\/octet-stream|\*\/\*|unknown\/unknown|application\/unknown)$/i;
80
+ export const contentType = {
81
+ key: "content-type",
82
+ kind: "url",
83
+ async run(ctx) {
84
+ const page = await landingPage(ctx);
85
+ const origin = new URL(page.url).origin;
86
+ const problems = [];
87
+ const checked = [];
88
+ const examine = (url, header, expected, what) => {
89
+ const type = (header ?? "").split(";")[0].trim();
90
+ checked.push(`${url}: ${type || "(none)"}`);
91
+ if (!type)
92
+ problems.push(`${url}: no Content-Type`);
93
+ else if (GENERIC_TYPES.test(type))
94
+ problems.push(`${url}: generic type ${type}`);
95
+ else if (!expected.test(type))
96
+ problems.push(`${url}: ${type} for ${what}`);
97
+ };
98
+ examine(page.url, page.headers["content-type"], /html|xml/i, "an HTML page");
99
+ const firstOf = (tag, attr, filter) => findTags(page.body, [tag]).filter((t) => filter(t.attrs)).map((t) => resolveUrl(t.attrs[attr], page.url)).find((u) => u?.origin === origin);
100
+ const script = firstOf("script", "src", () => true);
101
+ const style = firstOf("link", "href", (a) => /stylesheet/i.test(a["rel"] ?? ""));
102
+ if (script) {
103
+ const res = await ctx.request(script.href);
104
+ if (res.status === 200)
105
+ examine(script.href, res.headers["content-type"], /javascript|ecmascript/i, "a script");
106
+ }
107
+ if (style) {
108
+ const res = await ctx.request(style.href);
109
+ if (res.status === 200)
110
+ examine(style.href, res.headers["content-type"], /css/i, "a stylesheet");
111
+ }
112
+ if (problems.length)
113
+ return { status: "fail", detail: `${problems.length} response(s) have a missing, generic or wrong Content-Type.`, evidence: problems };
114
+ return { status: "pass", detail: `Content-Type is specific on ${checked.length} response(s) checked.`, evidence: checked };
115
+ },
116
+ };
117
+ export const charset = {
118
+ key: "charset",
119
+ kind: "url",
120
+ async run(ctx) {
121
+ const page = await landingPage(ctx);
122
+ const header = page.headers["content-type"] ?? "";
123
+ const inHeader = /charset\s*=\s*["']?([\w-]+)/i.exec(header)?.[1];
124
+ const head = page.body.slice(0, 2048);
125
+ const inMeta = /<meta[^>]+charset\s*=\s*["']?([\w-]+)/i.exec(head)?.[1];
126
+ if (inHeader)
127
+ return { status: "pass", detail: `Character set ${inHeader} is declared in the Content-Type header.`, evidence: [header] };
128
+ if (inMeta)
129
+ return { status: "pass", detail: `Character set ${inMeta} is declared in a <meta> tag (not in the Content-Type header).` };
130
+ return { status: "fail", detail: "No character set is declared in the Content-Type header or in a <meta> tag near the top of the page.", evidence: [header || "(no Content-Type)"] };
131
+ },
132
+ };
133
+ //# sourceMappingURL=page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page.js","sourceRoot":"","sources":["../../../src/checks/content/page.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare const cookieSecure: import("../../types.js").UrlCheck;
2
+ export declare const cookieHttpOnly: import("../../types.js").UrlCheck;
3
+ //# sourceMappingURL=cookie-flags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookie-flags.d.ts","sourceRoot":"","sources":["../../../src/checks/cookies/cookie-flags.ts"],"names":[],"mappings":"AA2CA,eAAO,MAAM,YAAY,mCAAiD,CAAC;AAC3E,eAAO,MAAM,cAAc,mCAAuD,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { landingPage, looksLikeSessionCookie, parseSetCookie } from "../util.js";
2
+ const NO_COOKIES = {
3
+ status: "manual",
4
+ detail: "No cookies were set on the landing page. Session cookies issued after login must be reviewed manually.",
5
+ };
6
+ async function observedCookies(ctx) {
7
+ const res = await landingPage(ctx);
8
+ return res.setCookies.map(parseSetCookie);
9
+ }
10
+ /** Annex B items 67/68 concern session cookies: fail on session-like cookies, list the rest as advisory. */
11
+ function flagCheck(key, flag, label) {
12
+ return {
13
+ kind: "url",
14
+ key,
15
+ async run(ctx) {
16
+ const cookies = await observedCookies(ctx);
17
+ if (cookies.length === 0)
18
+ return NO_COOKIES;
19
+ const missing = cookies.filter((c) => !c[flag]);
20
+ const sessionMissing = missing.filter((c) => looksLikeSessionCookie(c.name));
21
+ if (sessionMissing.length > 0) {
22
+ return {
23
+ status: "fail",
24
+ detail: `Session-like cookie(s) set without the ${label} flag.`,
25
+ evidence: sessionMissing.map((c) => c.name),
26
+ };
27
+ }
28
+ if (missing.length > 0) {
29
+ return {
30
+ status: "pass",
31
+ detail: `No session-like cookie lacks ${label}. Cookies listed below lack it; confirm none of them carries session data.`,
32
+ evidence: missing.map((c) => c.name),
33
+ };
34
+ }
35
+ return { status: "pass", detail: `All ${cookies.length} cookie(s) set ${label}.`, evidence: cookies.map((c) => c.name) };
36
+ },
37
+ };
38
+ }
39
+ export const cookieSecure = flagCheck("cookie-secure", "secure", "Secure");
40
+ export const cookieHttpOnly = flagCheck("cookie-httponly", "httpOnly", "HttpOnly");
41
+ //# sourceMappingURL=cookie-flags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookie-flags.js","sourceRoot":"","sources":["../../../src/checks/cookies/cookie-flags.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,cAAc,EAAqB,MAAM,YAAY,CAAC;AAEpG,MAAM,UAAU,GAAiB;IAC/B,MAAM,EAAE,QAAQ;IAChB,MAAM,EACJ,wGAAwG;CAC3G,CAAC;AAEF,KAAK,UAAU,eAAe,CAAC,GAAgB;IAC7C,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,CAAC;IACnC,OAAO,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;AAC5C,CAAC;AAED,4GAA4G;AAC5G,SAAS,SAAS,CAAC,GAAW,EAAE,IAA2B,EAAE,KAAa;IACxE,OAAO;QACL,IAAI,EAAE,KAAK;QACX,GAAG;QACH,KAAK,CAAC,GAAG,CAAC,GAAG;YACX,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,UAAU,CAAC;YAC5C,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;YAChD,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;YAC7E,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC9B,OAAO;oBACL,MAAM,EAAE,MAAM;oBACd,MAAM,EAAE,0CAA0C,KAAK,QAAQ;oBAC/D,QAAQ,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;iBAC5C,CAAC;YACJ,CAAC;YACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,OAAO;oBACL,MAAM,EAAE,MAAM;oBACd,MAAM,EAAE,gCAAgC,KAAK,4EAA4E;oBACzH,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;iBACrC,CAAC;YACJ,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,OAAO,CAAC,MAAM,kBAAkB,KAAK,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3H,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,SAAS,CAAC,eAAe,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAC3E,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAAC,iBAAiB,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Check } from "../../types.js";
2
+ /** #69 Non-restrictive domain on session cookies. */
3
+ export declare const cookieDomain: Check;
4
+ /** #70 Non-restrictive or missing path on session cookies. */
5
+ export declare const cookiePath: Check;
6
+ //# sourceMappingURL=cookie-scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookie-scope.d.ts","sourceRoot":"","sources":["../../../src/checks/cookies/cookie-scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAA6B,MAAM,gBAAgB,CAAC;AAavE,qDAAqD;AACrD,eAAO,MAAM,YAAY,EAAE,KAuB1B,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,UAAU,EAAE,KAqBxB,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { landingPage, looksLikeSessionCookie, parseSetCookie } from "../util.js";
2
+ const NONE = {
3
+ status: "manual",
4
+ detail: "No session-like cookies were set on the landing page. Session cookies issued after login must be reviewed manually.",
5
+ };
6
+ async function sessionCookies(ctx) {
7
+ const res = await landingPage(ctx);
8
+ return { host: new URL(res.url).hostname, cookies: res.setCookies.map(parseSetCookie).filter((c) => looksLikeSessionCookie(c.name)) };
9
+ }
10
+ /** #69 Non-restrictive domain on session cookies. */
11
+ export const cookieDomain = {
12
+ key: "cookie-domain",
13
+ kind: "url",
14
+ async run(ctx) {
15
+ const { host, cookies } = await sessionCookies(ctx);
16
+ if (cookies.length === 0)
17
+ return NONE;
18
+ const broad = cookies.filter((c) => c.domain && c.domain.replace(/^\./, "").toLowerCase() !== host.toLowerCase());
19
+ if (broad.length) {
20
+ return {
21
+ status: "fail",
22
+ detail: "Session cookie(s) are scoped to a parent domain, so every subdomain receives them.",
23
+ evidence: broad.map((c) => `${c.name}: Domain=${c.domain}`),
24
+ };
25
+ }
26
+ const explicit = cookies.filter((c) => c.domain);
27
+ return {
28
+ status: "pass",
29
+ detail: explicit.length
30
+ ? `Session cookie Domain matches the host (${host}); note that an explicit Domain also includes its subdomains.`
31
+ : "Session cookies set no Domain attribute, so they are host-only (most restrictive).",
32
+ evidence: cookies.map((c) => c.name),
33
+ };
34
+ },
35
+ };
36
+ /** #70 Non-restrictive or missing path on session cookies. */
37
+ export const cookiePath = {
38
+ key: "cookie-path",
39
+ kind: "url",
40
+ async run(ctx) {
41
+ const { cookies } = await sessionCookies(ctx);
42
+ if (cookies.length === 0)
43
+ return NONE;
44
+ const missing = cookies.filter((c) => c.path === undefined);
45
+ if (missing.length) {
46
+ return { status: "fail", detail: "Session cookie(s) set no Path attribute.", evidence: missing.map((c) => c.name) };
47
+ }
48
+ const root = cookies.filter((c) => c.path === "/");
49
+ if (root.length) {
50
+ // Whether "/" is too broad depends on what else is hosted on the same host.
51
+ return {
52
+ status: "manual",
53
+ detail: 'Session cookie(s) use Path=/, covering the whole host. Acceptable only if no other application shares this host.',
54
+ evidence: root.map((c) => c.name),
55
+ };
56
+ }
57
+ return { status: "pass", detail: "Session cookies are limited to a specific path.", evidence: cookies.map((c) => `${c.name}: Path=${c.path}`) };
58
+ },
59
+ };
60
+ //# sourceMappingURL=cookie-scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookie-scope.js","sourceRoot":"","sources":["../../../src/checks/cookies/cookie-scope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Check } from "../../types.js";
2
+ /** #91 Unsupported or end-of-life software versions, from version banners. */
3
+ export declare const serverBanner: Check;
4
+ //# sourceMappingURL=server-banner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-banner.d.ts","sourceRoot":"","sources":["../../../src/checks/deployment/server-banner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAqB5C,8EAA8E;AAC9E,eAAO,MAAM,YAAY,EAAE,KAiC1B,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { findTags } from "../html.js";
2
+ import { landingPage } from "../util.js";
3
+ /**
4
+ * End-of-life thresholds, reviewed 2026-10. Versions below `eolBelow` no longer receive security fixes.
5
+ * Kept conservative: distributions back-port fixes, so only clearly unsupported lines are flagged.
6
+ */
7
+ const PRODUCTS = [
8
+ { name: "PHP", pattern: /PHP\/(\d+)\.(\d+)/i, eolBelow: [8, 2] },
9
+ { name: "Apache httpd", pattern: /Apache\/(\d+)\.(\d+)/i, eolBelow: [2, 4] },
10
+ { name: "nginx", pattern: /nginx\/(\d+)\.(\d+)/i, eolBelow: [1, 18] },
11
+ { name: "Microsoft IIS", pattern: /Microsoft-IIS\/(\d+)\.(\d+)/i, eolBelow: [10, 0] },
12
+ { name: "OpenSSL", pattern: /OpenSSL\/(\d+)\.(\d+)/i, eolBelow: [3, 0] },
13
+ { name: "Apache Tomcat", pattern: /Tomcat\/(\d+)\.(\d+)/i, eolBelow: [9, 0] },
14
+ { name: "Python", pattern: /Python\/(\d+)\.(\d+)/i, eolBelow: [3, 9] },
15
+ { name: "ASP.NET", pattern: /^(\d+)\.(\d+)[\d.]*$/, eolBelow: [4, 0] },
16
+ { name: "Drupal", pattern: /Drupal (\d+)(?:\.(\d+))?/i, eolBelow: [10, 0] },
17
+ { name: "Joomla!", pattern: /Joomla!? (\d+)\.(\d+)/i, eolBelow: [4, 0] },
18
+ ];
19
+ /** #91 Unsupported or end-of-life software versions, from version banners. */
20
+ export const serverBanner = {
21
+ key: "server-banner",
22
+ kind: "url",
23
+ async run(ctx) {
24
+ const page = await landingPage(ctx);
25
+ const banners = [];
26
+ for (const h of ["server", "x-powered-by", "x-aspnet-version", "x-aspnetmvc-version", "x-generator"]) {
27
+ if (page.headers[h])
28
+ banners.push([h, page.headers[h]]);
29
+ }
30
+ for (const m of findTags(page.body, ["meta"])) {
31
+ if ((m.attrs["name"] ?? "").toLowerCase() === "generator" && m.attrs["content"])
32
+ banners.push(["meta generator", m.attrs["content"]]);
33
+ }
34
+ const eol = [];
35
+ const disclosed = [];
36
+ for (const [source, value] of banners) {
37
+ let matched = false;
38
+ for (const p of PRODUCTS) {
39
+ if (p.name === "ASP.NET" && source !== "x-aspnet-version")
40
+ continue;
41
+ const m = p.pattern.exec(value);
42
+ if (!m)
43
+ continue;
44
+ matched = true;
45
+ const [major, minor] = [Number(m[1]), Number(m[2] ?? 0)];
46
+ const old = major < p.eolBelow[0] || (major === p.eolBelow[0] && minor < p.eolBelow[1]);
47
+ (old ? eol : disclosed).push(`${p.name} ${major}.${minor} (${source}: ${value})`);
48
+ }
49
+ if (!matched && /\d+\.\d+/.test(value))
50
+ disclosed.push(`${source}: ${value}`);
51
+ }
52
+ if (eol.length)
53
+ return { status: "fail", detail: `${eol.length} end-of-life software version(s) identified from version banners.`, evidence: [...eol, ...disclosed] };
54
+ if (disclosed.length) {
55
+ return { status: "manual", detail: "Software versions are disclosed in banners. None is on this tool's end-of-life list; verify their support status, and consider hiding the versions.", evidence: disclosed };
56
+ }
57
+ return { status: "pass", detail: "No software version is disclosed in response headers or generator tags, so no end-of-life version could be identified." };
58
+ },
59
+ };
60
+ //# sourceMappingURL=server-banner.js.map