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
@@ -0,0 +1,186 @@
1
+ import { findTags, htmlComments, resolveUrl } from "../html.js";
2
+ import { effectiveOrigin, landingPage } from "../util.js";
3
+ // --- #3 Accessible debug functionality ---------------------------------------------
4
+ /** Well-known debug/status endpoints. A finding needs matching content, not just HTTP 200. */
5
+ const DEBUG_PROBES = [
6
+ { path: "/phpinfo.php", signature: /phpinfo\(\)|<title>PHP [\d.]+ - phpinfo|PHP Version/i, what: "PHP configuration page" },
7
+ { path: "/info.php", signature: /phpinfo\(\)|PHP Version/i, what: "PHP configuration page" },
8
+ { path: "/server-status", signature: /Apache Server Status/i, what: "Apache server status" },
9
+ { path: "/server-info", signature: /Apache Server Information/i, what: "Apache server information" },
10
+ { path: "/elmah.axd", signature: /Error Log for/i, what: "ELMAH error log" },
11
+ { path: "/trace.axd", signature: /Application Trace/i, what: "ASP.NET trace viewer" },
12
+ { path: "/actuator", signature: /"_links"\s*:/, what: "Spring Boot actuator index" },
13
+ { path: "/actuator/env", signature: /"propertySources"\s*:/, what: "Spring Boot environment" },
14
+ { path: "/_profiler", signature: /Symfony Profiler/i, what: "Symfony profiler" },
15
+ { path: "/__debug__/", signature: /djdt|Django Debug Toolbar/i, what: "Django debug toolbar" },
16
+ { path: "/telescope", signature: /Laravel Telescope/i, what: "Laravel Telescope" },
17
+ { path: "/debug/pprof/", signature: /Types of profiles available/i, what: "Go pprof profiler" },
18
+ { path: "/debug/default/view", signature: /Yii Debugger/i, what: "Yii debugger" },
19
+ ];
20
+ export const debugPages = {
21
+ key: "debug-pages",
22
+ kind: "url",
23
+ async run(ctx) {
24
+ const origin = await effectiveOrigin(ctx);
25
+ const found = [];
26
+ await Promise.all(DEBUG_PROBES.map(async (p) => {
27
+ const res = await ctx.request(`${origin}${p.path}`, { followRedirects: false });
28
+ if (res.status === 200 && p.signature.test(res.body))
29
+ found.push(`${origin}${p.path}: ${p.what}`);
30
+ }));
31
+ if (found.length)
32
+ return { status: "fail", detail: `${found.length} debug or status page(s) are publicly accessible.`, evidence: found.sort() };
33
+ return { status: "pass", detail: `None of ${DEBUG_PROBES.length} well-known debug/status endpoints are exposed.` };
34
+ },
35
+ };
36
+ // --- #4 Sensitive information in error messages -------------------------------------
37
+ const NOT_FOUND_PROBE = "/insa-check-not-found-probe";
38
+ const ERROR_LEAKS = [
39
+ { signature: /Traceback \(most recent call last\)/, what: "Python stack trace" },
40
+ { signature: /Django Version:|You're seeing this error because you have <code>DEBUG = True/, what: "Django debug error page" },
41
+ { signature: /at [\w$.]+\([\w$]+\.java:\d+\)/, what: "Java stack trace" },
42
+ { signature: /Server Error in '.*' Application|System\.Web\.|ASP\.NET Version:/, what: "ASP.NET detailed error" },
43
+ { signature: /Whoops, looks like something went wrong|Symfony\\Component|Illuminate\\/, what: "PHP framework debug page" },
44
+ { signature: /(Fatal error|Warning|Notice)<\/b>:[^<]* in <b>[^<]+<\/b> on line <b>\d+/, what: "PHP error with file path" },
45
+ { signature: /SQLSTATE\[|ORA-\d{5}|You have an error in your SQL syntax|PG::\w+Error/, what: "database error message" },
46
+ { signature: /at \w[\w.<>]* \((?:\/|[A-Z]:\\)[^)]+\.[cm]?[jt]s:\d+:\d+\)/, what: "Node.js stack trace" },
47
+ { signature: /Rails\.root:|ActionController::RoutingError/, what: "Rails debug error page" },
48
+ ];
49
+ export const errorMessages = {
50
+ key: "error-messages",
51
+ kind: "url",
52
+ async run(ctx) {
53
+ const origin = await effectiveOrigin(ctx);
54
+ const res = await ctx.request(`${origin}${NOT_FOUND_PROBE}`);
55
+ const leaks = ERROR_LEAKS.filter((l) => l.signature.test(res.body)).map((l) => l.what);
56
+ if (leaks.length) {
57
+ return { status: "fail", detail: `The "not found" page reveals technical details: ${leaks.join(", ")}.`, evidence: [`${origin}${NOT_FOUND_PROBE} → HTTP ${res.status}`] };
58
+ }
59
+ // A clean 404 page says nothing about errors caused by invalid input, or about log contents.
60
+ return {
61
+ status: "manual",
62
+ detail: `The "not found" page (HTTP ${res.status}) shows no stack traces or internal details. Errors from invalid input, and log contents, still need manual review.`,
63
+ };
64
+ },
65
+ };
66
+ // --- #5 Sensitive information in robots.txt ------------------------------------------
67
+ const ROBOTS_SENSITIVE = /backup|\.sql|\.env|\.git|secret|private|passw|config|dump|\.bak|database|\.zip|\.tar|credential|token/i;
68
+ const ROBOTS_ADMIN = /admin|login|dashboard|manage|internal|staff|cpanel|phpmyadmin/i;
69
+ export const robotsTxt = {
70
+ key: "robots-txt",
71
+ kind: "url",
72
+ async run(ctx) {
73
+ const origin = await effectiveOrigin(ctx);
74
+ const res = await ctx.request(`${origin}/robots.txt`);
75
+ if (res.status !== 200 || !/^\s*(user-agent|disallow|allow|sitemap)\s*:/im.test(res.body)) {
76
+ return { status: "pass", detail: "No robots.txt is served, so it discloses nothing." };
77
+ }
78
+ const paths = [...res.body.matchAll(/^\s*(?:dis)?allow\s*:\s*(\S+)/gim)].map((m) => m[1]);
79
+ const sensitive = [...new Set(paths.filter((p) => ROBOTS_SENSITIVE.test(p)))];
80
+ const admin = [...new Set(paths.filter((p) => !ROBOTS_SENSITIVE.test(p) && ROBOTS_ADMIN.test(p)))];
81
+ if (sensitive.length) {
82
+ return { status: "fail", detail: `robots.txt advertises ${sensitive.length} sensitive-looking path(s).`, evidence: [...sensitive, ...admin] };
83
+ }
84
+ if (admin.length) {
85
+ return { status: "manual", detail: `robots.txt lists ${admin.length} administrative path(s); confirm each still requires authentication.`, evidence: admin };
86
+ }
87
+ return { status: "pass", detail: `robots.txt lists ${paths.length} path(s); none look sensitive.` };
88
+ },
89
+ };
90
+ // --- #6 Sensitive information in page source ------------------------------------------
91
+ const MAX_SCRIPTS = 5;
92
+ /** Tokens that should never be in client-side code. */
93
+ const SECRET_PATTERNS = [
94
+ { signature: /AKIA[0-9A-Z]{16}/, what: "AWS access key ID" },
95
+ { signature: /-----BEGIN (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----/, what: "private key" },
96
+ { signature: /\bsk_live_[0-9a-zA-Z]{16,}/, what: "Stripe secret key" },
97
+ { signature: /\bgh[pousr]_[0-9A-Za-z]{30,}/, what: "GitHub token" },
98
+ { signature: /\bxox[baprs]-[0-9A-Za-z-]{10,}/, what: "Slack token" },
99
+ { signature: /\b(?:password|passwd|pwd)\s*[:=]\s*["'][^"']{4,}["']/i, what: "hard-coded password" },
100
+ ];
101
+ /** Things worth a human look; often legitimate (public API keys, leftover notes). */
102
+ const REVIEW_PATTERNS = [
103
+ { signature: /\b(?:api[_-]?key|secret|token)["']?\s*[:=]\s*["'][A-Za-z0-9_\-/+=]{16,}["']/i, what: "API key or token assignment" },
104
+ { signature: /AIza[0-9A-Za-z_-]{35}/, what: "Google API key" },
105
+ ];
106
+ const COMMENT_HINT = /todo|fixme|hack|password|passwd|secret|internal|debug|staging|credential/i;
107
+ export const pageSource = {
108
+ key: "page-source-secrets",
109
+ kind: "url",
110
+ async run(ctx) {
111
+ const page = await landingPage(ctx);
112
+ const origin = new URL(page.url).origin;
113
+ const sources = [{ url: page.url, body: page.body }];
114
+ const scripts = findTags(page.body, ["script"])
115
+ .map((t) => resolveUrl(t.attrs["src"], page.url))
116
+ .filter((u) => !!u && u.origin === origin)
117
+ .slice(0, MAX_SCRIPTS);
118
+ for (const u of scripts) {
119
+ const res = await ctx.request(u.href);
120
+ if (res.status === 200)
121
+ sources.push({ url: u.href, body: res.body });
122
+ }
123
+ // Evidence names the kind of data and where it is, never the value.
124
+ const secrets = [];
125
+ const review = [];
126
+ for (const { url, body } of sources) {
127
+ for (const p of SECRET_PATTERNS)
128
+ if (p.signature.test(body))
129
+ secrets.push(`${p.what} in ${url}`);
130
+ for (const p of REVIEW_PATTERNS)
131
+ if (p.signature.test(body))
132
+ review.push(`${p.what} in ${url}`);
133
+ }
134
+ const comments = htmlComments(page.body).filter((c) => COMMENT_HINT.test(c));
135
+ if (comments.length)
136
+ review.push(`${comments.length} HTML comment(s) mentioning passwords, TODOs or internal notes in ${page.url}`);
137
+ if (secrets.length)
138
+ return { status: "fail", detail: `${secrets.length} secret(s) found in page source or scripts (values not shown).`, evidence: [...secrets, ...review] };
139
+ if (review.length)
140
+ return { status: "manual", detail: "Page source contains items to review; they may be public by design (values not shown).", evidence: review };
141
+ return { status: "pass", detail: `No secrets or suspicious comments found in the landing page and ${sources.length - 1} same-origin script(s).` };
142
+ },
143
+ };
144
+ // --- #7 Disclosure of internal addresses -----------------------------------------------
145
+ const PRIVATE_IP = /\b(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3})\b/g;
146
+ const validIp = (ip) => ip.split(".").every((o) => Number(o) <= 255);
147
+ export const internalAddresses = {
148
+ key: "internal-addresses",
149
+ kind: "url",
150
+ async run(ctx) {
151
+ const page = await landingPage(ctx);
152
+ const origin = new URL(page.url).origin;
153
+ const notFound = await ctx.request(`${origin}${NOT_FOUND_PROBE}`);
154
+ const targetHost = new URL(page.url).hostname;
155
+ const found = new Set();
156
+ for (const res of [page, notFound]) {
157
+ const where = res === page ? "landing page" : "error page";
158
+ for (const [name, value] of Object.entries(res.headers)) {
159
+ for (const ip of value.match(PRIVATE_IP) ?? [])
160
+ if (validIp(ip) && ip !== targetHost)
161
+ found.add(`${ip} in ${name} header (${where})`);
162
+ }
163
+ for (const ip of res.body.match(PRIVATE_IP) ?? [])
164
+ if (validIp(ip) && ip !== targetHost)
165
+ found.add(`${ip} in body (${where})`);
166
+ }
167
+ if (found.size)
168
+ return { status: "fail", detail: `${found.size} internal address(es) disclosed in responses.`, evidence: [...found].sort() };
169
+ return { status: "pass", detail: "No private IP addresses found in the headers or bodies of the landing page and error page." };
170
+ },
171
+ };
172
+ // --- #10 Externally archived pages (note only) -------------------------------------------
173
+ export const archivedPages = {
174
+ key: "archived-pages",
175
+ kind: "url",
176
+ async run(ctx) {
177
+ const host = new URL((await landingPage(ctx)).url).host;
178
+ // Deliberately not queried: sending the target to a third-party service is outside this tool's privacy rules.
179
+ return {
180
+ status: "manual",
181
+ detail: "Not checked automatically (no third-party services are contacted). Search web archives and search-engine caches for old pages exposing sensitive content.",
182
+ evidence: [`https://web.archive.org/web/*/${host}/*`],
183
+ };
184
+ },
185
+ };
186
+ //# sourceMappingURL=more.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"more.js","sourceRoot":"","sources":["../../../src/checks/info-disclosure/more.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Check } from "../../types.js";
2
+ export declare const certificate: Check;
3
+ //# sourceMappingURL=certificate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"certificate.d.ts","sourceRoot":"","sources":["../../../src/checks/tls/certificate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAgB5C,eAAO,MAAM,WAAW,EAAE,KAqCzB,CAAC"}
@@ -0,0 +1,49 @@
1
+ const MIN_RSA_BITS = 2048;
2
+ const EXPIRY_WARNING_DAYS = 30;
3
+ const ERROR_MEANINGS = {
4
+ CERT_HAS_EXPIRED: "the certificate has expired",
5
+ CERT_NOT_YET_VALID: "the certificate is not yet valid",
6
+ DEPTH_ZERO_SELF_SIGNED_CERT: "the certificate is self-signed",
7
+ SELF_SIGNED_CERT_IN_CHAIN: "the chain contains an untrusted self-signed certificate",
8
+ UNABLE_TO_VERIFY_LEAF_SIGNATURE: "the chain is incomplete or the issuer is untrusted",
9
+ UNABLE_TO_GET_ISSUER_CERT_LOCALLY: "the issuer is not a trusted CA (or an intermediate is missing)",
10
+ ERR_TLS_CERT_ALTNAME_INVALID: "the certificate does not cover this hostname",
11
+ CERT_REVOKED: "the certificate has been revoked",
12
+ };
13
+ export const certificate = {
14
+ key: "certificate",
15
+ kind: "url",
16
+ async run(ctx) {
17
+ const info = await ctx.tlsInfo();
18
+ const cert = info.cert;
19
+ if (!cert)
20
+ return { status: "fail", detail: "The server presented no certificate." };
21
+ const evidence = [
22
+ `Subject: ${cert.subject}`,
23
+ `Issuer: ${cert.issuer}`,
24
+ `Valid: ${cert.validFrom} → ${cert.validTo}`,
25
+ `Key: ${cert.keyType ?? "unknown"} ${cert.bits ?? "?"} bits`,
26
+ ];
27
+ const problems = [];
28
+ if (!info.authorized) {
29
+ const code = info.authorizationError ?? "UNKNOWN";
30
+ problems.push(`not trusted: ${ERROR_MEANINGS[code] ?? code} (${code})`);
31
+ }
32
+ if (cert.daysRemaining < 0 && info.authorizationError !== "CERT_HAS_EXPIRED") {
33
+ problems.push(`expired ${-cert.daysRemaining} days ago`);
34
+ }
35
+ if (cert.keyType === "RSA" && cert.bits !== undefined && cert.bits < MIN_RSA_BITS) {
36
+ problems.push(`weak RSA key (${cert.bits} bits, minimum ${MIN_RSA_BITS})`);
37
+ }
38
+ if (problems.length > 0) {
39
+ return { status: "fail", detail: `Certificate ${problems.join("; ")}.`, evidence };
40
+ }
41
+ const soon = cert.daysRemaining <= EXPIRY_WARNING_DAYS ? ` Warning: expires in ${cert.daysRemaining} days.` : "";
42
+ return {
43
+ status: "pass",
44
+ detail: `Trusted certificate, valid for ${cert.daysRemaining} more days.${soon}`,
45
+ evidence,
46
+ };
47
+ },
48
+ };
49
+ //# sourceMappingURL=certificate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"certificate.js","sourceRoot":"","sources":["../../../src/checks/tls/certificate.ts"],"names":[],"mappings":"AAEA,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,MAAM,cAAc,GAA2B;IAC7C,gBAAgB,EAAE,6BAA6B;IAC/C,kBAAkB,EAAE,kCAAkC;IACtD,2BAA2B,EAAE,gCAAgC;IAC7D,yBAAyB,EAAE,yDAAyD;IACpF,+BAA+B,EAAE,oDAAoD;IACrF,iCAAiC,EAAE,gEAAgE;IACnG,4BAA4B,EAAE,8CAA8C;IAC5E,YAAY,EAAE,kCAAkC;CACjD,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAU;IAChC,GAAG,EAAE,aAAa;IAClB,IAAI,EAAE,KAAK;IACX,KAAK,CAAC,GAAG,CAAC,GAAG;QACX,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,sCAAsC,EAAE,CAAC;QAErF,MAAM,QAAQ,GAAG;YACf,YAAY,IAAI,CAAC,OAAO,EAAE;YAC1B,WAAW,IAAI,CAAC,MAAM,EAAE;YACxB,UAAU,IAAI,CAAC,SAAS,MAAM,IAAI,CAAC,OAAO,EAAE;YAC5C,QAAQ,IAAI,CAAC,OAAO,IAAI,SAAS,IAAI,IAAI,CAAC,IAAI,IAAI,GAAG,OAAO;SAC7D,CAAC;QACF,MAAM,QAAQ,GAAa,EAAE,CAAC;QAE9B,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB,IAAI,SAAS,CAAC;YAClD,QAAQ,CAAC,IAAI,CAAC,gBAAgB,cAAc,CAAC,IAAI,CAAC,IAAI,IAAI,KAAK,IAAI,GAAG,CAAC,CAAC;QAC1E,CAAC;QACD,IAAI,IAAI,CAAC,aAAa,GAAG,CAAC,IAAI,IAAI,CAAC,kBAAkB,KAAK,kBAAkB,EAAE,CAAC;YAC7E,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,aAAa,WAAW,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,GAAG,YAAY,EAAE,CAAC;YAClF,QAAQ,CAAC,IAAI,CAAC,iBAAiB,IAAI,CAAC,IAAI,kBAAkB,YAAY,GAAG,CAAC,CAAC;QAC7E,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,CAAC;QACrF,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,IAAI,mBAAmB,CAAC,CAAC,CAAC,wBAAwB,IAAI,CAAC,aAAa,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QACjH,OAAO;YACL,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,kCAAkC,IAAI,CAAC,aAAa,cAAc,IAAI,EAAE;YAChF,QAAQ;SACT,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { Check } from "../../types.js";
2
+ /** Six months, the common minimum (e.g. Mozilla Observatory). */
3
+ export declare const HSTS_MIN_MAX_AGE = 15768000;
4
+ export declare const hsts: Check;
5
+ //# sourceMappingURL=hsts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hsts.d.ts","sourceRoot":"","sources":["../../../src/checks/tls/hsts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAG5C,iEAAiE;AACjE,eAAO,MAAM,gBAAgB,WAAa,CAAC;AAE3C,eAAO,MAAM,IAAI,EAAE,KA8BlB,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { httpsUrl, parseDirectives } from "../util.js";
2
+ /** Six months, the common minimum (e.g. Mozilla Observatory). */
3
+ export const HSTS_MIN_MAX_AGE = 15_768_000;
4
+ export const hsts = {
5
+ key: "hsts",
6
+ kind: "url",
7
+ async run(ctx) {
8
+ const res = await ctx.request(httpsUrl(ctx));
9
+ if (new URL(res.url).protocol !== "https:") {
10
+ return { status: "fail", detail: "The HTTPS URL redirects back to HTTP, so HSTS cannot apply." };
11
+ }
12
+ const header = res.headers["strict-transport-security"];
13
+ if (!header) {
14
+ return { status: "fail", detail: "No Strict-Transport-Security header on the HTTPS response." };
15
+ }
16
+ const directives = parseDirectives(header);
17
+ const maxAge = Number(directives.get("max-age"));
18
+ if (!directives.has("max-age") || !Number.isInteger(maxAge)) {
19
+ return { status: "fail", detail: "Strict-Transport-Security has no valid max-age.", evidence: [header] };
20
+ }
21
+ if (maxAge < HSTS_MIN_MAX_AGE) {
22
+ return {
23
+ status: "fail",
24
+ detail: `HSTS max-age is ${maxAge}s; at least ${HSTS_MIN_MAX_AGE}s (6 months) is expected.`,
25
+ evidence: [header],
26
+ };
27
+ }
28
+ const extras = [
29
+ directives.has("includesubdomains") ? "includeSubDomains" : "no includeSubDomains",
30
+ directives.has("preload") ? "preload" : null,
31
+ ].filter(Boolean);
32
+ return { status: "pass", detail: `HSTS enabled, max-age ${maxAge}s (${extras.join(", ")}).`, evidence: [header] };
33
+ },
34
+ };
35
+ //# sourceMappingURL=hsts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hsts.js","sourceRoot":"","sources":["../../../src/checks/tls/hsts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEvD,iEAAiE;AACjE,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAC;AAE3C,MAAM,CAAC,MAAM,IAAI,GAAU;IACzB,GAAG,EAAE,MAAM;IACX,IAAI,EAAE,KAAK;IACX,KAAK,CAAC,GAAG,CAAC,GAAG;QACX,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7C,IAAI,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,6DAA6D,EAAE,CAAC;QACnG,CAAC;QACD,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,4DAA4D,EAAE,CAAC;QAClG,CAAC;QACD,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;QACjD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,iDAAiD,EAAE,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3G,CAAC;QACD,IAAI,MAAM,GAAG,gBAAgB,EAAE,CAAC;YAC9B,OAAO;gBACL,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,mBAAmB,MAAM,eAAe,gBAAgB,2BAA2B;gBAC3F,QAAQ,EAAE,CAAC,MAAM,CAAC;aACnB,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG;YACb,UAAU,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,sBAAsB;YAClF,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;SAC7C,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAClB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,yBAAyB,MAAM,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;IACpH,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Check } from "../../types.js";
2
+ export declare const httpsRedirect: Check;
3
+ //# sourceMappingURL=https-redirect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"https-redirect.d.ts","sourceRoot":"","sources":["../../../src/checks/tls/https-redirect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAG5C,eAAO,MAAM,aAAa,EAAE,KA6C3B,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { isNetworkUnreachable } from "../util.js";
2
+ export const httpsRedirect = {
3
+ key: "https-redirect",
4
+ kind: "url",
5
+ async run(ctx) {
6
+ const url = new URL(ctx.target.href);
7
+ url.protocol = "http:";
8
+ url.port = "";
9
+ let res;
10
+ try {
11
+ res = await ctx.request(url.href);
12
+ }
13
+ catch (err) {
14
+ if (isNetworkUnreachable(err)) {
15
+ return {
16
+ status: "pass",
17
+ detail: `Plain HTTP is not served (${err.code} on ${url.host}:80).`,
18
+ };
19
+ }
20
+ throw err;
21
+ }
22
+ const chain = [...res.redirects, res.url];
23
+ if (res.redirects.length === 0) {
24
+ return {
25
+ status: "fail",
26
+ detail: `${url.href} answered HTTP ${res.status} without redirecting to HTTPS.`,
27
+ evidence: chain,
28
+ };
29
+ }
30
+ if (new URL(res.url).protocol !== "https:") {
31
+ return {
32
+ status: "fail",
33
+ detail: "HTTP redirects never reach an HTTPS URL.",
34
+ evidence: chain,
35
+ };
36
+ }
37
+ return {
38
+ status: "pass",
39
+ detail: res.redirects.length === 1
40
+ ? "HTTP redirects straight to HTTPS."
41
+ : `HTTP reaches HTTPS after ${res.redirects.length} redirects.`,
42
+ evidence: chain,
43
+ };
44
+ },
45
+ };
46
+ //# sourceMappingURL=https-redirect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"https-redirect.js","sourceRoot":"","sources":["../../../src/checks/tls/https-redirect.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,CAAC,MAAM,aAAa,GAAU;IAClC,GAAG,EAAE,gBAAgB;IACrB,IAAI,EAAE,KAAK;IACX,KAAK,CAAC,GAAG,CAAC,GAAG;QACX,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACrC,GAAG,CAAC,QAAQ,GAAG,OAAO,CAAC;QACvB,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC;QAEd,IAAI,GAAG,CAAC;QACR,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,oBAAoB,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC9B,OAAO;oBACL,MAAM,EAAE,MAAM;oBACd,MAAM,EAAE,6BAA8B,GAAyB,CAAC,IAAI,OAAO,GAAG,CAAC,IAAI,OAAO;iBAC3F,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;QAED,MAAM,KAAK,GAAG,CAAC,GAAG,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,GAAG,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO;gBACL,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,kBAAkB,GAAG,CAAC,MAAM,gCAAgC;gBAC/E,QAAQ,EAAE,KAAK;aAChB,CAAC;QACJ,CAAC;QACD,IAAI,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC3C,OAAO;gBACL,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,0CAA0C;gBAClD,QAAQ,EAAE,KAAK;aAChB,CAAC;QACJ,CAAC;QACD,OAAO;YACL,MAAM,EAAE,MAAM;YACd,MAAM,EACJ,GAAG,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;gBACxB,CAAC,CAAC,mCAAmC;gBACrC,CAAC,CAAC,4BAA4B,GAAG,CAAC,SAAS,CAAC,MAAM,aAAa;YACnE,QAAQ,EAAE,KAAK;SAChB,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Check } from "../../types.js";
2
+ export declare const legacyProtocols: Check;
3
+ //# sourceMappingURL=legacy-protocols.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy-protocols.d.ts","sourceRoot":"","sources":["../../../src/checks/tls/legacy-protocols.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAoB,MAAM,gBAAgB,CAAC;AAI9D,eAAO,MAAM,eAAe,EAAE,KAuB7B,CAAC"}
@@ -0,0 +1,27 @@
1
+ const LEGACY = ["TLSv1", "TLSv1.1"];
2
+ export const legacyProtocols = {
3
+ key: "legacy-tls",
4
+ kind: "url",
5
+ async run(ctx) {
6
+ const info = await ctx.tlsInfo();
7
+ const accepted = [];
8
+ for (const version of LEGACY) {
9
+ if (await ctx.probeTls(version))
10
+ accepted.push(version);
11
+ }
12
+ const negotiated = `Default negotiation: ${info.protocol ?? "?"} / ${info.cipher ?? "?"}`;
13
+ if (accepted.length > 0) {
14
+ return {
15
+ status: "fail",
16
+ detail: `Server accepts deprecated protocol(s): ${accepted.join(", ")}.`,
17
+ evidence: [negotiated],
18
+ };
19
+ }
20
+ return {
21
+ status: "pass",
22
+ detail: "TLS 1.0 and 1.1 were refused. (Cipher-suite enumeration is not performed in v1.)",
23
+ evidence: [negotiated],
24
+ };
25
+ },
26
+ };
27
+ //# sourceMappingURL=legacy-protocols.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy-protocols.js","sourceRoot":"","sources":["../../../src/checks/tls/legacy-protocols.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,GAAuB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AAExD,MAAM,CAAC,MAAM,eAAe,GAAU;IACpC,GAAG,EAAE,YAAY;IACjB,IAAI,EAAE,KAAK;IACX,KAAK,CAAC,GAAG,CAAC,GAAG;QACX,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAuB,EAAE,CAAC;QACxC,KAAK,MAAM,OAAO,IAAI,MAAM,EAAE,CAAC;YAC7B,IAAI,MAAM,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,UAAU,GAAG,wBAAwB,IAAI,CAAC,QAAQ,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;QAC1F,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO;gBACL,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,0CAA0C,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBACxE,QAAQ,EAAE,CAAC,UAAU,CAAC;aACvB,CAAC;QACJ,CAAC;QACD,OAAO;YACL,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,kFAAkF;YAC1F,QAAQ,EAAE,CAAC,UAAU,CAAC;SACvB,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { HttpResponse, ScanContext } from "../types.js";
2
+ /** The landing page as a browser would see it (redirects followed). */
3
+ export declare function landingPage(ctx: ScanContext): Promise<HttpResponse>;
4
+ /** HTTPS version of the target URL (same host and path). */
5
+ export declare function httpsUrl(ctx: ScanContext): string;
6
+ /** Origin the site actually serves from, after following redirects from the target. */
7
+ export declare function effectiveOrigin(ctx: ScanContext): Promise<string>;
8
+ /** Splits a header value into a lowercase directive map: "max-age=1; preload" -> {max-age: "1", preload: ""}. */
9
+ export declare function parseDirectives(value: string, separator?: string): Map<string, string>;
10
+ export interface ParsedCookie {
11
+ name: string;
12
+ secure: boolean;
13
+ httpOnly: boolean;
14
+ domain?: string;
15
+ path?: string;
16
+ sameSite?: string;
17
+ }
18
+ export declare function parseSetCookie(header: string): ParsedCookie;
19
+ /** Heuristic: cookie names that conventionally carry a session or auth credential. */
20
+ export declare function looksLikeSessionCookie(name: string): boolean;
21
+ export declare function isNetworkUnreachable(err: unknown): boolean;
22
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/checks/util.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE7D,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAEnE;AAED,4DAA4D;AAC5D,wBAAgB,QAAQ,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAMjD;AAED,uFAAuF;AACvF,wBAAsB,eAAe,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAEvE;AAED,iHAAiH;AACjH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,SAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAWnF;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAe3D;AAID,sFAAsF;AACtF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAG1D"}
@@ -0,0 +1,63 @@
1
+ /** The landing page as a browser would see it (redirects followed). */
2
+ export function landingPage(ctx) {
3
+ return ctx.request(ctx.target.href);
4
+ }
5
+ /** HTTPS version of the target URL (same host and path). */
6
+ export function httpsUrl(ctx) {
7
+ if (ctx.target.protocol === "https:")
8
+ return ctx.target.href;
9
+ const url = new URL(ctx.target.href);
10
+ url.protocol = "https:";
11
+ url.port = "";
12
+ return url.href;
13
+ }
14
+ /** Origin the site actually serves from, after following redirects from the target. */
15
+ export async function effectiveOrigin(ctx) {
16
+ return new URL((await landingPage(ctx)).url).origin;
17
+ }
18
+ /** Splits a header value into a lowercase directive map: "max-age=1; preload" -> {max-age: "1", preload: ""}. */
19
+ export function parseDirectives(value, separator = ";") {
20
+ const out = new Map();
21
+ for (const part of value.split(separator)) {
22
+ const trimmed = part.trim();
23
+ if (!trimmed)
24
+ continue;
25
+ const eq = trimmed.indexOf("=");
26
+ const name = (eq === -1 ? trimmed : trimmed.slice(0, eq)).trim().toLowerCase();
27
+ const val = eq === -1 ? "" : trimmed.slice(eq + 1).trim().replace(/^"|"$/g, "");
28
+ if (!out.has(name))
29
+ out.set(name, val);
30
+ }
31
+ return out;
32
+ }
33
+ export function parseSetCookie(header) {
34
+ const [pair = "", ...attrs] = header.split(";");
35
+ const name = pair.split("=")[0]?.trim() ?? "";
36
+ const cookie = { name, secure: false, httpOnly: false };
37
+ for (const attr of attrs) {
38
+ const [rawKey = "", ...rest] = attr.split("=");
39
+ const key = rawKey.trim().toLowerCase();
40
+ const value = rest.join("=").trim();
41
+ if (key === "secure")
42
+ cookie.secure = true;
43
+ else if (key === "httponly")
44
+ cookie.httpOnly = true;
45
+ else if (key === "domain")
46
+ cookie.domain = value;
47
+ else if (key === "path")
48
+ cookie.path = value;
49
+ else if (key === "samesite")
50
+ cookie.sameSite = value;
51
+ }
52
+ return cookie;
53
+ }
54
+ const SESSION_COOKIE = /sess|sid$|^sid|auth|token|login|jwt|remember/i;
55
+ /** Heuristic: cookie names that conventionally carry a session or auth credential. */
56
+ export function looksLikeSessionCookie(name) {
57
+ return SESSION_COOKIE.test(name) && !/csrf|xsrf/i.test(name);
58
+ }
59
+ export function isNetworkUnreachable(err) {
60
+ const code = err.code;
61
+ return code === "ECONNREFUSED" || code === "ETIMEDOUT" || code === "EHOSTUNREACH" || code === "ECONNRESET";
62
+ }
63
+ //# sourceMappingURL=util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/checks/util.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /** #19 Weak / unproven algorithms. */
2
+ export declare const weakCrypto: import("../types.js").CodeCheck;
3
+ /** #20 Incorrect use of primitives. */
4
+ export declare const cryptoMisuse: import("../types.js").CodeCheck;
5
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../../src/code/checks/crypto.ts"],"names":[],"mappings":"AAqBA,sCAAsC;AACtC,eAAO,MAAM,UAAU,iCAmCtB,CAAC;AAEF,uCAAuC;AACvC,eAAO,MAAM,YAAY,iCAsCxB,CAAC"}