@ucptools/validator 1.0.0 → 1.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 (330) hide show
  1. package/dist/auth/config.d.ts +20 -0
  2. package/dist/auth/config.d.ts.map +1 -0
  3. package/dist/auth/config.js +114 -0
  4. package/dist/auth/config.js.map +1 -0
  5. package/dist/auth/index.d.ts +5 -0
  6. package/dist/auth/index.d.ts.map +1 -0
  7. package/dist/auth/index.js +17 -0
  8. package/dist/auth/index.js.map +1 -0
  9. package/dist/auth/middleware.d.ts +45 -0
  10. package/dist/auth/middleware.d.ts.map +1 -0
  11. package/dist/auth/middleware.js +170 -0
  12. package/dist/auth/middleware.js.map +1 -0
  13. package/dist/auth/service.d.ts +80 -0
  14. package/dist/auth/service.d.ts.map +1 -0
  15. package/dist/auth/service.js +298 -0
  16. package/dist/auth/service.js.map +1 -0
  17. package/dist/cli/index.d.ts +6 -0
  18. package/dist/cli/index.d.ts.map +1 -0
  19. package/dist/cli/index.js +375 -0
  20. package/dist/cli/index.js.map +1 -0
  21. package/dist/cli/mock-server.d.ts +20 -0
  22. package/dist/cli/mock-server.d.ts.map +1 -0
  23. package/dist/cli/mock-server.js +261 -0
  24. package/dist/cli/mock-server.js.map +1 -0
  25. package/dist/compliance/compliance-generator.d.ts +34 -0
  26. package/dist/compliance/compliance-generator.d.ts.map +1 -0
  27. package/dist/compliance/compliance-generator.js +320 -0
  28. package/dist/compliance/compliance-generator.js.map +1 -0
  29. package/dist/compliance/index.d.ts +8 -0
  30. package/dist/compliance/index.d.ts.map +1 -0
  31. package/dist/compliance/index.js +17 -0
  32. package/dist/compliance/index.js.map +1 -0
  33. package/dist/compliance/templates.d.ts +34 -0
  34. package/dist/compliance/templates.d.ts.map +1 -0
  35. package/{src/compliance/templates.ts → dist/compliance/templates.js} +117 -155
  36. package/dist/compliance/templates.js.map +1 -0
  37. package/dist/compliance/types.d.ts +64 -0
  38. package/dist/compliance/types.d.ts.map +1 -0
  39. package/dist/compliance/types.js +64 -0
  40. package/dist/compliance/types.js.map +1 -0
  41. package/dist/db/index.d.ts +17 -0
  42. package/dist/db/index.d.ts.map +1 -0
  43. package/dist/db/index.js +80 -0
  44. package/dist/db/index.js.map +1 -0
  45. package/dist/db/schema.d.ts +3886 -0
  46. package/dist/db/schema.d.ts.map +1 -0
  47. package/dist/db/schema.js +425 -0
  48. package/dist/db/schema.js.map +1 -0
  49. package/dist/db/utils.d.ts +252 -0
  50. package/dist/db/utils.d.ts.map +1 -0
  51. package/dist/db/utils.js +295 -0
  52. package/dist/db/utils.js.map +1 -0
  53. package/dist/feed-analyzer/feed-analyzer.d.ts +26 -0
  54. package/dist/feed-analyzer/feed-analyzer.d.ts.map +1 -0
  55. package/{src/feed-analyzer/feed-analyzer.ts → dist/feed-analyzer/feed-analyzer.js} +856 -726
  56. package/dist/feed-analyzer/feed-analyzer.js.map +1 -0
  57. package/dist/feed-analyzer/index.d.ts +8 -0
  58. package/dist/feed-analyzer/index.d.ts.map +1 -0
  59. package/dist/feed-analyzer/index.js +19 -0
  60. package/dist/feed-analyzer/index.js.map +1 -0
  61. package/dist/feed-analyzer/types.d.ts +285 -0
  62. package/dist/feed-analyzer/types.d.ts.map +1 -0
  63. package/dist/feed-analyzer/types.js +175 -0
  64. package/dist/feed-analyzer/types.js.map +1 -0
  65. package/{src/generator/index.ts → dist/generator/index.d.ts} +1 -1
  66. package/dist/generator/index.d.ts.map +1 -0
  67. package/dist/generator/index.js +13 -0
  68. package/dist/generator/index.js.map +1 -0
  69. package/dist/generator/key-generator.d.ts +24 -0
  70. package/dist/generator/key-generator.d.ts.map +1 -0
  71. package/dist/generator/key-generator.js +144 -0
  72. package/dist/generator/key-generator.js.map +1 -0
  73. package/dist/generator/profile-builder.d.ts +15 -0
  74. package/dist/generator/profile-builder.d.ts.map +1 -0
  75. package/dist/generator/profile-builder.js +338 -0
  76. package/dist/generator/profile-builder.js.map +1 -0
  77. package/dist/hosting/artifacts-generator.d.ts +10 -0
  78. package/dist/hosting/artifacts-generator.d.ts.map +1 -0
  79. package/{src/hosting/artifacts-generator.ts → dist/hosting/artifacts-generator.js} +191 -241
  80. package/dist/hosting/artifacts-generator.js.map +1 -0
  81. package/{src/hosting/index.ts → dist/hosting/index.d.ts} +1 -1
  82. package/dist/hosting/index.d.ts.map +1 -0
  83. package/dist/hosting/index.js +10 -0
  84. package/dist/hosting/index.js.map +1 -0
  85. package/dist/index.d.ts +18 -0
  86. package/dist/index.d.ts.map +1 -0
  87. package/dist/index.js +78 -0
  88. package/dist/index.js.map +1 -0
  89. package/dist/lib/analytics.d.ts +337 -0
  90. package/dist/lib/analytics.d.ts.map +1 -0
  91. package/dist/lib/analytics.js +188 -0
  92. package/dist/lib/analytics.js.map +1 -0
  93. package/{src/security/index.ts → dist/security/index.d.ts} +8 -15
  94. package/dist/security/index.d.ts.map +1 -0
  95. package/dist/security/index.js +12 -0
  96. package/dist/security/index.js.map +1 -0
  97. package/dist/security/security-scanner.d.ts +10 -0
  98. package/dist/security/security-scanner.d.ts.map +1 -0
  99. package/dist/security/security-scanner.js +669 -0
  100. package/dist/security/security-scanner.js.map +1 -0
  101. package/dist/security/types.d.ts +80 -0
  102. package/dist/security/types.d.ts.map +1 -0
  103. package/dist/security/types.js +21 -0
  104. package/dist/security/types.js.map +1 -0
  105. package/dist/services/analytics.d.ts +114 -0
  106. package/dist/services/analytics.d.ts.map +1 -0
  107. package/dist/services/analytics.js +862 -0
  108. package/dist/services/analytics.js.map +1 -0
  109. package/dist/services/badge.d.ts +31 -0
  110. package/dist/services/badge.d.ts.map +1 -0
  111. package/dist/services/badge.js +152 -0
  112. package/dist/services/badge.js.map +1 -0
  113. package/dist/services/cron.d.ts +125 -0
  114. package/dist/services/cron.d.ts.map +1 -0
  115. package/dist/services/cron.js +613 -0
  116. package/dist/services/cron.js.map +1 -0
  117. package/dist/services/directory.d.ts +106 -0
  118. package/dist/services/directory.d.ts.map +1 -0
  119. package/dist/services/directory.js +351 -0
  120. package/dist/services/directory.js.map +1 -0
  121. package/dist/services/email.d.ts +112 -0
  122. package/dist/services/email.d.ts.map +1 -0
  123. package/dist/services/email.js +772 -0
  124. package/dist/services/email.js.map +1 -0
  125. package/dist/services/hosted-profiles.d.ts +77 -0
  126. package/dist/services/hosted-profiles.d.ts.map +1 -0
  127. package/dist/services/hosted-profiles.js +433 -0
  128. package/dist/services/hosted-profiles.js.map +1 -0
  129. package/dist/services/latency.d.ts +67 -0
  130. package/dist/services/latency.d.ts.map +1 -0
  131. package/dist/services/latency.js +274 -0
  132. package/dist/services/latency.js.map +1 -0
  133. package/dist/services/manifest-compliance.d.ts +64 -0
  134. package/dist/services/manifest-compliance.d.ts.map +1 -0
  135. package/dist/services/manifest-compliance.js +271 -0
  136. package/dist/services/manifest-compliance.js.map +1 -0
  137. package/dist/services/monitoring-diff.d.ts +31 -0
  138. package/dist/services/monitoring-diff.d.ts.map +1 -0
  139. package/dist/services/monitoring-diff.js +189 -0
  140. package/dist/services/monitoring-diff.js.map +1 -0
  141. package/dist/services/notifications.d.ts +46 -0
  142. package/dist/services/notifications.d.ts.map +1 -0
  143. package/dist/services/notifications.js +88 -0
  144. package/dist/services/notifications.js.map +1 -0
  145. package/dist/services/stripe.d.ts +93 -0
  146. package/dist/services/stripe.d.ts.map +1 -0
  147. package/dist/services/stripe.js +490 -0
  148. package/dist/services/stripe.js.map +1 -0
  149. package/dist/services/validation-history.d.ts +99 -0
  150. package/dist/services/validation-history.d.ts.map +1 -0
  151. package/dist/services/validation-history.js +344 -0
  152. package/dist/services/validation-history.js.map +1 -0
  153. package/dist/services/validation-logging.d.ts +103 -0
  154. package/dist/services/validation-logging.d.ts.map +1 -0
  155. package/dist/services/validation-logging.js +210 -0
  156. package/dist/services/validation-logging.js.map +1 -0
  157. package/dist/services/validation.d.ts +119 -0
  158. package/dist/services/validation.d.ts.map +1 -0
  159. package/dist/services/validation.js +1185 -0
  160. package/dist/services/validation.js.map +1 -0
  161. package/dist/simulator/agent-simulator.d.ts +69 -0
  162. package/dist/simulator/agent-simulator.d.ts.map +1 -0
  163. package/dist/simulator/agent-simulator.js +870 -0
  164. package/dist/simulator/agent-simulator.js.map +1 -0
  165. package/{src/simulator/index.ts → dist/simulator/index.d.ts} +7 -7
  166. package/dist/simulator/index.d.ts.map +1 -0
  167. package/dist/simulator/index.js +23 -0
  168. package/dist/simulator/index.js.map +1 -0
  169. package/{src/simulator/types.ts → dist/simulator/types.d.ts} +171 -170
  170. package/dist/simulator/types.d.ts.map +1 -0
  171. package/dist/simulator/types.js +18 -0
  172. package/dist/simulator/types.js.map +1 -0
  173. package/dist/types/acp-validation.d.ts +87 -0
  174. package/dist/types/acp-validation.d.ts.map +1 -0
  175. package/dist/types/acp-validation.js +40 -0
  176. package/dist/types/acp-validation.js.map +1 -0
  177. package/dist/types/analytics.d.ts +182 -0
  178. package/dist/types/analytics.d.ts.map +1 -0
  179. package/dist/types/analytics.js +7 -0
  180. package/dist/types/analytics.js.map +1 -0
  181. package/dist/types/generator.d.ts +106 -0
  182. package/dist/types/generator.d.ts.map +1 -0
  183. package/dist/types/generator.js +6 -0
  184. package/dist/types/generator.js.map +1 -0
  185. package/{src/types/index.ts → dist/types/index.d.ts} +1 -1
  186. package/dist/types/index.d.ts.map +1 -0
  187. package/dist/types/index.js +23 -0
  188. package/dist/types/index.js.map +1 -0
  189. package/dist/types/ucp-profile.d.ts +111 -0
  190. package/dist/types/ucp-profile.d.ts.map +1 -0
  191. package/dist/types/ucp-profile.js +45 -0
  192. package/dist/types/ucp-profile.js.map +1 -0
  193. package/dist/types/validation.d.ts +76 -0
  194. package/dist/types/validation.d.ts.map +1 -0
  195. package/dist/types/validation.js +42 -0
  196. package/dist/types/validation.js.map +1 -0
  197. package/dist/validator/acp/index.d.ts +31 -0
  198. package/dist/validator/acp/index.d.ts.map +1 -0
  199. package/dist/validator/acp/index.js +574 -0
  200. package/dist/validator/acp/index.js.map +1 -0
  201. package/dist/validator/index.d.ts +26 -0
  202. package/dist/validator/index.d.ts.map +1 -0
  203. package/dist/validator/index.js +161 -0
  204. package/dist/validator/index.js.map +1 -0
  205. package/dist/validator/network-validator.d.ts +28 -0
  206. package/dist/validator/network-validator.d.ts.map +1 -0
  207. package/dist/validator/network-validator.js +319 -0
  208. package/dist/validator/network-validator.js.map +1 -0
  209. package/dist/validator/rules-validator.d.ts +19 -0
  210. package/dist/validator/rules-validator.d.ts.map +1 -0
  211. package/dist/validator/rules-validator.js +306 -0
  212. package/dist/validator/rules-validator.js.map +1 -0
  213. package/dist/validator/sdk-validator.d.ts +58 -0
  214. package/dist/validator/sdk-validator.d.ts.map +1 -0
  215. package/{src/validator/sdk-validator.ts → dist/validator/sdk-validator.js} +273 -330
  216. package/dist/validator/sdk-validator.js.map +1 -0
  217. package/dist/validator/structural-validator.d.ts +11 -0
  218. package/dist/validator/structural-validator.d.ts.map +1 -0
  219. package/dist/validator/structural-validator.js +549 -0
  220. package/dist/validator/structural-validator.js.map +1 -0
  221. package/dist/validator/utils.d.ts +51 -0
  222. package/dist/validator/utils.d.ts.map +1 -0
  223. package/dist/validator/utils.js +132 -0
  224. package/dist/validator/utils.js.map +1 -0
  225. package/package.json +44 -12
  226. package/CLAUDE.md +0 -109
  227. package/api/analyze-feed.js +0 -140
  228. package/api/badge.js +0 -185
  229. package/api/benchmark.js +0 -177
  230. package/api/directory-stats.ts +0 -29
  231. package/api/directory.ts +0 -73
  232. package/api/generate-compliance.js +0 -143
  233. package/api/generate-schema.js +0 -457
  234. package/api/generate.js +0 -132
  235. package/api/security-scan.js +0 -133
  236. package/api/simulate.js +0 -187
  237. package/api/tsconfig.json +0 -10
  238. package/api/validate.js +0 -1351
  239. package/apify-actor/.actor/actor.json +0 -68
  240. package/apify-actor/.actor/input_schema.json +0 -32
  241. package/apify-actor/APIFY-STORE-LISTING.md +0 -412
  242. package/apify-actor/Dockerfile +0 -8
  243. package/apify-actor/README.md +0 -166
  244. package/apify-actor/main.ts +0 -111
  245. package/apify-actor/package.json +0 -17
  246. package/apify-actor/src/main.js +0 -199
  247. package/docs/BRAND-IDENTITY.md +0 -238
  248. package/docs/BRAND-STYLE-GUIDE.md +0 -356
  249. package/drizzle/0000_black_king_cobra.sql +0 -39
  250. package/drizzle/meta/0000_snapshot.json +0 -309
  251. package/drizzle/meta/_journal.json +0 -13
  252. package/drizzle.config.ts +0 -10
  253. package/public/.well-known/ucp +0 -25
  254. package/public/android-chrome-192x192.png +0 -0
  255. package/public/android-chrome-512x512.png +0 -0
  256. package/public/apple-touch-icon.png +0 -0
  257. package/public/brand.css +0 -321
  258. package/public/directory.html +0 -701
  259. package/public/favicon-16x16.png +0 -0
  260. package/public/favicon-32x32.png +0 -0
  261. package/public/favicon.ico +0 -0
  262. package/public/guides/bigcommerce.html +0 -743
  263. package/public/guides/fastucp.html +0 -838
  264. package/public/guides/magento.html +0 -779
  265. package/public/guides/shopify.html +0 -726
  266. package/public/guides/squarespace.html +0 -749
  267. package/public/guides/wix.html +0 -747
  268. package/public/guides/woocommerce.html +0 -733
  269. package/public/index.html +0 -3835
  270. package/public/learn.html +0 -396
  271. package/public/logo.jpeg +0 -0
  272. package/public/og-image-icon.png +0 -0
  273. package/public/og-image.png +0 -0
  274. package/public/robots.txt +0 -6
  275. package/public/site.webmanifest +0 -31
  276. package/public/sitemap.xml +0 -69
  277. package/public/social/linkedin-banner-1128x191.png +0 -0
  278. package/public/social/temp.PNG +0 -0
  279. package/public/social/x-header-1500x500.png +0 -0
  280. package/public/verify.html +0 -410
  281. package/scripts/generate-favicons.js +0 -44
  282. package/scripts/generate-ico.js +0 -23
  283. package/scripts/generate-og-image.js +0 -45
  284. package/scripts/reset-db.ts +0 -77
  285. package/scripts/seed-db.ts +0 -71
  286. package/scripts/setup-benchmark-db.js +0 -70
  287. package/src/api/server.ts +0 -266
  288. package/src/cli/index.ts +0 -302
  289. package/src/compliance/compliance-generator.ts +0 -452
  290. package/src/compliance/index.ts +0 -28
  291. package/src/compliance/types.ts +0 -170
  292. package/src/db/index.ts +0 -28
  293. package/src/db/schema.ts +0 -84
  294. package/src/feed-analyzer/index.ts +0 -34
  295. package/src/feed-analyzer/types.ts +0 -354
  296. package/src/generator/key-generator.ts +0 -124
  297. package/src/generator/profile-builder.ts +0 -402
  298. package/src/index.ts +0 -105
  299. package/src/security/security-scanner.ts +0 -604
  300. package/src/security/types.ts +0 -55
  301. package/src/services/directory.ts +0 -434
  302. package/src/simulator/agent-simulator.ts +0 -941
  303. package/src/types/generator.ts +0 -140
  304. package/src/types/ucp-profile.ts +0 -140
  305. package/src/types/validation.ts +0 -89
  306. package/src/validator/index.ts +0 -194
  307. package/src/validator/network-validator.ts +0 -417
  308. package/src/validator/rules-validator.ts +0 -297
  309. package/src/validator/structural-validator.ts +0 -476
  310. package/tests/fixtures/non-compliant-profile.json +0 -25
  311. package/tests/fixtures/official-sample-profile.json +0 -75
  312. package/tests/integration/benchmark.test.ts +0 -207
  313. package/tests/integration/database.test.ts +0 -163
  314. package/tests/integration/directory-api.test.ts +0 -268
  315. package/tests/integration/simulate-api.test.ts +0 -230
  316. package/tests/integration/validate-api.test.ts +0 -269
  317. package/tests/setup.ts +0 -15
  318. package/tests/unit/agent-simulator.test.ts +0 -575
  319. package/tests/unit/compliance-generator.test.ts +0 -374
  320. package/tests/unit/directory-service.test.ts +0 -272
  321. package/tests/unit/feed-analyzer.test.ts +0 -517
  322. package/tests/unit/lint-suggestions.test.ts +0 -423
  323. package/tests/unit/official-samples.test.ts +0 -211
  324. package/tests/unit/pdf-report.test.ts +0 -390
  325. package/tests/unit/sdk-validator.test.ts +0 -531
  326. package/tests/unit/security-scanner.test.ts +0 -410
  327. package/tests/unit/validation.test.ts +0 -390
  328. package/tsconfig.json +0 -20
  329. package/vercel.json +0 -34
  330. package/vitest.config.ts +0 -22
@@ -1,410 +0,0 @@
1
- /**
2
- * Tests for Security Posture Scanner
3
- */
4
-
5
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
6
- import { scanEndpointSecurity } from '../../src/security/security-scanner.js';
7
- import { SecurityCheckIds } from '../../src/security/types.js';
8
-
9
- // Mock fetch globally
10
- const mockFetch = vi.fn();
11
- global.fetch = mockFetch;
12
-
13
- describe('Security Scanner', () => {
14
- beforeEach(() => {
15
- mockFetch.mockReset();
16
- });
17
-
18
- afterEach(() => {
19
- vi.restoreAllMocks();
20
- });
21
-
22
- describe('scanEndpointSecurity', () => {
23
- it('should return a complete security scan result', async () => {
24
- // Mock HTTP check (redirect to HTTPS)
25
- mockFetch.mockResolvedValueOnce({
26
- ok: false,
27
- status: 301,
28
- headers: new Headers({ 'location': 'https://example.com/.well-known/ucp' }),
29
- });
30
-
31
- // Mock HTTPS endpoint check
32
- mockFetch.mockResolvedValueOnce({
33
- ok: true,
34
- status: 200,
35
- headers: new Headers({
36
- 'content-type': 'application/json',
37
- 'strict-transport-security': 'max-age=31536000',
38
- 'x-content-type-options': 'nosniff',
39
- 'access-control-allow-origin': '*',
40
- }),
41
- text: async () => JSON.stringify({ ucp: { version: '2026-01-11' } }),
42
- });
43
-
44
- const result = await scanEndpointSecurity('example.com');
45
-
46
- expect(result.domain).toBe('example.com');
47
- expect(result.endpoint).toBe('https://example.com/.well-known/ucp');
48
- expect(result.scanned_at).toBeTruthy();
49
- expect(result.score).toBeGreaterThanOrEqual(0);
50
- expect(result.score).toBeLessThanOrEqual(100);
51
- expect(result.grade).toMatch(/^[A-F]$/);
52
- expect(result.checks).toBeInstanceOf(Array);
53
- expect(result.checks.length).toBeGreaterThan(0);
54
- expect(result.summary).toHaveProperty('passed');
55
- expect(result.summary).toHaveProperty('failed');
56
- expect(result.summary).toHaveProperty('warnings');
57
- expect(result.summary).toHaveProperty('skipped');
58
- });
59
-
60
- it('should detect HTTPS enforcement via redirect', async () => {
61
- // HTTP redirects to HTTPS
62
- mockFetch.mockResolvedValueOnce({
63
- ok: false,
64
- status: 301,
65
- headers: new Headers({ 'location': 'https://example.com/.well-known/ucp' }),
66
- });
67
-
68
- // HTTPS endpoint
69
- mockFetch.mockResolvedValueOnce({
70
- ok: true,
71
- status: 200,
72
- headers: new Headers({ 'content-type': 'application/json' }),
73
- text: async () => '{}',
74
- });
75
-
76
- const result = await scanEndpointSecurity('example.com');
77
-
78
- const httpsCheck = result.checks.find(c => c.id === SecurityCheckIds.HTTPS_REQUIRED);
79
- expect(httpsCheck).toBeDefined();
80
- expect(httpsCheck?.status).toBe('pass');
81
- });
82
-
83
- it('should fail HTTPS check if HTTP serves content', async () => {
84
- // HTTP serves content without redirect
85
- mockFetch.mockResolvedValueOnce({
86
- ok: true,
87
- status: 200,
88
- headers: new Headers({ 'content-type': 'application/json' }),
89
- text: async () => '{}',
90
- });
91
-
92
- // HTTPS endpoint
93
- mockFetch.mockResolvedValueOnce({
94
- ok: true,
95
- status: 200,
96
- headers: new Headers({ 'content-type': 'application/json' }),
97
- text: async () => '{}',
98
- });
99
-
100
- const result = await scanEndpointSecurity('example.com');
101
-
102
- const httpsCheck = result.checks.find(c => c.id === SecurityCheckIds.HTTPS_REQUIRED);
103
- expect(httpsCheck).toBeDefined();
104
- expect(httpsCheck?.status).toBe('fail');
105
- });
106
-
107
- it('should detect private IP addresses', async () => {
108
- // HTTP check fails
109
- mockFetch.mockRejectedValueOnce(new Error('connection refused'));
110
-
111
- const result = await scanEndpointSecurity('192.168.1.1');
112
-
113
- const privateIpCheck = result.checks.find(c => c.id === SecurityCheckIds.PRIVATE_IP);
114
- expect(privateIpCheck).toBeDefined();
115
- expect(privateIpCheck?.status).toBe('fail');
116
- expect(privateIpCheck?.severity).toBe('high');
117
- });
118
-
119
- it('should pass private IP check for public domains', async () => {
120
- mockFetch.mockRejectedValueOnce(new Error('connection refused'));
121
-
122
- const result = await scanEndpointSecurity('example.com');
123
-
124
- const privateIpCheck = result.checks.find(c => c.id === SecurityCheckIds.PRIVATE_IP);
125
- expect(privateIpCheck).toBeDefined();
126
- expect(privateIpCheck?.status).toBe('pass');
127
- });
128
-
129
- it('should check security headers', async () => {
130
- // HTTP fails
131
- mockFetch.mockRejectedValueOnce(new Error('connection refused'));
132
-
133
- // HTTPS with good security headers
134
- mockFetch.mockResolvedValueOnce({
135
- ok: true,
136
- status: 200,
137
- headers: new Headers({
138
- 'content-type': 'application/json',
139
- 'strict-transport-security': 'max-age=31536000; includeSubDomains',
140
- 'x-content-type-options': 'nosniff',
141
- 'x-frame-options': 'DENY',
142
- }),
143
- text: async () => '{}',
144
- });
145
-
146
- const result = await scanEndpointSecurity('example.com');
147
-
148
- const headersCheck = result.checks.find(c => c.id === SecurityCheckIds.SECURITY_HEADERS);
149
- expect(headersCheck).toBeDefined();
150
- expect(headersCheck?.status).toBe('pass');
151
- expect(headersCheck?.details).toContain('HSTS');
152
- });
153
-
154
- it('should warn on missing security headers', async () => {
155
- mockFetch.mockRejectedValueOnce(new Error('connection refused'));
156
-
157
- // HTTPS with no security headers
158
- mockFetch.mockResolvedValueOnce({
159
- ok: true,
160
- status: 200,
161
- headers: new Headers({
162
- 'content-type': 'application/json',
163
- }),
164
- text: async () => '{}',
165
- });
166
-
167
- const result = await scanEndpointSecurity('example.com');
168
-
169
- const headersCheck = result.checks.find(c => c.id === SecurityCheckIds.SECURITY_HEADERS);
170
- expect(headersCheck).toBeDefined();
171
- expect(headersCheck?.status).toBe('fail');
172
- });
173
-
174
- it('should detect CORS configuration', async () => {
175
- mockFetch.mockRejectedValueOnce(new Error('connection refused'));
176
-
177
- mockFetch.mockResolvedValueOnce({
178
- ok: true,
179
- status: 200,
180
- headers: new Headers({
181
- 'content-type': 'application/json',
182
- 'access-control-allow-origin': 'https://trusted.com',
183
- 'access-control-allow-methods': 'GET, POST',
184
- }),
185
- text: async () => '{}',
186
- });
187
-
188
- const result = await scanEndpointSecurity('example.com');
189
-
190
- const corsCheck = result.checks.find(c => c.id === SecurityCheckIds.CORS_CONFIG);
191
- expect(corsCheck).toBeDefined();
192
- expect(corsCheck?.status).toBe('pass');
193
- });
194
-
195
- it('should warn on wildcard CORS', async () => {
196
- mockFetch.mockRejectedValueOnce(new Error('connection refused'));
197
-
198
- mockFetch.mockResolvedValueOnce({
199
- ok: true,
200
- status: 200,
201
- headers: new Headers({
202
- 'content-type': 'application/json',
203
- 'access-control-allow-origin': '*',
204
- }),
205
- text: async () => '{}',
206
- });
207
-
208
- const result = await scanEndpointSecurity('example.com');
209
-
210
- const corsCheck = result.checks.find(c => c.id === SecurityCheckIds.CORS_CONFIG);
211
- expect(corsCheck).toBeDefined();
212
- expect(corsCheck?.status).toBe('warn');
213
- expect(corsCheck?.details).toContain('all origins');
214
- });
215
-
216
- it('should detect rate limiting headers', async () => {
217
- mockFetch.mockRejectedValueOnce(new Error('connection refused'));
218
-
219
- mockFetch.mockResolvedValueOnce({
220
- ok: true,
221
- status: 200,
222
- headers: new Headers({
223
- 'content-type': 'application/json',
224
- 'x-ratelimit-limit': '100',
225
- 'x-ratelimit-remaining': '99',
226
- }),
227
- text: async () => '{}',
228
- });
229
-
230
- const result = await scanEndpointSecurity('example.com');
231
-
232
- const rateLimitCheck = result.checks.find(c => c.id === SecurityCheckIds.RATE_LIMITING);
233
- expect(rateLimitCheck).toBeDefined();
234
- expect(rateLimitCheck?.status).toBe('pass');
235
- });
236
-
237
- it('should warn when no rate limiting detected', async () => {
238
- mockFetch.mockRejectedValueOnce(new Error('connection refused'));
239
-
240
- mockFetch.mockResolvedValueOnce({
241
- ok: true,
242
- status: 200,
243
- headers: new Headers({
244
- 'content-type': 'application/json',
245
- }),
246
- text: async () => '{}',
247
- });
248
-
249
- const result = await scanEndpointSecurity('example.com');
250
-
251
- const rateLimitCheck = result.checks.find(c => c.id === SecurityCheckIds.RATE_LIMITING);
252
- expect(rateLimitCheck).toBeDefined();
253
- expect(rateLimitCheck?.status).toBe('warn');
254
- });
255
-
256
- it('should check content-type header', async () => {
257
- mockFetch.mockRejectedValueOnce(new Error('connection refused'));
258
-
259
- mockFetch.mockResolvedValueOnce({
260
- ok: true,
261
- status: 200,
262
- headers: new Headers({
263
- 'content-type': 'application/json; charset=utf-8',
264
- }),
265
- text: async () => '{}',
266
- });
267
-
268
- const result = await scanEndpointSecurity('example.com');
269
-
270
- const contentTypeCheck = result.checks.find(c => c.id === SecurityCheckIds.CONTENT_TYPE);
271
- expect(contentTypeCheck).toBeDefined();
272
- expect(contentTypeCheck?.status).toBe('pass');
273
- });
274
-
275
- it('should detect potential error disclosure', async () => {
276
- mockFetch.mockRejectedValueOnce(new Error('connection refused'));
277
-
278
- mockFetch.mockResolvedValueOnce({
279
- ok: true,
280
- status: 200,
281
- headers: new Headers({
282
- 'content-type': 'application/json',
283
- }),
284
- text: async () => JSON.stringify({
285
- error: 'Error at /home/user/app/server.js:42:15',
286
- stack: 'at Object.<anonymous> (/home/user/app/server.js:42:15)',
287
- }),
288
- });
289
-
290
- const result = await scanEndpointSecurity('example.com');
291
-
292
- const errorCheck = result.checks.find(c => c.id === SecurityCheckIds.ERROR_DISCLOSURE);
293
- expect(errorCheck).toBeDefined();
294
- expect(errorCheck?.status).toBe('warn');
295
- });
296
-
297
- it('should handle unreachable endpoints gracefully', async () => {
298
- // Both HTTP and HTTPS fail
299
- mockFetch.mockRejectedValue(new Error('Network error'));
300
-
301
- const result = await scanEndpointSecurity('unreachable.example.com');
302
-
303
- expect(result.domain).toBe('unreachable.example.com');
304
- expect(result.checks.length).toBeGreaterThan(0);
305
- // Should have skipped checks for unreachable endpoint
306
- expect(result.summary.skipped).toBeGreaterThan(0);
307
- });
308
-
309
- it('should calculate appropriate grades', async () => {
310
- mockFetch.mockRejectedValueOnce(new Error('connection refused'));
311
-
312
- // Good security setup
313
- mockFetch.mockResolvedValueOnce({
314
- ok: true,
315
- status: 200,
316
- headers: new Headers({
317
- 'content-type': 'application/json',
318
- 'strict-transport-security': 'max-age=31536000',
319
- 'x-content-type-options': 'nosniff',
320
- 'access-control-allow-origin': 'https://trusted.com',
321
- 'x-ratelimit-limit': '100',
322
- }),
323
- text: async () => '{}',
324
- });
325
-
326
- const result = await scanEndpointSecurity('secure.example.com');
327
-
328
- expect(result.score).toBeGreaterThanOrEqual(60);
329
- expect(['A', 'B', 'C']).toContain(result.grade);
330
- });
331
-
332
- it('should normalize domain input', async () => {
333
- mockFetch.mockRejectedValueOnce(new Error('connection refused'));
334
- mockFetch.mockResolvedValueOnce({
335
- ok: true,
336
- status: 200,
337
- headers: new Headers({ 'content-type': 'application/json' }),
338
- text: async () => '{}',
339
- });
340
-
341
- const result = await scanEndpointSecurity('https://example.com/path/to/something');
342
-
343
- expect(result.domain).toBe('example.com');
344
- expect(result.endpoint).toBe('https://example.com/.well-known/ucp');
345
- });
346
-
347
- it('should detect localhost as private IP', async () => {
348
- mockFetch.mockRejectedValueOnce(new Error('connection refused'));
349
-
350
- const result = await scanEndpointSecurity('localhost');
351
-
352
- const privateIpCheck = result.checks.find(c => c.id === SecurityCheckIds.PRIVATE_IP);
353
- expect(privateIpCheck?.status).toBe('fail');
354
- });
355
-
356
- it('should detect various private IP ranges', async () => {
357
- const privateIps = ['10.0.0.1', '172.16.0.1', '192.168.0.1', '127.0.0.1'];
358
-
359
- for (const ip of privateIps) {
360
- mockFetch.mockReset();
361
- mockFetch.mockRejectedValue(new Error('connection refused'));
362
-
363
- const result = await scanEndpointSecurity(ip);
364
-
365
- const privateIpCheck = result.checks.find(c => c.id === SecurityCheckIds.PRIVATE_IP);
366
- expect(privateIpCheck?.status).toBe('fail');
367
- }
368
- });
369
- });
370
-
371
- describe('Score Calculation', () => {
372
- it('should give higher scores for passing critical checks', async () => {
373
- mockFetch.mockRejectedValueOnce(new Error('connection refused'));
374
-
375
- // All good headers
376
- mockFetch.mockResolvedValueOnce({
377
- ok: true,
378
- status: 200,
379
- headers: new Headers({
380
- 'content-type': 'application/json',
381
- 'strict-transport-security': 'max-age=31536000',
382
- 'x-content-type-options': 'nosniff',
383
- 'x-frame-options': 'DENY',
384
- 'access-control-allow-origin': 'https://trusted.com',
385
- 'access-control-allow-methods': 'GET, POST',
386
- 'x-ratelimit-limit': '100',
387
- 'cache-control': 'no-store',
388
- }),
389
- text: async () => '{}',
390
- });
391
-
392
- const goodResult = await scanEndpointSecurity('secure.example.com');
393
-
394
- mockFetch.mockReset();
395
- mockFetch.mockRejectedValueOnce(new Error('connection refused'));
396
-
397
- // Minimal headers
398
- mockFetch.mockResolvedValueOnce({
399
- ok: true,
400
- status: 200,
401
- headers: new Headers({}),
402
- text: async () => '{}',
403
- });
404
-
405
- const badResult = await scanEndpointSecurity('insecure.example.com');
406
-
407
- expect(goodResult.score).toBeGreaterThan(badResult.score);
408
- });
409
- });
410
- });