web-agent-bridge 3.4.0 → 3.9.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 (315) hide show
  1. package/LICENSE +84 -84
  2. package/README.ar.md +1565 -1304
  3. package/README.md +171 -298
  4. package/bin/agent-runner.js +474 -474
  5. package/bin/cli.js +237 -237
  6. package/bin/wab-init.js +244 -223
  7. package/bin/wab.js +80 -80
  8. package/examples/azure-dns-wab.js +83 -83
  9. package/examples/bidi-agent.js +119 -119
  10. package/examples/cloudflare-wab-dns.js +121 -121
  11. package/examples/cpanel-wab-dns.js +114 -114
  12. package/examples/cross-site-agent.js +91 -91
  13. package/examples/dns-discovery-agent.js +166 -166
  14. package/examples/gcp-dns-wab.js +76 -76
  15. package/examples/governance-agent.js +169 -169
  16. package/examples/mcp-agent.js +94 -94
  17. package/examples/next-app-router/README.md +44 -44
  18. package/examples/plesk-wab-dns.js +103 -103
  19. package/examples/puppeteer-agent.js +108 -108
  20. package/examples/route53-wab-dns.js +144 -144
  21. package/examples/saas-dashboard/README.md +55 -55
  22. package/examples/safe-mode-agent.js +96 -96
  23. package/examples/self-discovery.js +106 -0
  24. package/examples/shopify-hydrogen/README.md +74 -74
  25. package/examples/vision-agent.js +171 -171
  26. package/examples/wab-sign.js +74 -74
  27. package/examples/wab-verify.js +60 -60
  28. package/examples/wordpress-elementor/README.md +77 -77
  29. package/package.json +93 -93
  30. package/public/.well-known/agent-tools.json +180 -180
  31. package/public/.well-known/ai-assets.json +59 -59
  32. package/public/.well-known/security.txt +8 -8
  33. package/public/.well-known/wab.json +28 -28
  34. package/public/activate.html +448 -368
  35. package/public/adopt.html +236 -0
  36. package/public/adoption-metrics.html +188 -188
  37. package/public/agent-workspace.html +359 -349
  38. package/public/ai.html +198 -198
  39. package/public/api.html +397 -413
  40. package/public/atp.html +171 -0
  41. package/public/azure-dns-integration.html +289 -289
  42. package/public/browser.html +486 -486
  43. package/public/cloudflare-integration.html +380 -380
  44. package/public/commander-dashboard.html +243 -243
  45. package/public/cookies.html +210 -210
  46. package/public/cpanel-integration.html +398 -398
  47. package/public/css/agent-workspace.css +1713 -1713
  48. package/public/css/premium.css +317 -317
  49. package/public/css/styles.css +1401 -1263
  50. package/public/dashboard-shieldlink.html +295 -0
  51. package/public/dashboard.html +711 -707
  52. package/public/dns.html +436 -436
  53. package/public/docs.html +588 -588
  54. package/public/enterprise-mesh.ar.html +80 -0
  55. package/public/enterprise-mesh.html +81 -0
  56. package/public/feed.xml +89 -89
  57. package/public/gcp-dns-integration.html +318 -318
  58. package/public/governance.ar.html +70 -0
  59. package/public/governance.html +69 -0
  60. package/public/growth.html +465 -465
  61. package/public/index.html +1372 -1266
  62. package/public/integrations.html +556 -556
  63. package/public/js/activate.js +449 -145
  64. package/public/js/agent-workspace.js +1740 -1740
  65. package/public/js/auth-nav.js +117 -65
  66. package/public/js/auth-redirect.js +12 -12
  67. package/public/js/cookie-consent.js +56 -56
  68. package/public/js/dns.js +438 -438
  69. package/public/js/wab-demo-page.js +721 -721
  70. package/public/js/ws-client.js +74 -74
  71. package/public/l-preview.html +242 -0
  72. package/public/llms-full.txt +360 -360
  73. package/public/llms.txt +125 -125
  74. package/public/login.html +85 -85
  75. package/public/mesh-dashboard.html +328 -328
  76. package/public/milestones.html +346 -0
  77. package/public/one-click.html +779 -0
  78. package/public/openapi.json +669 -669
  79. package/public/partners.ar.html +145 -0
  80. package/public/partners.html +143 -0
  81. package/public/phone-shield.html +281 -281
  82. package/public/plesk-integration.html +375 -375
  83. package/public/premium-dashboard.html +2489 -2489
  84. package/public/premium.html +793 -793
  85. package/public/privacy.html +297 -297
  86. package/public/provider-onboarding.html +172 -172
  87. package/public/provider-sandbox.html +134 -134
  88. package/public/providers.html +359 -359
  89. package/public/refusals.html +172 -0
  90. package/public/register.html +105 -105
  91. package/public/registrar-integrations.html +141 -141
  92. package/public/ring4.html +292 -0
  93. package/public/robots.txt +99 -99
  94. package/public/route53-integration.html +531 -531
  95. package/public/score.html +263 -0
  96. package/public/script/wab-consent.d.ts +36 -36
  97. package/public/script/wab-consent.js +104 -104
  98. package/public/script/wab-schema.js +131 -131
  99. package/public/script/wab.d.ts +108 -108
  100. package/public/script/wab.min.js +580 -580
  101. package/public/security.txt +8 -8
  102. package/public/shieldlink.html +244 -0
  103. package/public/shieldqr.html +231 -231
  104. package/public/sitemap.xml +13 -1
  105. package/public/terms.html +256 -256
  106. package/public/trust-graph-api.ar.html +92 -0
  107. package/public/trust-graph-api.html +91 -0
  108. package/public/wab-features.html +560 -0
  109. package/public/wab-trust.html +200 -200
  110. package/public/wab-truth.html +375 -0
  111. package/public/wab-vs-protocols.html +210 -210
  112. package/public/whitepaper.html +449 -449
  113. package/script/ai-agent-bridge.js +1754 -1754
  114. package/sdk/README.md +99 -99
  115. package/sdk/agent-mesh.js +449 -449
  116. package/sdk/atp.js +103 -0
  117. package/sdk/auto-discovery.js +301 -288
  118. package/sdk/commander.js +262 -262
  119. package/sdk/governance.js +262 -262
  120. package/sdk/index.d.ts +464 -464
  121. package/sdk/index.js +653 -649
  122. package/sdk/multi-agent.js +318 -318
  123. package/sdk/safe-mode.js +221 -221
  124. package/sdk/safety-shield.js +219 -219
  125. package/sdk/schema-discovery.js +83 -83
  126. package/server/adapters/index.js +520 -520
  127. package/server/config/plans.js +412 -367
  128. package/server/config/secrets.js +102 -102
  129. package/server/control-plane/index.js +301 -301
  130. package/server/data-plane/index.js +354 -354
  131. package/server/index.js +793 -670
  132. package/server/llm/index.js +404 -404
  133. package/server/middleware/adminAuth.js +35 -35
  134. package/server/middleware/api-tier.js +170 -0
  135. package/server/middleware/auth.js +50 -50
  136. package/server/middleware/featureGate.js +88 -88
  137. package/server/middleware/rateLimits.js +100 -100
  138. package/server/middleware/sensitiveAction.js +157 -157
  139. package/server/middleware/wab-trust.js +141 -0
  140. package/server/migrations/001_add_analytics_indexes.sql +7 -7
  141. package/server/migrations/002_premium_features.sql +418 -418
  142. package/server/migrations/003_ads_integer_cents.sql +33 -33
  143. package/server/migrations/004_agent_os.sql +158 -158
  144. package/server/migrations/005_marketplace_metering.sql +126 -126
  145. package/server/migrations/006_growth_suite.sql +138 -0
  146. package/server/migrations/007_governance.sql +106 -106
  147. package/server/migrations/008_plans.sql +144 -144
  148. package/server/migrations/009_shieldqr.sql +30 -30
  149. package/server/migrations/010_extended_trust.sql +33 -33
  150. package/server/migrations/011_outreach.sql +47 -0
  151. package/server/migrations/012_shieldlink.sql +116 -0
  152. package/server/migrations/013_ct_monitor.sql +13 -0
  153. package/server/migrations/014_wab_advanced_features.sql +128 -0
  154. package/server/migrations/015_wab_truth_layer.sql +101 -0
  155. package/server/migrations/016_ring4_external_trust.sql +84 -0
  156. package/server/migrations/017_ring4_extensions.sql +69 -0
  157. package/server/migrations/018_commercial_foundations.sql +167 -0
  158. package/server/migrations/019_unify_tier_constraints.sql +133 -0
  159. package/server/migrations/020_agent_transaction_primitive.sql +119 -0
  160. package/server/models/adapters/index.js +33 -33
  161. package/server/models/adapters/mysql.js +183 -183
  162. package/server/models/adapters/postgresql.js +172 -172
  163. package/server/models/adapters/sqlite.js +7 -7
  164. package/server/models/db.js +740 -740
  165. package/server/observability/failure-analysis.js +337 -337
  166. package/server/observability/index.js +394 -394
  167. package/server/protocol/capabilities.js +223 -223
  168. package/server/protocol/index.js +243 -243
  169. package/server/protocol/schema.js +584 -584
  170. package/server/registry/certification.js +271 -271
  171. package/server/registry/index.js +326 -326
  172. package/server/routes/activate.js +478 -0
  173. package/server/routes/admin-outreach.js +239 -0
  174. package/server/routes/admin-plans.js +76 -76
  175. package/server/routes/admin-premium.js +674 -673
  176. package/server/routes/admin-shieldlink.js +137 -0
  177. package/server/routes/admin-shieldqr.js +90 -90
  178. package/server/routes/admin-trust-monitor.js +139 -83
  179. package/server/routes/admin.js +550 -549
  180. package/server/routes/adopt.js +61 -0
  181. package/server/routes/ads.js +130 -130
  182. package/server/routes/agent-workspace.js +540 -540
  183. package/server/routes/api-keys.js +127 -0
  184. package/server/routes/api.js +150 -150
  185. package/server/routes/auth.js +71 -71
  186. package/server/routes/billing.js +57 -57
  187. package/server/routes/commander.js +316 -316
  188. package/server/routes/customer-shieldlink.js +133 -0
  189. package/server/routes/demo-showcase.js +332 -332
  190. package/server/routes/demo-store.js +154 -154
  191. package/server/routes/diagnose.js +373 -0
  192. package/server/routes/discovery.js +2348 -2348
  193. package/server/routes/enterprise-mesh.js +170 -0
  194. package/server/routes/gateway.js +173 -173
  195. package/server/routes/governance-saas.js +203 -0
  196. package/server/routes/governance.js +208 -208
  197. package/server/routes/growth.js +1048 -0
  198. package/server/routes/intent.js +328 -0
  199. package/server/routes/license.js +251 -251
  200. package/server/routes/mesh.js +469 -469
  201. package/server/routes/noscript.js +543 -543
  202. package/server/routes/partners.js +201 -0
  203. package/server/routes/plans.js +33 -33
  204. package/server/routes/premium-v2.js +686 -686
  205. package/server/routes/premium.js +724 -724
  206. package/server/routes/providers.js +650 -650
  207. package/server/routes/reputation.js +411 -0
  208. package/server/routes/ring4.js +885 -0
  209. package/server/routes/runtime.js +2148 -2148
  210. package/server/routes/shieldlink.js +70 -0
  211. package/server/routes/shieldqr.js +88 -88
  212. package/server/routes/sovereign.js +465 -465
  213. package/server/routes/transactions.js +233 -0
  214. package/server/routes/truth-layer.js +670 -0
  215. package/server/routes/universal.js +200 -200
  216. package/server/routes/unsubscribe.js +51 -0
  217. package/server/routes/wab-api.js +850 -850
  218. package/server/routes/wab-cache.js +282 -0
  219. package/server/runtime/container-worker.js +111 -111
  220. package/server/runtime/container.js +448 -448
  221. package/server/runtime/distributed-worker.js +362 -362
  222. package/server/runtime/event-bus.js +210 -210
  223. package/server/runtime/index.js +253 -253
  224. package/server/runtime/queue.js +599 -599
  225. package/server/runtime/replay.js +666 -666
  226. package/server/runtime/sandbox.js +266 -266
  227. package/server/runtime/scheduler.js +534 -534
  228. package/server/runtime/session-engine.js +293 -293
  229. package/server/runtime/state-manager.js +188 -188
  230. package/server/secrets/wab-signing-key.pem +3 -0
  231. package/server/secrets/wab-signing-pub.pem +3 -0
  232. package/server/security/cross-site-redactor.js +196 -196
  233. package/server/security/dry-run.js +180 -180
  234. package/server/security/human-gate-rate-limit.js +147 -147
  235. package/server/security/human-gate-transports.js +178 -178
  236. package/server/security/human-gate.js +281 -281
  237. package/server/security/index.js +368 -368
  238. package/server/security/intent-engine.js +245 -245
  239. package/server/security/reward-guard.js +171 -171
  240. package/server/security/rollback-store.js +239 -239
  241. package/server/security/token-scope.js +404 -404
  242. package/server/security/url-policy.js +139 -139
  243. package/server/services/adoption-agent.js +182 -0
  244. package/server/services/agent-chat.js +506 -506
  245. package/server/services/agent-learning.js +601 -601
  246. package/server/services/agent-memory.js +625 -625
  247. package/server/services/agent-mesh.js +555 -555
  248. package/server/services/agent-symphony.js +717 -717
  249. package/server/services/agent-tasks.js +1807 -1807
  250. package/server/services/api-key-engine.js +292 -292
  251. package/server/services/cluster.js +894 -894
  252. package/server/services/commander.js +738 -738
  253. package/server/services/edge-compute.js +440 -440
  254. package/server/services/email.js +233 -233
  255. package/server/services/fairness-engine.js +409 -0
  256. package/server/services/fairness.js +420 -0
  257. package/server/services/governance.js +466 -466
  258. package/server/services/hosted-runtime.js +205 -205
  259. package/server/services/lfd.js +635 -635
  260. package/server/services/local-ai.js +389 -389
  261. package/server/services/marketplace.js +270 -270
  262. package/server/services/metering.js +182 -182
  263. package/server/services/modules/affiliate-intelligence.js +93 -93
  264. package/server/services/modules/agent-firewall.js +90 -90
  265. package/server/services/modules/bounty.js +89 -89
  266. package/server/services/modules/collective-bargaining.js +92 -92
  267. package/server/services/modules/dark-pattern.js +66 -66
  268. package/server/services/modules/gov-intelligence.js +45 -45
  269. package/server/services/modules/neural.js +55 -55
  270. package/server/services/modules/notary.js +49 -49
  271. package/server/services/modules/price-time-machine.js +86 -86
  272. package/server/services/modules/protocol.js +104 -104
  273. package/server/services/negotiation.js +439 -439
  274. package/server/services/outreach-agent.js +312 -0
  275. package/server/services/plans.js +214 -214
  276. package/server/services/plugins.js +771 -771
  277. package/server/services/price-intelligence.js +566 -566
  278. package/server/services/price-shield.js +1137 -1137
  279. package/server/services/provider-clients.js +740 -740
  280. package/server/services/reputation.js +465 -465
  281. package/server/services/search-engine.js +357 -357
  282. package/server/services/security.js +513 -513
  283. package/server/services/self-healing.js +843 -843
  284. package/server/services/shieldlink.js +492 -0
  285. package/server/services/shieldqr.js +322 -322
  286. package/server/services/sovereign-shield.js +542 -542
  287. package/server/services/ssl-ct-monitor.js +224 -0
  288. package/server/services/ssl-inspector.js +42 -42
  289. package/server/services/ssl-monitor.js +167 -167
  290. package/server/services/stripe.js +206 -205
  291. package/server/services/swarm.js +788 -788
  292. package/server/services/transactions.js +525 -0
  293. package/server/services/universal-scraper.js +662 -662
  294. package/server/services/verification.js +481 -481
  295. package/server/services/vision.js +1163 -1163
  296. package/server/services/wab-crypto.js +178 -178
  297. package/server/utils/cache.js +125 -125
  298. package/server/utils/migrate.js +81 -81
  299. package/server/utils/safe-fetch.js +228 -228
  300. package/server/utils/secureFields.js +50 -50
  301. package/server/ws.js +161 -161
  302. package/templates/artisan-marketplace.yaml +104 -104
  303. package/templates/book-price-scout.yaml +98 -98
  304. package/templates/electronics-price-tracker.yaml +108 -108
  305. package/templates/flight-deal-hunter.yaml +113 -113
  306. package/templates/freelancer-direct.yaml +116 -116
  307. package/templates/grocery-price-compare.yaml +93 -93
  308. package/templates/hotel-direct-booking.yaml +113 -113
  309. package/templates/local-services.yaml +98 -98
  310. package/templates/olive-oil-tunisia.yaml +88 -88
  311. package/templates/organic-farm-fresh.yaml +101 -101
  312. package/templates/restaurant-direct.yaml +97 -97
  313. package/templates/ring4/banking-sovereign.yaml +55 -0
  314. package/templates/ring4/ecommerce-sovereign.yaml +58 -0
  315. package/templates/ring4/healthcare-sovereign.yaml +60 -0
@@ -1,100 +1,100 @@
1
- /**
2
- * Comprehensive rate limits for all security-sensitive endpoints.
3
- */
4
-
5
- const rateLimit = require('express-rate-limit');
6
-
7
- // ─── Auth endpoints ──────────────────────────────────────────────────
8
-
9
- const authLimiter = rateLimit({
10
- windowMs: 15 * 60 * 1000,
11
- max: 10,
12
- standardHeaders: true,
13
- legacyHeaders: false,
14
- message: { error: 'Too many authentication attempts, please try again later' }
15
- });
16
-
17
- const registerLimiter = rateLimit({
18
- windowMs: 60 * 60 * 1000,
19
- max: 20,
20
- standardHeaders: true,
21
- legacyHeaders: false,
22
- message: { error: 'Too many registration attempts, please try again later' }
23
- });
24
-
25
- const adminLoginLimiter = rateLimit({
26
- windowMs: 15 * 60 * 1000,
27
- max: 5,
28
- standardHeaders: true,
29
- legacyHeaders: false,
30
- message: { error: 'Too many admin login attempts, please try again later' }
31
- });
32
-
33
- // ─── WAB API endpoints ───────────────────────────────────────────────
34
-
35
- const wabAuthenticateLimiter = rateLimit({
36
- windowMs: 15 * 60 * 1000,
37
- max: 20,
38
- standardHeaders: true,
39
- legacyHeaders: false,
40
- keyGenerator: (req) => `${req.ip}:${req.body?.siteId || req.body?.apiKey || 'anon'}`,
41
- message: { error: 'Too many WAB authentication attempts' }
42
- });
43
-
44
- const wabActionLimiter = rateLimit({
45
- windowMs: 60 * 1000,
46
- max: 300,
47
- standardHeaders: true,
48
- legacyHeaders: false,
49
- keyGenerator: (req) => `${req.ip}:${req.wabSession?.siteId || 'anon'}`,
50
- message: { error: 'Too many action requests, please slow down' }
51
- });
52
-
53
- // ─── General API endpoints ───────────────────────────────────────────
54
-
55
- const apiLimiter = rateLimit({
56
- windowMs: 60 * 1000,
57
- max: 300,
58
- standardHeaders: true,
59
- legacyHeaders: false,
60
- message: { error: 'Too many requests, please try again later' }
61
- });
62
-
63
- const searchLimiter = rateLimit({
64
- windowMs: 60 * 1000,
65
- max: 30,
66
- standardHeaders: true,
67
- legacyHeaders: false,
68
- message: { error: 'Too many search requests' }
69
- });
70
-
71
- // ─── License endpoints (existing) ────────────────────────────────────
72
-
73
- const licenseTokenLimiter = rateLimit({
74
- windowMs: 15 * 60 * 1000,
75
- max: 30,
76
- standardHeaders: true,
77
- legacyHeaders: false,
78
- message: { error: 'Too many token requests, please try again later' }
79
- });
80
-
81
- const licenseTrackLimiter = rateLimit({
82
- windowMs: 60 * 1000,
83
- max: 300,
84
- standardHeaders: true,
85
- legacyHeaders: false,
86
- keyGenerator: (req) => `${req.ip}:${req.body?.sessionToken || req.body?.siteId || 'anon'}`,
87
- message: { error: 'Too many track requests, please try again later' }
88
- });
89
-
90
- module.exports = {
91
- authLimiter,
92
- registerLimiter,
93
- adminLoginLimiter,
94
- wabAuthenticateLimiter,
95
- wabActionLimiter,
96
- apiLimiter,
97
- searchLimiter,
98
- licenseTokenLimiter,
99
- licenseTrackLimiter,
100
- };
1
+ /**
2
+ * Comprehensive rate limits for all security-sensitive endpoints.
3
+ */
4
+
5
+ const rateLimit = require('express-rate-limit');
6
+
7
+ // ─── Auth endpoints ──────────────────────────────────────────────────
8
+
9
+ const authLimiter = rateLimit({
10
+ windowMs: 15 * 60 * 1000,
11
+ max: 10,
12
+ standardHeaders: true,
13
+ legacyHeaders: false,
14
+ message: { error: 'Too many authentication attempts, please try again later' }
15
+ });
16
+
17
+ const registerLimiter = rateLimit({
18
+ windowMs: 60 * 60 * 1000,
19
+ max: 20,
20
+ standardHeaders: true,
21
+ legacyHeaders: false,
22
+ message: { error: 'Too many registration attempts, please try again later' }
23
+ });
24
+
25
+ const adminLoginLimiter = rateLimit({
26
+ windowMs: 15 * 60 * 1000,
27
+ max: 5,
28
+ standardHeaders: true,
29
+ legacyHeaders: false,
30
+ message: { error: 'Too many admin login attempts, please try again later' }
31
+ });
32
+
33
+ // ─── WAB API endpoints ───────────────────────────────────────────────
34
+
35
+ const wabAuthenticateLimiter = rateLimit({
36
+ windowMs: 15 * 60 * 1000,
37
+ max: 20,
38
+ standardHeaders: true,
39
+ legacyHeaders: false,
40
+ keyGenerator: (req) => `${req.ip}:${req.body?.siteId || req.body?.apiKey || 'anon'}`,
41
+ message: { error: 'Too many WAB authentication attempts' }
42
+ });
43
+
44
+ const wabActionLimiter = rateLimit({
45
+ windowMs: 60 * 1000,
46
+ max: 300,
47
+ standardHeaders: true,
48
+ legacyHeaders: false,
49
+ keyGenerator: (req) => `${req.ip}:${req.wabSession?.siteId || 'anon'}`,
50
+ message: { error: 'Too many action requests, please slow down' }
51
+ });
52
+
53
+ // ─── General API endpoints ───────────────────────────────────────────
54
+
55
+ const apiLimiter = rateLimit({
56
+ windowMs: 60 * 1000,
57
+ max: 300,
58
+ standardHeaders: true,
59
+ legacyHeaders: false,
60
+ message: { error: 'Too many requests, please try again later' }
61
+ });
62
+
63
+ const searchLimiter = rateLimit({
64
+ windowMs: 60 * 1000,
65
+ max: 30,
66
+ standardHeaders: true,
67
+ legacyHeaders: false,
68
+ message: { error: 'Too many search requests' }
69
+ });
70
+
71
+ // ─── License endpoints (existing) ────────────────────────────────────
72
+
73
+ const licenseTokenLimiter = rateLimit({
74
+ windowMs: 15 * 60 * 1000,
75
+ max: 30,
76
+ standardHeaders: true,
77
+ legacyHeaders: false,
78
+ message: { error: 'Too many token requests, please try again later' }
79
+ });
80
+
81
+ const licenseTrackLimiter = rateLimit({
82
+ windowMs: 60 * 1000,
83
+ max: 300,
84
+ standardHeaders: true,
85
+ legacyHeaders: false,
86
+ keyGenerator: (req) => `${req.ip}:${req.body?.sessionToken || req.body?.siteId || 'anon'}`,
87
+ message: { error: 'Too many track requests, please try again later' }
88
+ });
89
+
90
+ module.exports = {
91
+ authLimiter,
92
+ registerLimiter,
93
+ adminLoginLimiter,
94
+ wabAuthenticateLimiter,
95
+ wabActionLimiter,
96
+ apiLimiter,
97
+ searchLimiter,
98
+ licenseTokenLimiter,
99
+ licenseTrackLimiter,
100
+ };
@@ -1,157 +1,157 @@
1
- 'use strict';
2
-
3
- /**
4
- * Sensitive Action Gate — Human-in-the-Loop confirmation for destructive
5
- * or financially-impactful agent actions.
6
- *
7
- * Threat: prompt-injection or compromised agent issues `purchase`, `transfer`,
8
- * `delete-account`, etc. without user intent.
9
- *
10
- * Defense:
11
- * 1. Maintain a static list of sensitive action verbs.
12
- * 2. If a request payload references one of those verbs, require either:
13
- * - X-WAB-Confirm header containing an HMAC over the request body, OR
14
- * - explicit `confirmed:true` flag set by an authenticated *user* token
15
- * (not just an agent token).
16
- * 3. Otherwise return 412 with a confirmation challenge so the
17
- * orchestrator can surface the prompt to a human.
18
- *
19
- * The HMAC is computed using process.env.HITL_SECRET (falls back to a
20
- * derived secret on first start) over `${actorId}:${actionKey}:${nonce}`.
21
- *
22
- * This is a defense-in-depth gate — site-level policies in
23
- * control-plane/policy-engine remain authoritative.
24
- */
25
-
26
- const crypto = require('crypto');
27
-
28
- const SENSITIVE_VERBS = new Set([
29
- 'purchase',
30
- 'checkout',
31
- 'pay',
32
- 'payment',
33
- 'transfer',
34
- 'wire',
35
- 'send-money',
36
- 'withdraw',
37
- 'delete',
38
- 'delete-account',
39
- 'wipe',
40
- 'unsubscribe-all',
41
- 'cancel-subscription',
42
- 'submit-payment',
43
- 'authorize',
44
- 'sign-contract',
45
- 'change-password',
46
- 'change-email',
47
- 'export-data',
48
- 'grant-admin',
49
- 'revoke-access',
50
- ]);
51
-
52
- let _runtimeSecret = process.env.HITL_SECRET;
53
- function _secret() {
54
- if (_runtimeSecret) return _runtimeSecret;
55
- // Derive a stable per-process secret if none configured. Note: this means
56
- // confirmations don't survive restarts, which is acceptable (HITL tokens
57
- // are short-lived by design).
58
- _runtimeSecret = crypto.randomBytes(32).toString('hex');
59
- return _runtimeSecret;
60
- }
61
-
62
- function _flatten(obj, depth = 0, out = []) {
63
- if (depth > 4 || obj == null) return out;
64
- if (typeof obj === 'string') { out.push(obj.toLowerCase()); return out; }
65
- if (Array.isArray(obj)) { obj.forEach((v) => _flatten(v, depth + 1, out)); return out; }
66
- if (typeof obj === 'object') { Object.values(obj).forEach((v) => _flatten(v, depth + 1, out)); }
67
- return out;
68
- }
69
-
70
- function detectSensitiveAction(body) {
71
- const candidates = _flatten(body || {});
72
- for (const v of candidates) {
73
- if (typeof v !== 'string') continue;
74
- // Match verb tokens: "purchase", "checkout.confirm", etc.
75
- const tokens = v.split(/[\s.\-_/:]+/);
76
- for (const t of tokens) {
77
- if (SENSITIVE_VERBS.has(t)) return t;
78
- }
79
- }
80
- return null;
81
- }
82
-
83
- function makeChallenge(actorId, actionKey) {
84
- const nonce = crypto.randomBytes(12).toString('hex');
85
- const ts = Date.now();
86
- const payload = `${actorId || 'anon'}:${actionKey}:${nonce}:${ts}`;
87
- const hmac = crypto.createHmac('sha256', _secret()).update(payload).digest('hex');
88
- return { nonce, ts, signature: hmac, expiresInMs: 5 * 60 * 1000 };
89
- }
90
-
91
- function verifyConfirmation(header, actorId, actionKey) {
92
- if (!header || typeof header !== 'string') return false;
93
- const parts = header.split('.');
94
- if (parts.length !== 3) return false;
95
- const [nonce, ts, sig] = parts;
96
- const tsNum = parseInt(ts, 10);
97
- if (!nonce || !sig || !tsNum) return false;
98
- if (Date.now() - tsNum > 5 * 60 * 1000) return false; // 5 min window
99
- const payload = `${actorId || 'anon'}:${actionKey}:${nonce}:${tsNum}`;
100
- const expected = crypto.createHmac('sha256', _secret()).update(payload).digest('hex');
101
- try {
102
- return crypto.timingSafeEqual(Buffer.from(sig, 'hex'), Buffer.from(expected, 'hex'));
103
- } catch {
104
- return false;
105
- }
106
- }
107
-
108
- /**
109
- * Express middleware. Apply to runtime/execute/policy routes.
110
- * Bypassed when X-WAB-Confirm verifies, or when an authenticated USER token
111
- * (req.user) explicitly sets `confirmed:true` in the body.
112
- */
113
- function sensitiveActionGate(req, res, next) {
114
- const action = detectSensitiveAction(req.body);
115
- if (!action) return next();
116
-
117
- const actorId = req.user?.id || req.agentId || req.session?.agentId || req.ip;
118
- const actionKey = `${req.method}:${req.baseUrl || ''}${req.path}:${action}`;
119
-
120
- // 1) User-supplied confirmation header (preferred)
121
- const header = req.headers['x-wab-confirm'];
122
- if (header && verifyConfirmation(header, actorId, action)) {
123
- req._hitlConfirmed = action;
124
- return next();
125
- }
126
-
127
- // 2) Logged-in user explicitly confirmed in body
128
- if (req.user && req.body && req.body.confirmed === true && req.body.confirmedAction === action) {
129
- req._hitlConfirmed = action;
130
- return next();
131
- }
132
-
133
- // Otherwise issue a challenge.
134
- const challenge = makeChallenge(actorId, action);
135
- return res.status(412).json({
136
- error: 'Human-in-the-loop confirmation required',
137
- code: 'HITL_REQUIRED',
138
- sensitiveAction: action,
139
- challenge: {
140
- nonce: challenge.nonce,
141
- ts: challenge.ts,
142
- signature: challenge.signature,
143
- headerName: 'X-WAB-Confirm',
144
- headerValue: `${challenge.nonce}.${challenge.ts}.${challenge.signature}`,
145
- expiresInMs: challenge.expiresInMs,
146
- },
147
- hint: 'Resubmit the same request with the X-WAB-Confirm header carrying the headerValue above, after a human has approved the action.',
148
- });
149
- }
150
-
151
- module.exports = {
152
- sensitiveActionGate,
153
- detectSensitiveAction,
154
- makeChallenge,
155
- verifyConfirmation,
156
- SENSITIVE_VERBS,
157
- };
1
+ 'use strict';
2
+
3
+ /**
4
+ * Sensitive Action Gate — Human-in-the-Loop confirmation for destructive
5
+ * or financially-impactful agent actions.
6
+ *
7
+ * Threat: prompt-injection or compromised agent issues `purchase`, `transfer`,
8
+ * `delete-account`, etc. without user intent.
9
+ *
10
+ * Defense:
11
+ * 1. Maintain a static list of sensitive action verbs.
12
+ * 2. If a request payload references one of those verbs, require either:
13
+ * - X-WAB-Confirm header containing an HMAC over the request body, OR
14
+ * - explicit `confirmed:true` flag set by an authenticated *user* token
15
+ * (not just an agent token).
16
+ * 3. Otherwise return 412 with a confirmation challenge so the
17
+ * orchestrator can surface the prompt to a human.
18
+ *
19
+ * The HMAC is computed using process.env.HITL_SECRET (falls back to a
20
+ * derived secret on first start) over `${actorId}:${actionKey}:${nonce}`.
21
+ *
22
+ * This is a defense-in-depth gate — site-level policies in
23
+ * control-plane/policy-engine remain authoritative.
24
+ */
25
+
26
+ const crypto = require('crypto');
27
+
28
+ const SENSITIVE_VERBS = new Set([
29
+ 'purchase',
30
+ 'checkout',
31
+ 'pay',
32
+ 'payment',
33
+ 'transfer',
34
+ 'wire',
35
+ 'send-money',
36
+ 'withdraw',
37
+ 'delete',
38
+ 'delete-account',
39
+ 'wipe',
40
+ 'unsubscribe-all',
41
+ 'cancel-subscription',
42
+ 'submit-payment',
43
+ 'authorize',
44
+ 'sign-contract',
45
+ 'change-password',
46
+ 'change-email',
47
+ 'export-data',
48
+ 'grant-admin',
49
+ 'revoke-access',
50
+ ]);
51
+
52
+ let _runtimeSecret = process.env.HITL_SECRET;
53
+ function _secret() {
54
+ if (_runtimeSecret) return _runtimeSecret;
55
+ // Derive a stable per-process secret if none configured. Note: this means
56
+ // confirmations don't survive restarts, which is acceptable (HITL tokens
57
+ // are short-lived by design).
58
+ _runtimeSecret = crypto.randomBytes(32).toString('hex');
59
+ return _runtimeSecret;
60
+ }
61
+
62
+ function _flatten(obj, depth = 0, out = []) {
63
+ if (depth > 4 || obj == null) return out;
64
+ if (typeof obj === 'string') { out.push(obj.toLowerCase()); return out; }
65
+ if (Array.isArray(obj)) { obj.forEach((v) => _flatten(v, depth + 1, out)); return out; }
66
+ if (typeof obj === 'object') { Object.values(obj).forEach((v) => _flatten(v, depth + 1, out)); }
67
+ return out;
68
+ }
69
+
70
+ function detectSensitiveAction(body) {
71
+ const candidates = _flatten(body || {});
72
+ for (const v of candidates) {
73
+ if (typeof v !== 'string') continue;
74
+ // Match verb tokens: "purchase", "checkout.confirm", etc.
75
+ const tokens = v.split(/[\s.\-_/:]+/);
76
+ for (const t of tokens) {
77
+ if (SENSITIVE_VERBS.has(t)) return t;
78
+ }
79
+ }
80
+ return null;
81
+ }
82
+
83
+ function makeChallenge(actorId, actionKey) {
84
+ const nonce = crypto.randomBytes(12).toString('hex');
85
+ const ts = Date.now();
86
+ const payload = `${actorId || 'anon'}:${actionKey}:${nonce}:${ts}`;
87
+ const hmac = crypto.createHmac('sha256', _secret()).update(payload).digest('hex');
88
+ return { nonce, ts, signature: hmac, expiresInMs: 5 * 60 * 1000 };
89
+ }
90
+
91
+ function verifyConfirmation(header, actorId, actionKey) {
92
+ if (!header || typeof header !== 'string') return false;
93
+ const parts = header.split('.');
94
+ if (parts.length !== 3) return false;
95
+ const [nonce, ts, sig] = parts;
96
+ const tsNum = parseInt(ts, 10);
97
+ if (!nonce || !sig || !tsNum) return false;
98
+ if (Date.now() - tsNum > 5 * 60 * 1000) return false; // 5 min window
99
+ const payload = `${actorId || 'anon'}:${actionKey}:${nonce}:${tsNum}`;
100
+ const expected = crypto.createHmac('sha256', _secret()).update(payload).digest('hex');
101
+ try {
102
+ return crypto.timingSafeEqual(Buffer.from(sig, 'hex'), Buffer.from(expected, 'hex'));
103
+ } catch {
104
+ return false;
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Express middleware. Apply to runtime/execute/policy routes.
110
+ * Bypassed when X-WAB-Confirm verifies, or when an authenticated USER token
111
+ * (req.user) explicitly sets `confirmed:true` in the body.
112
+ */
113
+ function sensitiveActionGate(req, res, next) {
114
+ const action = detectSensitiveAction(req.body);
115
+ if (!action) return next();
116
+
117
+ const actorId = req.user?.id || req.agentId || req.session?.agentId || req.ip;
118
+ const actionKey = `${req.method}:${req.baseUrl || ''}${req.path}:${action}`;
119
+
120
+ // 1) User-supplied confirmation header (preferred)
121
+ const header = req.headers['x-wab-confirm'];
122
+ if (header && verifyConfirmation(header, actorId, action)) {
123
+ req._hitlConfirmed = action;
124
+ return next();
125
+ }
126
+
127
+ // 2) Logged-in user explicitly confirmed in body
128
+ if (req.user && req.body && req.body.confirmed === true && req.body.confirmedAction === action) {
129
+ req._hitlConfirmed = action;
130
+ return next();
131
+ }
132
+
133
+ // Otherwise issue a challenge.
134
+ const challenge = makeChallenge(actorId, action);
135
+ return res.status(412).json({
136
+ error: 'Human-in-the-loop confirmation required',
137
+ code: 'HITL_REQUIRED',
138
+ sensitiveAction: action,
139
+ challenge: {
140
+ nonce: challenge.nonce,
141
+ ts: challenge.ts,
142
+ signature: challenge.signature,
143
+ headerName: 'X-WAB-Confirm',
144
+ headerValue: `${challenge.nonce}.${challenge.ts}.${challenge.signature}`,
145
+ expiresInMs: challenge.expiresInMs,
146
+ },
147
+ hint: 'Resubmit the same request with the X-WAB-Confirm header carrying the headerValue above, after a human has approved the action.',
148
+ });
149
+ }
150
+
151
+ module.exports = {
152
+ sensitiveActionGate,
153
+ detectSensitiveAction,
154
+ makeChallenge,
155
+ verifyConfirmation,
156
+ SENSITIVE_VERBS,
157
+ };
@@ -0,0 +1,141 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════
2
+ // WAB Trust Headers Middleware (Ring 4)
3
+ //
4
+ // Recognizes:
5
+ // X-WAB-Trust-Domain — the sovereign agent's declared trusted origin
6
+ // X-WAB-Signature — Ed25519 signature (base64, "ed25519:..." or raw)
7
+ // X-WAB-Trust-Nonce — replay-defence nonce
8
+ //
9
+ // Behavior:
10
+ // - Looks up the domain's registered Ring 4 trust profile.
11
+ // - If signature present, verifies it against the profile's signed_by_pk
12
+ // using the canonical message: `${method} ${path}\n${nonce}`
13
+ // - Attaches `req.wabTrust = { domain, verified, profile, headers }`
14
+ // - NEVER blocks the request. Downstream handlers decide what to do.
15
+ // - Logs every recognition / verification event to ring4_interaction_log.
16
+ // ═══════════════════════════════════════════════════════════════════════════
17
+
18
+ const crypto = require('crypto');
19
+ const { db } = require('../models/db');
20
+
21
+ function ed25519Verify(publicKeyB64, message, signatureB64) {
22
+ try {
23
+ const pk = Buffer.from(publicKeyB64, 'base64');
24
+ if (pk.length !== 32) return false;
25
+ const der = Buffer.concat([Buffer.from('302a300506032b6570032100', 'hex'), pk]);
26
+ const keyObj = crypto.createPublicKey({ key: der, format: 'der', type: 'spki' });
27
+ const sig = Buffer.from(signatureB64, 'base64');
28
+ const msg = Buffer.isBuffer(message) ? message : Buffer.from(message, 'utf8');
29
+ return crypto.verify(null, msg, keyObj, sig);
30
+ } catch {
31
+ return false;
32
+ }
33
+ }
34
+
35
+ const DOMAIN_RE = /^[a-z0-9.-]{3,253}$/i;
36
+ const NONCE_RE = /^[A-Za-z0-9_\-]{8,128}$/;
37
+ const PROJECT_RE = /^[a-z0-9-]{2,64}$/i;
38
+ const TRACEPARENT_RE = /^[0-9a-f]{2}-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$/i;
39
+
40
+ function stripPrefix(s) {
41
+ return typeof s === 'string' && s.startsWith('ed25519:') ? s.slice(8) : s;
42
+ }
43
+
44
+ function wabTrustMiddleware(req, _res, next) {
45
+ // Trace context passthrough (always, even without trust headers).
46
+ const traceparent = String(req.headers['traceparent'] || '').trim();
47
+ const tracestate = String(req.headers['tracestate'] || '').trim();
48
+ const trace = TRACEPARENT_RE.test(traceparent)
49
+ ? { traceparent, tracestate: tracestate || null }
50
+ : null;
51
+
52
+ const domain = String(req.headers['x-wab-trust-domain'] || '').toLowerCase().trim();
53
+ if (!domain || !DOMAIN_RE.test(domain)) {
54
+ if (trace) req.wabTrust = { recognized: false, verified: false, trace };
55
+ return next();
56
+ }
57
+
58
+ const sigHdr = String(req.headers['x-wab-signature'] || '').trim();
59
+ const nonce = String(req.headers['x-wab-trust-nonce'] || '').trim();
60
+ const projectHdr = String(req.headers['x-wab-trust-project'] || '').trim();
61
+
62
+ let profile = null;
63
+ try {
64
+ profile = db.prepare(`
65
+ SELECT domain, label, capabilities, constraints, signed_by_pk, expires_at, trust_score
66
+ FROM ring4_trust_profiles
67
+ WHERE domain = ?
68
+ `).get(domain);
69
+ } catch { /* table not yet migrated */ }
70
+
71
+ // Resolve verification key — prefer project pubkey when supplied, else profile.signed_by_pk.
72
+ let verifyPk = null;
73
+ let pkSource = null;
74
+ if (projectHdr && PROJECT_RE.test(projectHdr)) {
75
+ try {
76
+ const proj = db.prepare(`SELECT public_key FROM ring4_projects WHERE project_id = ?`).get(projectHdr);
77
+ if (proj && proj.public_key) { verifyPk = proj.public_key; pkSource = 'project'; }
78
+ } catch { /* swallow */ }
79
+ }
80
+ if (!verifyPk && profile && profile.signed_by_pk) {
81
+ verifyPk = profile.signed_by_pk;
82
+ pkSource = 'profile';
83
+ }
84
+
85
+ const expired = profile ? new Date(profile.expires_at).getTime() < Date.now() : false;
86
+ let verified = false;
87
+ let verifyDetail = null;
88
+
89
+ if (sigHdr && verifyPk && nonce && NONCE_RE.test(nonce)) {
90
+ const sigRaw = stripPrefix(sigHdr);
91
+ const pkRaw = stripPrefix(verifyPk);
92
+ const message = `${req.method} ${req.originalUrl || req.url}\n${nonce}`;
93
+ verified = ed25519Verify(pkRaw, message, sigRaw);
94
+ verifyDetail = verified ? `sig_ok(${pkSource})` : `sig_invalid(${pkSource})`;
95
+ } else if (profile) {
96
+ verifyDetail = 'recognized_without_signature';
97
+ }
98
+
99
+ req.wabTrust = {
100
+ domain,
101
+ recognized: !!profile,
102
+ verified,
103
+ expired,
104
+ profile: profile ? {
105
+ label: profile.label,
106
+ trust_score: profile.trust_score,
107
+ capabilities: safeJson(profile.capabilities),
108
+ constraints: safeJson(profile.constraints),
109
+ expires_at: profile.expires_at
110
+ } : null,
111
+ project_id: (projectHdr && PROJECT_RE.test(projectHdr)) ? projectHdr : null,
112
+ pk_source: pkSource,
113
+ nonce: nonce || null,
114
+ detail: verifyDetail,
115
+ trace
116
+ };
117
+
118
+ // Best-effort audit log (never blocks the request)
119
+ if (profile) {
120
+ try {
121
+ db.prepare(`
122
+ INSERT INTO ring4_interaction_log (project_id, domain, event_type, signature_valid, outcome, detail, agent_nonce)
123
+ VALUES (?, ?, ?, ?, ?, ?, ?)
124
+ `).run(
125
+ req.wabTrust.project_id || 'wab-system',
126
+ domain,
127
+ sigHdr ? 'verify' : 'recognize',
128
+ sigHdr ? (verified ? 1 : 0) : null,
129
+ expired ? 'expired' : (sigHdr ? (verified ? 'allow' : 'refuse') : 'recognize'),
130
+ (trace ? `tp=${traceparent.slice(0, 36)}; ` : '') + (verifyDetail || ''),
131
+ nonce || null
132
+ );
133
+ } catch { /* swallow */ }
134
+ }
135
+
136
+ next();
137
+ }
138
+
139
+ function safeJson(s) { try { return JSON.parse(s || '{}'); } catch { return {}; } }
140
+
141
+ module.exports = { wabTrustMiddleware };
@@ -1,7 +1,7 @@
1
- -- Migration 001: Add composite indexes for analytics performance
2
- -- Created: 2024-12-01
3
-
4
- CREATE INDEX IF NOT EXISTS idx_analytics_site_action ON analytics(site_id, action_name);
5
- CREATE INDEX IF NOT EXISTS idx_analytics_site_created ON analytics(site_id, created_at);
6
- CREATE INDEX IF NOT EXISTS idx_subscriptions_user ON subscriptions(user_id);
7
- CREATE INDEX IF NOT EXISTS idx_subscriptions_status ON subscriptions(status);
1
+ -- Migration 001: Add composite indexes for analytics performance
2
+ -- Created: 2024-12-01
3
+
4
+ CREATE INDEX IF NOT EXISTS idx_analytics_site_action ON analytics(site_id, action_name);
5
+ CREATE INDEX IF NOT EXISTS idx_analytics_site_created ON analytics(site_id, created_at);
6
+ CREATE INDEX IF NOT EXISTS idx_subscriptions_user ON subscriptions(user_id);
7
+ CREATE INDEX IF NOT EXISTS idx_subscriptions_status ON subscriptions(status);