@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,604 +0,0 @@
1
- /**
2
- * Security Posture Scanner for UCP Endpoints
3
- * Scans UCP endpoints for common security misconfigurations
4
- */
5
-
6
- import type { SecurityCheck, SecurityScanResult, SecurityScanOptions } from './types.js';
7
- import { SecurityCheckIds } from './types.js';
8
-
9
- const DEFAULT_TIMEOUT_MS = 15000;
10
-
11
- /**
12
- * Run a full security scan on a UCP endpoint
13
- */
14
- export async function scanEndpointSecurity(
15
- domain: string,
16
- options: SecurityScanOptions = {}
17
- ): Promise<SecurityScanResult> {
18
- const timeoutMs = options.timeoutMs || DEFAULT_TIMEOUT_MS;
19
- const checks: SecurityCheck[] = [];
20
-
21
- // Normalize domain
22
- const normalizedDomain = domain.replace(/^https?:\/\//, '').replace(/\/.*$/, '');
23
- const endpoint = `https://${normalizedDomain}/.well-known/ucp`;
24
-
25
- // Run all security checks
26
- checks.push(await checkHttpsRequired(normalizedDomain));
27
- checks.push(await checkPrivateIp(normalizedDomain));
28
-
29
- // Fetch the endpoint to analyze response
30
- const response = await fetchEndpointSafely(endpoint, timeoutMs);
31
-
32
- if (response.success && response.response && response.headers && response.body !== undefined) {
33
- checks.push(await checkTlsVersion(endpoint, response.response));
34
- checks.push(await checkCorsConfiguration(response.response, response.headers));
35
- checks.push(await checkSecurityHeaders(response.headers));
36
- checks.push(await checkContentType(response.headers));
37
- checks.push(await checkRateLimiting(response.headers));
38
- checks.push(await checkCacheHeaders(response.headers));
39
- checks.push(await checkErrorDisclosure(response.body));
40
- checks.push(checkResponseTime(response.responseTimeMs));
41
- } else {
42
- // Endpoint not reachable - add skip results
43
- checks.push(createSkippedCheck(SecurityCheckIds.TLS_VERSION, 'TLS Version', 'Endpoint not reachable'));
44
- checks.push(createSkippedCheck(SecurityCheckIds.CORS_CONFIG, 'CORS Configuration', 'Endpoint not reachable'));
45
- checks.push(createSkippedCheck(SecurityCheckIds.SECURITY_HEADERS, 'Security Headers', 'Endpoint not reachable'));
46
- checks.push(createSkippedCheck(SecurityCheckIds.CONTENT_TYPE, 'Content-Type', 'Endpoint not reachable'));
47
- checks.push(createSkippedCheck(SecurityCheckIds.RATE_LIMITING, 'Rate Limiting', 'Endpoint not reachable'));
48
- checks.push(createSkippedCheck(SecurityCheckIds.CACHE_HEADERS, 'Cache Headers', 'Endpoint not reachable'));
49
- checks.push(createSkippedCheck(SecurityCheckIds.ERROR_DISCLOSURE, 'Error Disclosure', 'Endpoint not reachable'));
50
- checks.push(createSkippedCheck(SecurityCheckIds.RESPONSE_TIME, 'Response Time', 'Endpoint not reachable'));
51
- }
52
-
53
- // Calculate score and grade
54
- const summary = calculateSummary(checks);
55
- const score = calculateScore(checks);
56
- const grade = calculateGrade(score);
57
-
58
- return {
59
- domain: normalizedDomain,
60
- endpoint,
61
- scanned_at: new Date().toISOString(),
62
- score,
63
- grade,
64
- checks,
65
- summary,
66
- };
67
- }
68
-
69
- /**
70
- * Fetch endpoint safely with timeout
71
- */
72
- async function fetchEndpointSafely(
73
- url: string,
74
- timeoutMs: number
75
- ): Promise<{
76
- success: boolean;
77
- response?: Response;
78
- headers?: Headers;
79
- body?: string;
80
- responseTimeMs?: number;
81
- error?: string;
82
- }> {
83
- const controller = new AbortController();
84
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
85
- const startTime = Date.now();
86
-
87
- try {
88
- const response = await fetch(url, {
89
- signal: controller.signal,
90
- headers: {
91
- 'Accept': 'application/json',
92
- 'User-Agent': 'UCP-Security-Scanner/1.0',
93
- 'Origin': 'https://ucptools.dev',
94
- },
95
- });
96
-
97
- clearTimeout(timeoutId);
98
- const responseTimeMs = Date.now() - startTime;
99
- const body = await response.text();
100
-
101
- return {
102
- success: true,
103
- response,
104
- headers: response.headers,
105
- body,
106
- responseTimeMs,
107
- };
108
- } catch (error) {
109
- clearTimeout(timeoutId);
110
- return {
111
- success: false,
112
- error: error instanceof Error ? error.message : 'Unknown error',
113
- };
114
- }
115
- }
116
-
117
- /**
118
- * Check 1: HTTPS Required
119
- */
120
- async function checkHttpsRequired(domain: string): Promise<SecurityCheck> {
121
- // Try HTTP to see if it redirects or is accessible
122
- try {
123
- const controller = new AbortController();
124
- const timeoutId = setTimeout(() => controller.abort(), 5000);
125
-
126
- const response = await fetch(`http://${domain}/.well-known/ucp`, {
127
- signal: controller.signal,
128
- redirect: 'manual',
129
- headers: { 'User-Agent': 'UCP-Security-Scanner/1.0' },
130
- });
131
-
132
- clearTimeout(timeoutId);
133
-
134
- // Check if HTTP redirects to HTTPS
135
- if (response.status >= 300 && response.status < 400) {
136
- const location = response.headers.get('location');
137
- if (location?.startsWith('https://')) {
138
- return {
139
- id: SecurityCheckIds.HTTPS_REQUIRED,
140
- name: 'HTTPS Enforcement',
141
- description: 'HTTP requests should redirect to HTTPS',
142
- status: 'pass',
143
- severity: 'critical',
144
- details: 'HTTP correctly redirects to HTTPS',
145
- };
146
- }
147
- }
148
-
149
- // HTTP is accessible without redirect
150
- if (response.ok) {
151
- return {
152
- id: SecurityCheckIds.HTTPS_REQUIRED,
153
- name: 'HTTPS Enforcement',
154
- description: 'HTTP requests should redirect to HTTPS',
155
- status: 'fail',
156
- severity: 'critical',
157
- details: 'HTTP endpoint is accessible without redirect to HTTPS',
158
- recommendation: 'Configure your server to redirect all HTTP traffic to HTTPS',
159
- };
160
- }
161
-
162
- // HTTP returns error (good - means HTTPS only)
163
- return {
164
- id: SecurityCheckIds.HTTPS_REQUIRED,
165
- name: 'HTTPS Enforcement',
166
- description: 'HTTP requests should redirect to HTTPS',
167
- status: 'pass',
168
- severity: 'critical',
169
- details: 'HTTP endpoint not accessible (HTTPS only)',
170
- };
171
- } catch {
172
- // HTTP connection failed - HTTPS only
173
- return {
174
- id: SecurityCheckIds.HTTPS_REQUIRED,
175
- name: 'HTTPS Enforcement',
176
- description: 'HTTP requests should redirect to HTTPS',
177
- status: 'pass',
178
- severity: 'critical',
179
- details: 'HTTP endpoint not accessible (HTTPS only)',
180
- };
181
- }
182
- }
183
-
184
- /**
185
- * Check 2: Private IP Detection
186
- */
187
- async function checkPrivateIp(domain: string): Promise<SecurityCheck> {
188
- // Check if domain looks like a private IP or localhost
189
- const privatePatterns = [
190
- /^localhost$/i,
191
- /^127\.\d+\.\d+\.\d+$/,
192
- /^10\.\d+\.\d+\.\d+$/,
193
- /^172\.(1[6-9]|2\d|3[01])\.\d+\.\d+$/,
194
- /^192\.168\.\d+\.\d+$/,
195
- /^::1$/,
196
- /^fc00:/i,
197
- /^fd00:/i,
198
- ];
199
-
200
- const isPrivate = privatePatterns.some(pattern => pattern.test(domain));
201
-
202
- if (isPrivate) {
203
- return {
204
- id: SecurityCheckIds.PRIVATE_IP,
205
- name: 'Private IP Detection',
206
- description: 'Endpoint should not use private/internal IP addresses',
207
- status: 'fail',
208
- severity: 'high',
209
- details: `Domain "${domain}" appears to be a private or internal address`,
210
- recommendation: 'Use a public domain name for production UCP endpoints',
211
- };
212
- }
213
-
214
- return {
215
- id: SecurityCheckIds.PRIVATE_IP,
216
- name: 'Private IP Detection',
217
- description: 'Endpoint should not use private/internal IP addresses',
218
- status: 'pass',
219
- severity: 'high',
220
- details: 'Domain is publicly routable',
221
- };
222
- }
223
-
224
- /**
225
- * Check 3: TLS Version
226
- */
227
- async function checkTlsVersion(_url: string, _response: Response): Promise<SecurityCheck> {
228
- // Note: Browser fetch doesn't expose TLS version directly
229
- // We can only verify HTTPS is working
230
- return {
231
- id: SecurityCheckIds.TLS_VERSION,
232
- name: 'TLS Configuration',
233
- description: 'Endpoint should use TLS 1.2 or higher',
234
- status: 'pass',
235
- severity: 'high',
236
- details: 'HTTPS connection successful (TLS version not directly verifiable from browser)',
237
- };
238
- }
239
-
240
- /**
241
- * Check 4: CORS Configuration
242
- */
243
- async function checkCorsConfiguration(_response: Response, headers: Headers): Promise<SecurityCheck> {
244
- const acao = headers.get('access-control-allow-origin');
245
- const acam = headers.get('access-control-allow-methods');
246
- const acah = headers.get('access-control-allow-headers');
247
-
248
- if (!acao) {
249
- return {
250
- id: SecurityCheckIds.CORS_CONFIG,
251
- name: 'CORS Configuration',
252
- description: 'CORS should be properly configured for AI agent access',
253
- status: 'warn',
254
- severity: 'medium',
255
- details: 'No Access-Control-Allow-Origin header found',
256
- recommendation: 'Configure CORS headers to allow AI agents to access your UCP endpoint',
257
- };
258
- }
259
-
260
- // Check if CORS is too permissive
261
- if (acao === '*') {
262
- return {
263
- id: SecurityCheckIds.CORS_CONFIG,
264
- name: 'CORS Configuration',
265
- description: 'CORS should be properly configured for AI agent access',
266
- status: 'warn',
267
- severity: 'low',
268
- details: 'CORS allows all origins (*). Consider restricting to known AI agent domains.',
269
- recommendation: 'For production, consider restricting CORS to specific trusted origins',
270
- };
271
- }
272
-
273
- const hasGoodConfig = acao && (acam || acah);
274
-
275
- return {
276
- id: SecurityCheckIds.CORS_CONFIG,
277
- name: 'CORS Configuration',
278
- description: 'CORS should be properly configured for AI agent access',
279
- status: hasGoodConfig ? 'pass' : 'warn',
280
- severity: 'medium',
281
- details: `CORS configured: Origin=${acao}${acam ? `, Methods=${acam}` : ''}`,
282
- };
283
- }
284
-
285
- /**
286
- * Check 5: Security Headers
287
- */
288
- async function checkSecurityHeaders(headers: Headers): Promise<SecurityCheck> {
289
- const securityHeaders = {
290
- 'x-content-type-options': headers.get('x-content-type-options'),
291
- 'x-frame-options': headers.get('x-frame-options'),
292
- 'x-xss-protection': headers.get('x-xss-protection'),
293
- 'strict-transport-security': headers.get('strict-transport-security'),
294
- 'content-security-policy': headers.get('content-security-policy'),
295
- };
296
-
297
- const presentHeaders: string[] = [];
298
- const missingHeaders: string[] = [];
299
-
300
- // Check important headers
301
- if (securityHeaders['strict-transport-security']) {
302
- presentHeaders.push('HSTS');
303
- } else {
304
- missingHeaders.push('HSTS');
305
- }
306
-
307
- if (securityHeaders['x-content-type-options']) {
308
- presentHeaders.push('X-Content-Type-Options');
309
- } else {
310
- missingHeaders.push('X-Content-Type-Options');
311
- }
312
-
313
- if (securityHeaders['x-frame-options']) {
314
- presentHeaders.push('X-Frame-Options');
315
- }
316
-
317
- // Calculate status based on critical headers
318
- const hasHsts = !!securityHeaders['strict-transport-security'];
319
- const hasXcto = !!securityHeaders['x-content-type-options'];
320
-
321
- let status: SecurityCheck['status'] = 'pass';
322
- let severity: SecurityCheck['severity'] = 'medium';
323
-
324
- if (!hasHsts && !hasXcto) {
325
- status = 'fail';
326
- severity = 'medium';
327
- } else if (!hasHsts || !hasXcto) {
328
- status = 'warn';
329
- severity = 'low';
330
- }
331
-
332
- return {
333
- id: SecurityCheckIds.SECURITY_HEADERS,
334
- name: 'Security Headers',
335
- description: 'Response should include security headers (HSTS, X-Content-Type-Options)',
336
- status,
337
- severity,
338
- details: presentHeaders.length > 0
339
- ? `Present: ${presentHeaders.join(', ')}${missingHeaders.length > 0 ? `. Missing: ${missingHeaders.join(', ')}` : ''}`
340
- : 'No security headers found',
341
- recommendation: missingHeaders.length > 0
342
- ? `Add missing security headers: ${missingHeaders.join(', ')}`
343
- : undefined,
344
- };
345
- }
346
-
347
- /**
348
- * Check 6: Content-Type
349
- */
350
- async function checkContentType(headers: Headers): Promise<SecurityCheck> {
351
- const contentType = headers.get('content-type');
352
-
353
- if (!contentType) {
354
- return {
355
- id: SecurityCheckIds.CONTENT_TYPE,
356
- name: 'Content-Type Header',
357
- description: 'Response should have correct Content-Type for JSON',
358
- status: 'warn',
359
- severity: 'low',
360
- details: 'No Content-Type header found',
361
- recommendation: 'Set Content-Type: application/json for UCP endpoints',
362
- };
363
- }
364
-
365
- const isJson = contentType.includes('application/json');
366
-
367
- return {
368
- id: SecurityCheckIds.CONTENT_TYPE,
369
- name: 'Content-Type Header',
370
- description: 'Response should have correct Content-Type for JSON',
371
- status: isJson ? 'pass' : 'warn',
372
- severity: 'low',
373
- details: `Content-Type: ${contentType}`,
374
- recommendation: isJson ? undefined : 'Set Content-Type: application/json for UCP endpoints',
375
- };
376
- }
377
-
378
- /**
379
- * Check 7: Rate Limiting
380
- */
381
- async function checkRateLimiting(headers: Headers): Promise<SecurityCheck> {
382
- // Look for common rate limiting headers
383
- const rateLimitHeaders = {
384
- 'x-ratelimit-limit': headers.get('x-ratelimit-limit'),
385
- 'x-ratelimit-remaining': headers.get('x-ratelimit-remaining'),
386
- 'x-rate-limit-limit': headers.get('x-rate-limit-limit'),
387
- 'ratelimit-limit': headers.get('ratelimit-limit'),
388
- 'retry-after': headers.get('retry-after'),
389
- };
390
-
391
- const hasRateLimiting = Object.values(rateLimitHeaders).some(v => v !== null);
392
-
393
- if (hasRateLimiting) {
394
- const limitValue = rateLimitHeaders['x-ratelimit-limit'] ||
395
- rateLimitHeaders['x-rate-limit-limit'] ||
396
- rateLimitHeaders['ratelimit-limit'];
397
- return {
398
- id: SecurityCheckIds.RATE_LIMITING,
399
- name: 'Rate Limiting',
400
- description: 'Endpoint should have rate limiting to prevent abuse',
401
- status: 'pass',
402
- severity: 'high',
403
- details: `Rate limiting detected${limitValue ? `: ${limitValue} requests` : ''}`,
404
- };
405
- }
406
-
407
- return {
408
- id: SecurityCheckIds.RATE_LIMITING,
409
- name: 'Rate Limiting',
410
- description: 'Endpoint should have rate limiting to prevent abuse',
411
- status: 'warn',
412
- severity: 'high',
413
- details: 'No rate limiting headers detected (may still be present server-side)',
414
- recommendation: 'Implement rate limiting to protect against abuse and DoS attacks',
415
- };
416
- }
417
-
418
- /**
419
- * Check 8: Cache Headers
420
- */
421
- async function checkCacheHeaders(headers: Headers): Promise<SecurityCheck> {
422
- const cacheControl = headers.get('cache-control');
423
- const etag = headers.get('etag');
424
- const lastModified = headers.get('last-modified');
425
-
426
- const hasCaching = cacheControl || etag || lastModified;
427
-
428
- if (!hasCaching) {
429
- return {
430
- id: SecurityCheckIds.CACHE_HEADERS,
431
- name: 'Cache Headers',
432
- description: 'Response should have appropriate caching headers',
433
- status: 'pass',
434
- severity: 'info',
435
- details: 'No caching headers found (optional)',
436
- recommendation: 'Consider adding Cache-Control headers for better performance',
437
- };
438
- }
439
-
440
- const details: string[] = [];
441
- if (cacheControl) details.push(`Cache-Control: ${cacheControl}`);
442
- if (etag) details.push('ETag present');
443
- if (lastModified) details.push('Last-Modified present');
444
-
445
- return {
446
- id: SecurityCheckIds.CACHE_HEADERS,
447
- name: 'Cache Headers',
448
- description: 'Response should have appropriate caching headers',
449
- status: 'pass',
450
- severity: 'info',
451
- details: details.join(', '),
452
- };
453
- }
454
-
455
- /**
456
- * Check 9: Error Disclosure
457
- */
458
- async function checkErrorDisclosure(body: string): Promise<SecurityCheck> {
459
- // Look for signs of stack traces or internal error details
460
- const sensitivePatterns = [
461
- /stack\s*trace/i,
462
- /at\s+\w+\s+\([^)]+:\d+:\d+\)/, // Stack trace lines
463
- /exception|error.*at\s+line/i,
464
- /mysql|postgresql|mongodb|redis/i, // Database names in errors
465
- /\/home\/|\/var\/|\/usr\/|C:\\|D:\\/i, // File paths
466
- /password|secret|api[_-]?key/i,
467
- ];
468
-
469
- const foundPatterns: string[] = [];
470
-
471
- for (const pattern of sensitivePatterns) {
472
- if (pattern.test(body)) {
473
- const match = body.match(pattern);
474
- if (match) {
475
- foundPatterns.push(match[0].substring(0, 30));
476
- }
477
- }
478
- }
479
-
480
- if (foundPatterns.length > 0) {
481
- return {
482
- id: SecurityCheckIds.ERROR_DISCLOSURE,
483
- name: 'Error Information Disclosure',
484
- description: 'Response should not expose internal error details or stack traces',
485
- status: 'warn',
486
- severity: 'medium',
487
- details: `Potentially sensitive information found: ${foundPatterns.join(', ')}`,
488
- recommendation: 'Ensure production responses do not expose stack traces or internal paths',
489
- };
490
- }
491
-
492
- return {
493
- id: SecurityCheckIds.ERROR_DISCLOSURE,
494
- name: 'Error Information Disclosure',
495
- description: 'Response should not expose internal error details or stack traces',
496
- status: 'pass',
497
- severity: 'medium',
498
- details: 'No sensitive error information detected',
499
- };
500
- }
501
-
502
- /**
503
- * Check 10: Response Time
504
- */
505
- function checkResponseTime(responseTimeMs?: number): SecurityCheck {
506
- if (!responseTimeMs) {
507
- return createSkippedCheck(SecurityCheckIds.RESPONSE_TIME, 'Response Time', 'Could not measure response time');
508
- }
509
-
510
- let status: SecurityCheck['status'] = 'pass';
511
- let severity: SecurityCheck['severity'] = 'low';
512
- let recommendation: string | undefined;
513
-
514
- if (responseTimeMs > 5000) {
515
- status = 'fail';
516
- severity = 'medium';
517
- recommendation = 'Response time is very slow. Consider optimizing your endpoint or using a CDN.';
518
- } else if (responseTimeMs > 2000) {
519
- status = 'warn';
520
- severity = 'low';
521
- recommendation = 'Response time is slow. Consider optimizing or using caching.';
522
- }
523
-
524
- return {
525
- id: SecurityCheckIds.RESPONSE_TIME,
526
- name: 'Response Time',
527
- description: 'Endpoint should respond quickly to prevent timeouts',
528
- status,
529
- severity,
530
- details: `Response time: ${responseTimeMs}ms`,
531
- recommendation,
532
- };
533
- }
534
-
535
- /**
536
- * Create a skipped check result
537
- */
538
- function createSkippedCheck(id: string, name: string, reason: string): SecurityCheck {
539
- return {
540
- id,
541
- name,
542
- description: reason,
543
- status: 'skip',
544
- severity: 'info',
545
- details: `Skipped: ${reason}`,
546
- };
547
- }
548
-
549
- /**
550
- * Calculate summary from checks
551
- */
552
- function calculateSummary(checks: SecurityCheck[]): SecurityScanResult['summary'] {
553
- return {
554
- passed: checks.filter(c => c.status === 'pass').length,
555
- failed: checks.filter(c => c.status === 'fail').length,
556
- warnings: checks.filter(c => c.status === 'warn').length,
557
- skipped: checks.filter(c => c.status === 'skip').length,
558
- };
559
- }
560
-
561
- /**
562
- * Calculate security score (0-100)
563
- */
564
- function calculateScore(checks: SecurityCheck[]): number {
565
- const weights: Record<SecurityCheck['severity'], number> = {
566
- critical: 25,
567
- high: 20,
568
- medium: 15,
569
- low: 10,
570
- info: 5,
571
- };
572
-
573
- let totalWeight = 0;
574
- let earnedPoints = 0;
575
-
576
- for (const check of checks) {
577
- if (check.status === 'skip') continue;
578
-
579
- const weight = weights[check.severity];
580
- totalWeight += weight;
581
-
582
- if (check.status === 'pass') {
583
- earnedPoints += weight;
584
- } else if (check.status === 'warn') {
585
- earnedPoints += weight * 0.5;
586
- }
587
- // 'fail' gets 0 points
588
- }
589
-
590
- if (totalWeight === 0) return 0;
591
-
592
- return Math.round((earnedPoints / totalWeight) * 100);
593
- }
594
-
595
- /**
596
- * Calculate grade from score
597
- */
598
- function calculateGrade(score: number): string {
599
- if (score >= 90) return 'A';
600
- if (score >= 80) return 'B';
601
- if (score >= 70) return 'C';
602
- if (score >= 60) return 'D';
603
- return 'F';
604
- }
@@ -1,55 +0,0 @@
1
- /**
2
- * Security Scanner Types
3
- * Types for UCP endpoint security posture scanning
4
- */
5
-
6
- export type SecuritySeverity = 'critical' | 'high' | 'medium' | 'low' | 'info';
7
-
8
- export type SecurityCheckStatus = 'pass' | 'fail' | 'warn' | 'skip';
9
-
10
- export interface SecurityCheck {
11
- id: string;
12
- name: string;
13
- description: string;
14
- status: SecurityCheckStatus;
15
- severity: SecuritySeverity;
16
- details?: string;
17
- recommendation?: string;
18
- }
19
-
20
- export interface SecurityScanResult {
21
- domain: string;
22
- endpoint: string;
23
- scanned_at: string;
24
- score: number; // 0-100 security score
25
- grade: string; // A, B, C, D, F
26
- checks: SecurityCheck[];
27
- summary: {
28
- passed: number;
29
- failed: number;
30
- warnings: number;
31
- skipped: number;
32
- };
33
- }
34
-
35
- export interface SecurityScanOptions {
36
- timeoutMs?: number;
37
- skipTlsCheck?: boolean;
38
- includeHeaders?: boolean;
39
- }
40
-
41
- // Security check IDs
42
- export const SecurityCheckIds = {
43
- HTTPS_REQUIRED: 'HTTPS_REQUIRED',
44
- TLS_VERSION: 'TLS_VERSION',
45
- CORS_CONFIG: 'CORS_CONFIG',
46
- RATE_LIMITING: 'RATE_LIMITING',
47
- SECURITY_HEADERS: 'SECURITY_HEADERS',
48
- CONTENT_TYPE: 'CONTENT_TYPE',
49
- ERROR_DISCLOSURE: 'ERROR_DISCLOSURE',
50
- PRIVATE_IP: 'PRIVATE_IP',
51
- RESPONSE_TIME: 'RESPONSE_TIME',
52
- CACHE_HEADERS: 'CACHE_HEADERS',
53
- } as const;
54
-
55
- export type SecurityCheckId = typeof SecurityCheckIds[keyof typeof SecurityCheckIds];