web-agent-bridge 3.4.0 → 3.8.1
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.
- package/LICENSE +84 -84
- package/README.ar.md +1563 -1304
- package/README.md +137 -298
- package/bin/agent-runner.js +474 -474
- package/bin/cli.js +237 -237
- package/bin/wab-init.js +244 -223
- package/bin/wab.js +80 -80
- package/examples/azure-dns-wab.js +83 -83
- package/examples/bidi-agent.js +119 -119
- package/examples/cloudflare-wab-dns.js +121 -121
- package/examples/cpanel-wab-dns.js +114 -114
- package/examples/cross-site-agent.js +91 -91
- package/examples/dns-discovery-agent.js +166 -166
- package/examples/gcp-dns-wab.js +76 -76
- package/examples/governance-agent.js +169 -169
- package/examples/mcp-agent.js +94 -94
- package/examples/next-app-router/README.md +44 -44
- package/examples/plesk-wab-dns.js +103 -103
- package/examples/puppeteer-agent.js +108 -108
- package/examples/route53-wab-dns.js +144 -144
- package/examples/saas-dashboard/README.md +55 -55
- package/examples/safe-mode-agent.js +96 -96
- package/examples/self-discovery.js +106 -0
- package/examples/shopify-hydrogen/README.md +74 -74
- package/examples/vision-agent.js +171 -171
- package/examples/wab-sign.js +74 -74
- package/examples/wab-verify.js +60 -60
- package/examples/wordpress-elementor/README.md +77 -77
- package/package.json +93 -93
- package/public/.well-known/agent-tools.json +180 -180
- package/public/.well-known/ai-assets.json +59 -59
- package/public/.well-known/security.txt +8 -8
- package/public/.well-known/wab.json +28 -28
- package/public/activate.html +448 -368
- package/public/adopt.html +236 -0
- package/public/adoption-metrics.html +188 -188
- package/public/agent-workspace.html +359 -349
- package/public/ai.html +198 -198
- package/public/api.html +397 -413
- package/public/azure-dns-integration.html +289 -289
- package/public/browser.html +486 -486
- package/public/cloudflare-integration.html +380 -380
- package/public/commander-dashboard.html +243 -243
- package/public/cookies.html +210 -210
- package/public/cpanel-integration.html +398 -398
- package/public/css/agent-workspace.css +1713 -1713
- package/public/css/premium.css +317 -317
- package/public/css/styles.css +1401 -1263
- package/public/dashboard-shieldlink.html +295 -0
- package/public/dashboard.html +711 -707
- package/public/dns.html +436 -436
- package/public/docs.html +588 -588
- package/public/enterprise-mesh.ar.html +80 -0
- package/public/enterprise-mesh.html +81 -0
- package/public/feed.xml +89 -89
- package/public/gcp-dns-integration.html +318 -318
- package/public/governance.ar.html +70 -0
- package/public/governance.html +69 -0
- package/public/growth.html +465 -465
- package/public/index.html +1372 -1266
- package/public/integrations.html +556 -556
- package/public/js/activate.js +449 -145
- package/public/js/agent-workspace.js +1740 -1740
- package/public/js/auth-nav.js +117 -65
- package/public/js/auth-redirect.js +12 -12
- package/public/js/cookie-consent.js +56 -56
- package/public/js/dns.js +438 -438
- package/public/js/wab-demo-page.js +721 -721
- package/public/js/ws-client.js +74 -74
- package/public/l-preview.html +242 -0
- package/public/llms-full.txt +360 -360
- package/public/llms.txt +125 -125
- package/public/login.html +85 -85
- package/public/mesh-dashboard.html +328 -328
- package/public/milestones.html +346 -0
- package/public/one-click.html +779 -0
- package/public/openapi.json +669 -669
- package/public/partners.ar.html +145 -0
- package/public/partners.html +143 -0
- package/public/phone-shield.html +281 -281
- package/public/plesk-integration.html +375 -375
- package/public/premium-dashboard.html +2489 -2489
- package/public/premium.html +793 -793
- package/public/privacy.html +297 -297
- package/public/provider-onboarding.html +172 -172
- package/public/provider-sandbox.html +134 -134
- package/public/providers.html +359 -359
- package/public/refusals.html +172 -0
- package/public/register.html +105 -105
- package/public/registrar-integrations.html +141 -141
- package/public/ring4.html +292 -0
- package/public/robots.txt +99 -99
- package/public/route53-integration.html +531 -531
- package/public/score.html +263 -0
- package/public/script/wab-consent.d.ts +36 -36
- package/public/script/wab-consent.js +104 -104
- package/public/script/wab-schema.js +131 -131
- package/public/script/wab.d.ts +108 -108
- package/public/script/wab.min.js +580 -580
- package/public/security.txt +8 -8
- package/public/shieldlink.html +244 -0
- package/public/shieldqr.html +231 -231
- package/public/sitemap.xml +13 -1
- package/public/terms.html +256 -256
- package/public/trust-graph-api.ar.html +92 -0
- package/public/trust-graph-api.html +91 -0
- package/public/wab-features.html +560 -0
- package/public/wab-trust.html +200 -200
- package/public/wab-truth.html +375 -0
- package/public/wab-vs-protocols.html +210 -210
- package/public/whitepaper.html +449 -449
- package/script/ai-agent-bridge.js +1754 -1754
- package/sdk/README.md +99 -99
- package/sdk/agent-mesh.js +449 -449
- package/sdk/auto-discovery.js +301 -288
- package/sdk/commander.js +262 -262
- package/sdk/governance.js +262 -262
- package/sdk/index.d.ts +464 -464
- package/sdk/index.js +649 -649
- package/sdk/multi-agent.js +318 -318
- package/sdk/safe-mode.js +221 -221
- package/sdk/safety-shield.js +219 -219
- package/sdk/schema-discovery.js +83 -83
- package/server/adapters/index.js +520 -520
- package/server/config/plans.js +412 -367
- package/server/config/secrets.js +102 -102
- package/server/control-plane/index.js +301 -301
- package/server/data-plane/index.js +354 -354
- package/server/index.js +790 -670
- package/server/llm/index.js +404 -404
- package/server/middleware/adminAuth.js +35 -35
- package/server/middleware/api-tier.js +170 -0
- package/server/middleware/auth.js +50 -50
- package/server/middleware/featureGate.js +88 -88
- package/server/middleware/rateLimits.js +100 -100
- package/server/middleware/sensitiveAction.js +157 -157
- package/server/middleware/wab-trust.js +141 -0
- package/server/migrations/001_add_analytics_indexes.sql +7 -7
- package/server/migrations/002_premium_features.sql +418 -418
- package/server/migrations/003_ads_integer_cents.sql +33 -33
- package/server/migrations/004_agent_os.sql +158 -158
- package/server/migrations/005_marketplace_metering.sql +126 -126
- package/server/migrations/006_growth_suite.sql +138 -0
- package/server/migrations/007_governance.sql +106 -106
- package/server/migrations/008_plans.sql +144 -144
- package/server/migrations/009_shieldqr.sql +30 -30
- package/server/migrations/010_extended_trust.sql +33 -33
- package/server/migrations/011_outreach.sql +47 -0
- package/server/migrations/012_shieldlink.sql +116 -0
- package/server/migrations/013_ct_monitor.sql +13 -0
- package/server/migrations/014_wab_advanced_features.sql +128 -0
- package/server/migrations/015_wab_truth_layer.sql +101 -0
- package/server/migrations/016_ring4_external_trust.sql +84 -0
- package/server/migrations/017_ring4_extensions.sql +69 -0
- package/server/migrations/018_commercial_foundations.sql +167 -0
- package/server/migrations/019_unify_tier_constraints.sql +133 -0
- package/server/models/adapters/index.js +33 -33
- package/server/models/adapters/mysql.js +183 -183
- package/server/models/adapters/postgresql.js +172 -172
- package/server/models/adapters/sqlite.js +7 -7
- package/server/models/db.js +740 -740
- package/server/observability/failure-analysis.js +337 -337
- package/server/observability/index.js +394 -394
- package/server/protocol/capabilities.js +223 -223
- package/server/protocol/index.js +243 -243
- package/server/protocol/schema.js +584 -584
- package/server/registry/certification.js +271 -271
- package/server/registry/index.js +326 -326
- package/server/routes/activate.js +478 -0
- package/server/routes/admin-outreach.js +239 -0
- package/server/routes/admin-plans.js +76 -76
- package/server/routes/admin-premium.js +674 -673
- package/server/routes/admin-shieldlink.js +137 -0
- package/server/routes/admin-shieldqr.js +90 -90
- package/server/routes/admin-trust-monitor.js +139 -83
- package/server/routes/admin.js +550 -549
- package/server/routes/adopt.js +61 -0
- package/server/routes/ads.js +130 -130
- package/server/routes/agent-workspace.js +540 -540
- package/server/routes/api-keys.js +127 -0
- package/server/routes/api.js +150 -150
- package/server/routes/auth.js +71 -71
- package/server/routes/billing.js +57 -57
- package/server/routes/commander.js +316 -316
- package/server/routes/customer-shieldlink.js +133 -0
- package/server/routes/demo-showcase.js +332 -332
- package/server/routes/demo-store.js +154 -154
- package/server/routes/diagnose.js +373 -0
- package/server/routes/discovery.js +2348 -2348
- package/server/routes/enterprise-mesh.js +170 -0
- package/server/routes/gateway.js +173 -173
- package/server/routes/governance-saas.js +203 -0
- package/server/routes/governance.js +208 -208
- package/server/routes/growth.js +1048 -0
- package/server/routes/intent.js +328 -0
- package/server/routes/license.js +251 -251
- package/server/routes/mesh.js +469 -469
- package/server/routes/noscript.js +543 -543
- package/server/routes/partners.js +201 -0
- package/server/routes/plans.js +33 -33
- package/server/routes/premium-v2.js +686 -686
- package/server/routes/premium.js +724 -724
- package/server/routes/providers.js +650 -650
- package/server/routes/reputation.js +411 -0
- package/server/routes/ring4.js +885 -0
- package/server/routes/runtime.js +2148 -2148
- package/server/routes/shieldlink.js +70 -0
- package/server/routes/shieldqr.js +88 -88
- package/server/routes/sovereign.js +465 -465
- package/server/routes/truth-layer.js +670 -0
- package/server/routes/universal.js +200 -200
- package/server/routes/unsubscribe.js +51 -0
- package/server/routes/wab-api.js +850 -850
- package/server/routes/wab-cache.js +282 -0
- package/server/runtime/container-worker.js +111 -111
- package/server/runtime/container.js +448 -448
- package/server/runtime/distributed-worker.js +362 -362
- package/server/runtime/event-bus.js +210 -210
- package/server/runtime/index.js +253 -253
- package/server/runtime/queue.js +599 -599
- package/server/runtime/replay.js +666 -666
- package/server/runtime/sandbox.js +266 -266
- package/server/runtime/scheduler.js +534 -534
- package/server/runtime/session-engine.js +293 -293
- package/server/runtime/state-manager.js +188 -188
- package/server/secrets/wab-signing-key.pem +3 -0
- package/server/secrets/wab-signing-pub.pem +3 -0
- package/server/security/cross-site-redactor.js +196 -196
- package/server/security/dry-run.js +180 -180
- package/server/security/human-gate-rate-limit.js +147 -147
- package/server/security/human-gate-transports.js +178 -178
- package/server/security/human-gate.js +281 -281
- package/server/security/index.js +368 -368
- package/server/security/intent-engine.js +245 -245
- package/server/security/reward-guard.js +171 -171
- package/server/security/rollback-store.js +239 -239
- package/server/security/token-scope.js +404 -404
- package/server/security/url-policy.js +139 -139
- package/server/services/adoption-agent.js +182 -0
- package/server/services/agent-chat.js +506 -506
- package/server/services/agent-learning.js +601 -601
- package/server/services/agent-memory.js +625 -625
- package/server/services/agent-mesh.js +555 -555
- package/server/services/agent-symphony.js +717 -717
- package/server/services/agent-tasks.js +1807 -1807
- package/server/services/api-key-engine.js +292 -292
- package/server/services/cluster.js +894 -894
- package/server/services/commander.js +738 -738
- package/server/services/edge-compute.js +440 -440
- package/server/services/email.js +233 -233
- package/server/services/fairness-engine.js +409 -0
- package/server/services/fairness.js +420 -0
- package/server/services/governance.js +466 -466
- package/server/services/hosted-runtime.js +205 -205
- package/server/services/lfd.js +635 -635
- package/server/services/local-ai.js +389 -389
- package/server/services/marketplace.js +270 -270
- package/server/services/metering.js +182 -182
- package/server/services/modules/affiliate-intelligence.js +93 -93
- package/server/services/modules/agent-firewall.js +90 -90
- package/server/services/modules/bounty.js +89 -89
- package/server/services/modules/collective-bargaining.js +92 -92
- package/server/services/modules/dark-pattern.js +66 -66
- package/server/services/modules/gov-intelligence.js +45 -45
- package/server/services/modules/neural.js +55 -55
- package/server/services/modules/notary.js +49 -49
- package/server/services/modules/price-time-machine.js +86 -86
- package/server/services/modules/protocol.js +104 -104
- package/server/services/negotiation.js +439 -439
- package/server/services/outreach-agent.js +312 -0
- package/server/services/plans.js +214 -214
- package/server/services/plugins.js +771 -771
- package/server/services/price-intelligence.js +566 -566
- package/server/services/price-shield.js +1137 -1137
- package/server/services/provider-clients.js +740 -740
- package/server/services/reputation.js +465 -465
- package/server/services/search-engine.js +357 -357
- package/server/services/security.js +513 -513
- package/server/services/self-healing.js +843 -843
- package/server/services/shieldlink.js +492 -0
- package/server/services/shieldqr.js +322 -322
- package/server/services/sovereign-shield.js +542 -542
- package/server/services/ssl-ct-monitor.js +224 -0
- package/server/services/ssl-inspector.js +42 -42
- package/server/services/ssl-monitor.js +167 -167
- package/server/services/stripe.js +206 -205
- package/server/services/swarm.js +788 -788
- package/server/services/universal-scraper.js +662 -662
- package/server/services/verification.js +481 -481
- package/server/services/vision.js +1163 -1163
- package/server/services/wab-crypto.js +178 -178
- package/server/utils/cache.js +125 -125
- package/server/utils/migrate.js +81 -81
- package/server/utils/safe-fetch.js +228 -228
- package/server/utils/secureFields.js +50 -50
- package/server/ws.js +161 -161
- package/templates/artisan-marketplace.yaml +104 -104
- package/templates/book-price-scout.yaml +98 -98
- package/templates/electronics-price-tracker.yaml +108 -108
- package/templates/flight-deal-hunter.yaml +113 -113
- package/templates/freelancer-direct.yaml +116 -116
- package/templates/grocery-price-compare.yaml +93 -93
- package/templates/hotel-direct-booking.yaml +113 -113
- package/templates/local-services.yaml +98 -98
- package/templates/olive-oil-tunisia.yaml +88 -88
- package/templates/organic-farm-fresh.yaml +101 -101
- package/templates/restaurant-direct.yaml +97 -97
- package/templates/ring4/banking-sovereign.yaml +55 -0
- package/templates/ring4/ecommerce-sovereign.yaml +58 -0
- 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);
|