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
package/sdk/safe-mode.js
CHANGED
|
@@ -1,221 +1,221 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WAB Safe Mode — Agent-side trust gate.
|
|
3
|
-
*
|
|
4
|
-
* Splits the web into Trusted (WAB + valid signature) vs Untrusted, and
|
|
5
|
-
* gives the agent a single function to ask before any action:
|
|
6
|
-
* await safeMode.evaluate(domain) → { level, verdict, allow_execute,
|
|
7
|
-
* allow_read, reason }
|
|
8
|
-
*
|
|
9
|
-
* Trust levels:
|
|
10
|
-
* 3 — DNS + Ed25519 signature valid + telemetry score ≥ 60 (full execute)
|
|
11
|
-
* 2 — DNS + valid wab.json (no signature OR no telemetry) (limited execute)
|
|
12
|
-
* 1 — Resolves but no _wab record / score below threshold (read-only)
|
|
13
|
-
* 0 — Compliance verdict = deny / suspicious (block)
|
|
14
|
-
*
|
|
15
|
-
* Usage (Node):
|
|
16
|
-
* const { WABSafeMode } = require('web-agent-bridge/sdk');
|
|
17
|
-
* const safe = new WABSafeMode({ apiBase: 'https://webagentbridge.com' });
|
|
18
|
-
* const v = await safe.evaluate('example.com');
|
|
19
|
-
* if (v.allow_execute) await agent.execute(...);
|
|
20
|
-
* else if (v.allow_read) await agent.readOnly(...);
|
|
21
|
-
* else throw new Error('Blocked by Safe Mode: ' + v.reason);
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
'use strict';
|
|
25
|
-
|
|
26
|
-
const DEFAULT_API = 'https://webagentbridge.com';
|
|
27
|
-
|
|
28
|
-
const POLICIES = {
|
|
29
|
-
strict: { require_dnssec: true, require_signature: true, min_score: 75 },
|
|
30
|
-
standard: { require_dnssec: false, require_signature: true, min_score: 60 },
|
|
31
|
-
permissive: { require_dnssec: false, require_signature: false, min_score: 40 },
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
class WABSafeMode {
|
|
35
|
-
/**
|
|
36
|
-
* @param {object} [opts]
|
|
37
|
-
* @param {string} [opts.apiBase='https://webagentbridge.com']
|
|
38
|
-
* @param {'strict'|'standard'|'permissive'} [opts.policy='standard']
|
|
39
|
-
* @param {number} [opts.cacheTtlMs=60000] — verdicts cached this long
|
|
40
|
-
* @param {number} [opts.timeoutMs=8000]
|
|
41
|
-
* @param {function} [opts.fetch] — fetch impl (defaults to global fetch)
|
|
42
|
-
*/
|
|
43
|
-
constructor(opts = {}) {
|
|
44
|
-
this.apiBase = (opts.apiBase || DEFAULT_API).replace(/\/+$/, '');
|
|
45
|
-
this.policy = POLICIES[opts.policy] ? opts.policy : 'standard';
|
|
46
|
-
this.cacheTtl = Number.isFinite(opts.cacheTtlMs) ? opts.cacheTtlMs : 60_000;
|
|
47
|
-
this.timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : 8_000;
|
|
48
|
-
this._fetch = opts.fetch || (typeof fetch !== 'undefined' ? fetch : null);
|
|
49
|
-
this._cache = new Map(); // domain → { at, value }
|
|
50
|
-
if (!this._fetch) {
|
|
51
|
-
// Node ≤ 17 fallback
|
|
52
|
-
try { this._fetch = require('node-fetch'); } catch { /* user must supply */ }
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Normalises a domain or URL to bare hostname. */
|
|
57
|
-
static normalizeDomain(input) {
|
|
58
|
-
if (!input || typeof input !== 'string') return null;
|
|
59
|
-
let s = input.trim().toLowerCase();
|
|
60
|
-
s = s.replace(/^https?:\/\//, '').replace(/\/.*$/, '').replace(/^www\./, '');
|
|
61
|
-
return /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$/.test(s) ? s : null;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async _get(path) {
|
|
65
|
-
if (!this._fetch) throw new Error('Safe Mode requires fetch (Node 18+ or pass opts.fetch)');
|
|
66
|
-
const ctl = (typeof AbortController !== 'undefined') ? new AbortController() : null;
|
|
67
|
-
const timer = ctl ? setTimeout(() => ctl.abort(), this.timeoutMs) : null;
|
|
68
|
-
try {
|
|
69
|
-
const r = await this._fetch(this.apiBase + path, ctl ? { signal: ctl.signal } : {});
|
|
70
|
-
if (!r.ok) return null;
|
|
71
|
-
return await r.json();
|
|
72
|
-
} catch { return null; }
|
|
73
|
-
finally { if (timer) clearTimeout(timer); }
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Evaluate a domain and produce a verdict the agent can act on.
|
|
78
|
-
* @param {string} domain
|
|
79
|
-
* @param {object} [opts]
|
|
80
|
-
* @param {boolean} [opts.live=false] — force a live trust check (skip cache).
|
|
81
|
-
* @returns {Promise<{
|
|
82
|
-
* domain: string, level: 0|1|2|3,
|
|
83
|
-
* verdict: 'allow'|'restrict'|'deny',
|
|
84
|
-
* allow_execute: boolean, allow_read: boolean,
|
|
85
|
-
* score: number, score_label: string,
|
|
86
|
-
* reason: string, reasons: Array,
|
|
87
|
-
* trust: object|null, score_detail: object|null,
|
|
88
|
-
* compliance: object|null,
|
|
89
|
-
* evaluated_at: string,
|
|
90
|
-
* }>}
|
|
91
|
-
*/
|
|
92
|
-
async evaluate(domain, opts = {}) {
|
|
93
|
-
const d = WABSafeMode.normalizeDomain(domain);
|
|
94
|
-
if (!d) {
|
|
95
|
-
return this._verdict(domain, 0, 'deny', 0, 'unrated',
|
|
96
|
-
'invalid_domain', [{ code: 'invalid_domain', severity: 'deny' }],
|
|
97
|
-
null, null, null);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const cached = this._cache.get(d);
|
|
101
|
-
if (!opts.live && cached && (Date.now() - cached.at) < this.cacheTtl) return cached.value;
|
|
102
|
-
|
|
103
|
-
// Optionally trigger a live trust check first so compliance has fresh data.
|
|
104
|
-
let trust = null;
|
|
105
|
-
if (opts.live) {
|
|
106
|
-
trust = await this._get(`/api/discovery/trust/${encodeURIComponent(d)}`);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const [score, compliance] = await Promise.all([
|
|
110
|
-
this._get(`/api/discovery/score/${encodeURIComponent(d)}`),
|
|
111
|
-
this._get(`/api/discovery/compliance/${encodeURIComponent(d)}?policy=${this.policy}`),
|
|
112
|
-
]);
|
|
113
|
-
|
|
114
|
-
// Derive trust level
|
|
115
|
-
let level = 1;
|
|
116
|
-
let reasonCode = 'no_signal';
|
|
117
|
-
if (compliance) {
|
|
118
|
-
if (compliance.verdict === 'deny') { level = 0; reasonCode = 'compliance_deny'; }
|
|
119
|
-
else if (compliance.verdict === 'restrict') { level = 1; reasonCode = 'compliance_restrict'; }
|
|
120
|
-
else { // allow
|
|
121
|
-
const sigRate = score?.signature_valid_rate ?? compliance.signature_valid_rate ?? 0;
|
|
122
|
-
const sc = compliance.score ?? score?.score ?? 0;
|
|
123
|
-
if (sigRate > 0.5 && sc >= 60) { level = 3; reasonCode = 'trusted_full'; }
|
|
124
|
-
else { level = 2; reasonCode = 'trusted_limited'; }
|
|
125
|
-
}
|
|
126
|
-
} else {
|
|
127
|
-
level = 1;
|
|
128
|
-
reasonCode = 'no_compliance_record';
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const verdict = compliance?.verdict || (level === 0 ? 'deny' : level >= 2 ? 'allow' : 'restrict');
|
|
132
|
-
const value = this._verdict(
|
|
133
|
-
d, level, verdict,
|
|
134
|
-
compliance?.score ?? score?.score ?? 0,
|
|
135
|
-
compliance?.score_label ?? score?.label ?? 'unrated',
|
|
136
|
-
reasonCode,
|
|
137
|
-
compliance?.reasons || [],
|
|
138
|
-
trust, score, compliance,
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
this._cache.set(d, { at: Date.now(), value });
|
|
142
|
-
return value;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
_verdict(domain, level, verdict, score, label, reason, reasons, trust, scoreDetail, compliance) {
|
|
146
|
-
const allow_execute = level >= 2 && verdict === 'allow';
|
|
147
|
-
const allow_read = level >= 1 && verdict !== 'deny';
|
|
148
|
-
return {
|
|
149
|
-
domain,
|
|
150
|
-
level,
|
|
151
|
-
verdict,
|
|
152
|
-
allow_execute,
|
|
153
|
-
allow_read,
|
|
154
|
-
score,
|
|
155
|
-
score_label: label,
|
|
156
|
-
reason,
|
|
157
|
-
reasons,
|
|
158
|
-
trust,
|
|
159
|
-
score_detail: scoreDetail,
|
|
160
|
-
compliance,
|
|
161
|
-
policy: this.policy,
|
|
162
|
-
evaluated_at: new Date().toISOString(),
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Wrap an async action so it only runs if Safe Mode allows execute on the
|
|
168
|
-
* given domain. Throws WABSafeModeError otherwise.
|
|
169
|
-
*/
|
|
170
|
-
async guardExecute(domain, action) {
|
|
171
|
-
const v = await this.evaluate(domain);
|
|
172
|
-
if (!v.allow_execute) {
|
|
173
|
-
const err = new WABSafeModeError(
|
|
174
|
-
`Safe Mode blocked execute on ${v.domain} (level ${v.level}, verdict ${v.verdict}, ${v.reason})`,
|
|
175
|
-
v,
|
|
176
|
-
);
|
|
177
|
-
throw err;
|
|
178
|
-
}
|
|
179
|
-
return await action(v);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/** Read-only variant: throws only if level === 0. */
|
|
183
|
-
async guardRead(domain, action) {
|
|
184
|
-
const v = await this.evaluate(domain);
|
|
185
|
-
if (!v.allow_read) {
|
|
186
|
-
throw new WABSafeModeError(
|
|
187
|
-
`Safe Mode blocked read on ${v.domain} (level ${v.level}, verdict ${v.verdict})`,
|
|
188
|
-
v,
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
return await action(v);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/** Picks the highest-trust domain from a candidate list. */
|
|
195
|
-
async pickBest(domains) {
|
|
196
|
-
const evals = await Promise.all(
|
|
197
|
-
(domains || []).map((d) => this.evaluate(d).catch(() => null)),
|
|
198
|
-
);
|
|
199
|
-
const sorted = evals.filter(Boolean).sort((a, b) => {
|
|
200
|
-
if (b.level !== a.level) return b.level - a.level;
|
|
201
|
-
return (b.score || 0) - (a.score || 0);
|
|
202
|
-
});
|
|
203
|
-
return sorted[0] || null;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
clearCache(domain) {
|
|
207
|
-
if (domain) this._cache.delete(WABSafeMode.normalizeDomain(domain));
|
|
208
|
-
else this._cache.clear();
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
class WABSafeModeError extends Error {
|
|
213
|
-
constructor(message, verdict) {
|
|
214
|
-
super(message);
|
|
215
|
-
this.name = 'WABSafeModeError';
|
|
216
|
-
this.code = 'WAB_SAFE_MODE_BLOCKED';
|
|
217
|
-
this.verdict = verdict;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
module.exports = { WABSafeMode, WABSafeModeError, POLICIES };
|
|
1
|
+
/**
|
|
2
|
+
* WAB Safe Mode — Agent-side trust gate.
|
|
3
|
+
*
|
|
4
|
+
* Splits the web into Trusted (WAB + valid signature) vs Untrusted, and
|
|
5
|
+
* gives the agent a single function to ask before any action:
|
|
6
|
+
* await safeMode.evaluate(domain) → { level, verdict, allow_execute,
|
|
7
|
+
* allow_read, reason }
|
|
8
|
+
*
|
|
9
|
+
* Trust levels:
|
|
10
|
+
* 3 — DNS + Ed25519 signature valid + telemetry score ≥ 60 (full execute)
|
|
11
|
+
* 2 — DNS + valid wab.json (no signature OR no telemetry) (limited execute)
|
|
12
|
+
* 1 — Resolves but no _wab record / score below threshold (read-only)
|
|
13
|
+
* 0 — Compliance verdict = deny / suspicious (block)
|
|
14
|
+
*
|
|
15
|
+
* Usage (Node):
|
|
16
|
+
* const { WABSafeMode } = require('web-agent-bridge/sdk');
|
|
17
|
+
* const safe = new WABSafeMode({ apiBase: 'https://webagentbridge.com' });
|
|
18
|
+
* const v = await safe.evaluate('example.com');
|
|
19
|
+
* if (v.allow_execute) await agent.execute(...);
|
|
20
|
+
* else if (v.allow_read) await agent.readOnly(...);
|
|
21
|
+
* else throw new Error('Blocked by Safe Mode: ' + v.reason);
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
'use strict';
|
|
25
|
+
|
|
26
|
+
const DEFAULT_API = 'https://webagentbridge.com';
|
|
27
|
+
|
|
28
|
+
const POLICIES = {
|
|
29
|
+
strict: { require_dnssec: true, require_signature: true, min_score: 75 },
|
|
30
|
+
standard: { require_dnssec: false, require_signature: true, min_score: 60 },
|
|
31
|
+
permissive: { require_dnssec: false, require_signature: false, min_score: 40 },
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
class WABSafeMode {
|
|
35
|
+
/**
|
|
36
|
+
* @param {object} [opts]
|
|
37
|
+
* @param {string} [opts.apiBase='https://webagentbridge.com']
|
|
38
|
+
* @param {'strict'|'standard'|'permissive'} [opts.policy='standard']
|
|
39
|
+
* @param {number} [opts.cacheTtlMs=60000] — verdicts cached this long
|
|
40
|
+
* @param {number} [opts.timeoutMs=8000]
|
|
41
|
+
* @param {function} [opts.fetch] — fetch impl (defaults to global fetch)
|
|
42
|
+
*/
|
|
43
|
+
constructor(opts = {}) {
|
|
44
|
+
this.apiBase = (opts.apiBase || DEFAULT_API).replace(/\/+$/, '');
|
|
45
|
+
this.policy = POLICIES[opts.policy] ? opts.policy : 'standard';
|
|
46
|
+
this.cacheTtl = Number.isFinite(opts.cacheTtlMs) ? opts.cacheTtlMs : 60_000;
|
|
47
|
+
this.timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : 8_000;
|
|
48
|
+
this._fetch = opts.fetch || (typeof fetch !== 'undefined' ? fetch : null);
|
|
49
|
+
this._cache = new Map(); // domain → { at, value }
|
|
50
|
+
if (!this._fetch) {
|
|
51
|
+
// Node ≤ 17 fallback
|
|
52
|
+
try { this._fetch = require('node-fetch'); } catch { /* user must supply */ }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Normalises a domain or URL to bare hostname. */
|
|
57
|
+
static normalizeDomain(input) {
|
|
58
|
+
if (!input || typeof input !== 'string') return null;
|
|
59
|
+
let s = input.trim().toLowerCase();
|
|
60
|
+
s = s.replace(/^https?:\/\//, '').replace(/\/.*$/, '').replace(/^www\./, '');
|
|
61
|
+
return /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$/.test(s) ? s : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async _get(path) {
|
|
65
|
+
if (!this._fetch) throw new Error('Safe Mode requires fetch (Node 18+ or pass opts.fetch)');
|
|
66
|
+
const ctl = (typeof AbortController !== 'undefined') ? new AbortController() : null;
|
|
67
|
+
const timer = ctl ? setTimeout(() => ctl.abort(), this.timeoutMs) : null;
|
|
68
|
+
try {
|
|
69
|
+
const r = await this._fetch(this.apiBase + path, ctl ? { signal: ctl.signal } : {});
|
|
70
|
+
if (!r.ok) return null;
|
|
71
|
+
return await r.json();
|
|
72
|
+
} catch { return null; }
|
|
73
|
+
finally { if (timer) clearTimeout(timer); }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Evaluate a domain and produce a verdict the agent can act on.
|
|
78
|
+
* @param {string} domain
|
|
79
|
+
* @param {object} [opts]
|
|
80
|
+
* @param {boolean} [opts.live=false] — force a live trust check (skip cache).
|
|
81
|
+
* @returns {Promise<{
|
|
82
|
+
* domain: string, level: 0|1|2|3,
|
|
83
|
+
* verdict: 'allow'|'restrict'|'deny',
|
|
84
|
+
* allow_execute: boolean, allow_read: boolean,
|
|
85
|
+
* score: number, score_label: string,
|
|
86
|
+
* reason: string, reasons: Array,
|
|
87
|
+
* trust: object|null, score_detail: object|null,
|
|
88
|
+
* compliance: object|null,
|
|
89
|
+
* evaluated_at: string,
|
|
90
|
+
* }>}
|
|
91
|
+
*/
|
|
92
|
+
async evaluate(domain, opts = {}) {
|
|
93
|
+
const d = WABSafeMode.normalizeDomain(domain);
|
|
94
|
+
if (!d) {
|
|
95
|
+
return this._verdict(domain, 0, 'deny', 0, 'unrated',
|
|
96
|
+
'invalid_domain', [{ code: 'invalid_domain', severity: 'deny' }],
|
|
97
|
+
null, null, null);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const cached = this._cache.get(d);
|
|
101
|
+
if (!opts.live && cached && (Date.now() - cached.at) < this.cacheTtl) return cached.value;
|
|
102
|
+
|
|
103
|
+
// Optionally trigger a live trust check first so compliance has fresh data.
|
|
104
|
+
let trust = null;
|
|
105
|
+
if (opts.live) {
|
|
106
|
+
trust = await this._get(`/api/discovery/trust/${encodeURIComponent(d)}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const [score, compliance] = await Promise.all([
|
|
110
|
+
this._get(`/api/discovery/score/${encodeURIComponent(d)}`),
|
|
111
|
+
this._get(`/api/discovery/compliance/${encodeURIComponent(d)}?policy=${this.policy}`),
|
|
112
|
+
]);
|
|
113
|
+
|
|
114
|
+
// Derive trust level
|
|
115
|
+
let level = 1;
|
|
116
|
+
let reasonCode = 'no_signal';
|
|
117
|
+
if (compliance) {
|
|
118
|
+
if (compliance.verdict === 'deny') { level = 0; reasonCode = 'compliance_deny'; }
|
|
119
|
+
else if (compliance.verdict === 'restrict') { level = 1; reasonCode = 'compliance_restrict'; }
|
|
120
|
+
else { // allow
|
|
121
|
+
const sigRate = score?.signature_valid_rate ?? compliance.signature_valid_rate ?? 0;
|
|
122
|
+
const sc = compliance.score ?? score?.score ?? 0;
|
|
123
|
+
if (sigRate > 0.5 && sc >= 60) { level = 3; reasonCode = 'trusted_full'; }
|
|
124
|
+
else { level = 2; reasonCode = 'trusted_limited'; }
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
level = 1;
|
|
128
|
+
reasonCode = 'no_compliance_record';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const verdict = compliance?.verdict || (level === 0 ? 'deny' : level >= 2 ? 'allow' : 'restrict');
|
|
132
|
+
const value = this._verdict(
|
|
133
|
+
d, level, verdict,
|
|
134
|
+
compliance?.score ?? score?.score ?? 0,
|
|
135
|
+
compliance?.score_label ?? score?.label ?? 'unrated',
|
|
136
|
+
reasonCode,
|
|
137
|
+
compliance?.reasons || [],
|
|
138
|
+
trust, score, compliance,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
this._cache.set(d, { at: Date.now(), value });
|
|
142
|
+
return value;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
_verdict(domain, level, verdict, score, label, reason, reasons, trust, scoreDetail, compliance) {
|
|
146
|
+
const allow_execute = level >= 2 && verdict === 'allow';
|
|
147
|
+
const allow_read = level >= 1 && verdict !== 'deny';
|
|
148
|
+
return {
|
|
149
|
+
domain,
|
|
150
|
+
level,
|
|
151
|
+
verdict,
|
|
152
|
+
allow_execute,
|
|
153
|
+
allow_read,
|
|
154
|
+
score,
|
|
155
|
+
score_label: label,
|
|
156
|
+
reason,
|
|
157
|
+
reasons,
|
|
158
|
+
trust,
|
|
159
|
+
score_detail: scoreDetail,
|
|
160
|
+
compliance,
|
|
161
|
+
policy: this.policy,
|
|
162
|
+
evaluated_at: new Date().toISOString(),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Wrap an async action so it only runs if Safe Mode allows execute on the
|
|
168
|
+
* given domain. Throws WABSafeModeError otherwise.
|
|
169
|
+
*/
|
|
170
|
+
async guardExecute(domain, action) {
|
|
171
|
+
const v = await this.evaluate(domain);
|
|
172
|
+
if (!v.allow_execute) {
|
|
173
|
+
const err = new WABSafeModeError(
|
|
174
|
+
`Safe Mode blocked execute on ${v.domain} (level ${v.level}, verdict ${v.verdict}, ${v.reason})`,
|
|
175
|
+
v,
|
|
176
|
+
);
|
|
177
|
+
throw err;
|
|
178
|
+
}
|
|
179
|
+
return await action(v);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Read-only variant: throws only if level === 0. */
|
|
183
|
+
async guardRead(domain, action) {
|
|
184
|
+
const v = await this.evaluate(domain);
|
|
185
|
+
if (!v.allow_read) {
|
|
186
|
+
throw new WABSafeModeError(
|
|
187
|
+
`Safe Mode blocked read on ${v.domain} (level ${v.level}, verdict ${v.verdict})`,
|
|
188
|
+
v,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
return await action(v);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Picks the highest-trust domain from a candidate list. */
|
|
195
|
+
async pickBest(domains) {
|
|
196
|
+
const evals = await Promise.all(
|
|
197
|
+
(domains || []).map((d) => this.evaluate(d).catch(() => null)),
|
|
198
|
+
);
|
|
199
|
+
const sorted = evals.filter(Boolean).sort((a, b) => {
|
|
200
|
+
if (b.level !== a.level) return b.level - a.level;
|
|
201
|
+
return (b.score || 0) - (a.score || 0);
|
|
202
|
+
});
|
|
203
|
+
return sorted[0] || null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
clearCache(domain) {
|
|
207
|
+
if (domain) this._cache.delete(WABSafeMode.normalizeDomain(domain));
|
|
208
|
+
else this._cache.clear();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
class WABSafeModeError extends Error {
|
|
213
|
+
constructor(message, verdict) {
|
|
214
|
+
super(message);
|
|
215
|
+
this.name = 'WABSafeModeError';
|
|
216
|
+
this.code = 'WAB_SAFE_MODE_BLOCKED';
|
|
217
|
+
this.verdict = verdict;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
module.exports = { WABSafeMode, WABSafeModeError, POLICIES };
|