XPR Trustless Agents - Security Audit Report
Date: 2026-02-08
Auditor: 8-agent parallel audit swarm (Claude Opus 4.6)
Scope: Full stack - 4 smart contracts, SDK, indexer, OpenClaw plugin, frontend, deployment/infra, cross-component consistency, test coverage
8 specialized audit agents examined every source file across the entire XPR Agents stack. The audit identified 3 CRITICAL , 21 HIGH , 38 MEDIUM , and 40+ LOW severity findings. The most urgent issues are missing re-initialization guards on two contracts (allowing owner takeover), schema mismatches between the SDK/indexer and contracts (causing silent data corruption), and a missing indexer handler that permanently corrupts indexed state.
The codebase demonstrates strong fundamentals: parameterized SQL queries, proper auth checks on most actions, token transfer safety patterns (checks-effects-interactions), and no hardcoded secrets. The issues found are primarily gaps rather than flaws in existing logic.
Smart Contracts
SDK
Indexer
OpenClaw Plugin
Frontend
Cross-Component Consistency
Deployment & Infrastructure
Test Coverage Gaps
Recommended Fix Priority
ID
Contract
Issue
Location
SC-C01
agentfeed
init() has no re-initialization guard - config can be overwritten by anyone with contract authority
agentfeed.contract.ts:532-548
SC-C02
agentescrow
init() has no re-initialization guard - same issue
agentescrow.contract.ts:264-283
SC-C03
agentescrow
setConfig() cannot update core_contract, feed_contract, acceptance_timeout, min_arbitrator_stake, or arb_unstake_delay - contract becomes unmanageable if core migrates
agentescrow.contract.ts:285-311
SC-C01 / SC-C02 Details: Both agentcore and agentvalid correctly check existingConfig.owner == EMPTY_NAME before allowing initialization. The agentfeed and agentescrow contracts skip this check, meaning init() can be called again to overwrite the owner field.
Fix: Add const existingConfig = this.configSingleton.get(); check(existingConfig.owner == EMPTY_NAME, "Contract already initialized."); to both contracts.
ID
Contract
Issue
Location
SC-H01
agentfeed
FeedbackRateLimit secondary index uses XOR (reviewer.N ^ agent.N) - guaranteed collisions can cause CPU exhaustion DoS
agentfeed.contract.ts:329-331
SC-H02
agentfeed/agentvalid/agentescrow
Permissionless cleanup actions can delete live data, desync scores when followed by recalculate()
agentfeed.contract.ts:1107-1128, agentvalid.contract.ts:994-1015, agentescrow.contract.ts:1058-1088
SC-H03
agentescrow
No dispute timeout mechanism FIXED - resolvetmout action allows owner to resolve disputes after 14 days
agentescrow.contract.ts:830+
SC-H04
agentfeed
calcaggtrust overwrites native avg_score in agentscores table - next submit() overwrites it back, causing score oscillation
agentfeed.contract.ts:1517-1573
ID
Contract
Issue
Location
SC-M01
agentfeed
init() does not validate core_contract is a real account
agentfeed.contract.ts:532-548
SC-M02
agentfeed
submit() does not call updateDirectionalTrust() but submitctx() and submitwpay() do
agentfeed.contract.ts:630-697
SC-M03
agentescrow
No validation that symbol parameter matches XPR in createJob()
agentescrow.contract.ts:324-388
SC-M04
agentescrow
Most actions missing pause check (createjob, addmilestone, acceptjob, startjob, submitmile, deliver, dispute)
Multiple locations
SC-M05
agentescrow
Arbitrator can deactivate while assigned to active disputes, triggering owner fallback (0% fee dodge)
agentescrow.contract.ts:937-944
SC-M06
agentcore
getSystemStake() integer truncation at boundary (9999 / 10000 = 0)
agentcore.contract.ts:289-301
SC-M07
agentfeed
amount_paid is self-reported and unverified in submit()
agentfeed.contract.ts:630-697
SC-M08
agentcore
hashString() (DJB2) collisions for plugin name secondary index
agentcore.contract.ts:144-150
ID
Contract
Issue
SC-L01
agentvalid
cleanValidations can delete validations with pending unfunded challenges
SC-L02
agentfeed
cleanFeedback deletes records without updating agentscores
SC-L03
agentescrow
Milestone order not validated for uniqueness
SC-L04
agentescrow
No maximum deadline enforcement on createJob()
SC-L05
agentvalid
resolve() resets challenged = false, enabling cumulative slashing
SC-L06
agentescrow
arb_unstake_delay not configurable via init() FIXED - positional arg now passes 604800 (7 days)
ID
Contract
Issue
SC-I01
All
Rounding dust from integer division in fee calculations
SC-I02
agentcore
transfer action name collision with token notify handler
SC-I03
All
Singleton config defaults create valid-looking config with owner == EMPTY_NAME
SC-I04
agentfeed
Context embedded in tags field causes false colon detection
SC-I05
agentvalid
Accuracy dilutable by volume padding
ID
Issue
Location
SDK-H01
ValidatorRaw missing pending_challenges field - positional data corruption for registered_at and active
types.ts:191-213
SDK-H02
Challenge field order mismatch / missing funded_at - every field after stake reads wrong value
ValidationRegistry.ts:155-176
SDK-H03
Arbitrator missing active_disputes field - active filter permanently broken
EscrowRegistry.ts:317-343
SDK-H04
No validation on amount string in registerWithFee, claimWithFee, and fee methods
Multiple locations
ID
Issue
Location
SDK-M01
listFeedbackForAgent secondary index queries fetch globally, not scoped to account
FeedbackRegistry.ts:52-105
SDK-M02
No validation on score (1-5 range) before transaction
FeedbackRegistry.ts:189-215
SDK-M03
No validation on confidence (0-100 range) before transaction
ValidationRegistry.ts:307-332
SDK-M04
parseInt() without radix or NaN guard throughout all parse methods
Multiple files
SDK-M05
Trust score longevity uses client Date.now() instead of chain time
utils.ts:50-52
SDK-M06
safeJsonParse<T> does type assertion not runtime validation (prototype pollution risk)
utils.ts:129-135
SDK-M07
Account names never validated on write operations despite isValidAccountName() existing
Multiple write methods
SDK-M08
parseXpr uses floating-point math, losing precision (0.7 * 10000 = 6999.999...)
utils.ts:171-175
ID
Issue
SDK-L01
DISPUTE_RESOLUTIONS array index access without bounds check
SDK-L02
console.warn in transferOwnership leaks implementation details
SDK-L03
listPlugins hardcoded limit of 1000 with no pagination
SDK-L04
Cleanup methods have no maxAge/maxDelete validation
SDK-L05
MilestoneRaw.order typed as number inconsistent with other raw fields
SDK-L06
@proton/js caret version range allows untested minor versions
ID
Issue
Location
IDX-H01
No SSRF protection on webhook URLs FIXED - isValidWebhookUrl() validates at registration (blocks private IPs, localhost, metadata endpoints, non-HTTPS schemes)
routes.ts:395-433
IDX-H02
SQL sort column interpolation pattern - safe by accident, fragile to future changes
routes.ts:28-29
ID
Issue
Location
IDX-M01
Webhook admin token comparison uses !== (timing attack susceptible)
routes.ts:366-367
IDX-M02
Unbounded webhook_deliveries table growth (no cleanup)
dispatcher.ts:173-186
IDX-M03
Unbounded events table growth (no TTL or rotation)
schema.ts:136-144
IDX-M04
No rate limiting on any endpoint
index.ts
IDX-M05
Synthetic ID drift (MAX(id) + 1) on missed blocks
All handlers
ID
Issue
IDX-L01
CORS fully open (Access-Control-Allow-Origin: *)
IDX-L02
Unauthenticated POST /admin/sync-kyc FIXED - requireAdminAuth guard added
IDX-L03
Webhook tokens stored in plaintext in SQLite
IDX-L04
No validation of Hyperion stream data schema
IDX-L05
Docker container runs as root
IDX-L06
No WebSocket origin/auth validation on reconnect
IDX-L07
Error handler logs full error objects (may leak internals)
IDX-L08
Unbounded concurrent webhook deliveries (no concurrency limit)
All SQL queries properly parameterized
Webhook auto-disable after 50 failures
Query result limits enforced (500 max)
10-second webhook delivery timeout with AbortSignal
4xx errors not retried
Graceful shutdown (SIGINT/SIGTERM)
Irreversible-only stream processing
Exponential backoff on reconnection
ID
Issue
Location
OC-H01
Contract names from config not validated - config compromise redirects all financial operations
index.ts:38-51
OC-H02
indexerUrl not validated - config compromise enables SSRF and data exfiltration
indexer.ts:10-17
ID
Issue
Location
OC-M01
3 agent write tools missing confirmation gate (update_agent, set_status, manage_plugin)
agent.ts:191-292
OC-M02
dispute_feedback and recalculate_score missing confirmation gate
feedback.ts:161-201
OC-M03
register_validator, submit_validation, challenge_validation missing confirmation gate
validation.ts:140-232
OC-M04
Default maxTransferAmount is 10,000 XPR - very generous for autonomous agent
index.ts:53
OC-M05
validateUrl function exists but is never called on any URI/endpoint field
agent.ts, feedback.ts, validation.ts, escrow.ts
OC-M06
No session null guard on 18+ write tools - cryptic errors in read-only mode
All write tool files
OC-M07
Docker ports bound to 0.0.0.0 FIXED - Bound to 127.0.0.1
docker-compose.yml:16,39
ID
Issue
OC-L01
accept_job, deliver_job, submit_milestone missing confirmation gate
OC-L02
Falsy fee_amount (0) skips validation guard
OC-L03
Floating-point precision in amount conversion FIXED - xprToSmallestUnits() uses string-split integer math
OC-L04
No string length limits on any field
OC-L05
Account name regex allows leading/trailing dots
OC-L06
Private key in memory with no zeroization (inherent JS limitation)
OC-L07
Internal URL leaked in health check error response
OC-L08
Raw error messages propagated to tool output
OC-L09
Private key passed as Docker environment variable (visible via docker inspect)
OC-L10
setup.sh appends tokens without dedup on re-run
ID
Issue
Location
FE-M01
Unprotected JSON.parse on chain-sourced capabilities field - crashes agent list
registry.ts:84,112
FE-M02
Missing security headers (CSP, X-Frame-Options, HSTS, etc.)
next.config.js
FE-M03
Agent endpoint URL rendered without protocol validation - future XSS risk if made clickable
[id].tsx:119-121
FE-M04
Agent description injected into <meta> tag without sanitization
[id].tsx:43
ID
Issue
FE-L01
Raw error messages from RPC surfaced to UI
FE-L02
No client-side score range validation (1-5)
FE-L03
Tag field allows comma injection
FE-L04
Wallet session not shared via React Context (stale state across components)
FE-L05
Staking amount parsed as float without NaN/negative checks
FE-L06
No rate limiting on form submissions
FE-L07
Inconsistent rel="noopener noreferrer" on external links
Zero dangerouslySetInnerHTML usage
No private key handling in frontend code
No localStorage/sessionStorage for sensitive data
No eval or Function constructors
No API routes (all data from RPC)
CSRF inherently mitigated by wallet signing
Self-review prevention in FeedbackForm
Environment variables use NEXT_PUBLIC_ prefix correctly
6. Cross-Component Consistency
Entity
Missing Field
SDK Impact
Indexer Impact
Validator
pending_challenges
Fields after accuracy_score read wrong values
Column missing, can't track
Challenge
funded_at
Missing critical timestamp for dispute period
Column missing
Arbitrator
active_disputes
active filter permanently broken
Column missing
Missing Indexer Handlers (HIGH) - ALL FIXED
Action
Contract
Impact
expirefunded
agentvalid
Funded challenge expiry permanently corrupts indexer FIXED - Handler exists at validation.ts:321-349
resolvetmout
agentescrow
Timeout resolution not indexed FIXED - Handler added to escrow.ts
Missing Indexer Handlers (LOW - cleanup actions)
Actions
Impact
cleanjobs, cleandisps (agentescrow)
Indexer retains records chain has pruned
cleanvals, cleanchals (agentvalid)
Same
cleanfback, cleandisps (agentfeed)
Same
Severity
Issue
MEDIUM
SDK trust score hardcodes 'agentfeed' instead of reading from config
MEDIUM
OpenClaw protocol description suggests invalid values (a2a, mcp, rest) - contract requires http, https, grpc, websocket, mqtt, wss
MEDIUM
Indexer feedback reviewer_kyc_level always 0 (contract reads it internally, not in action data)
MEDIUM
Indexer lacks time-based score decay that contract applies
LOW
Indexer agents.stake column never populated (agents use system staking)
LOW
Indexer agents.trust_score column always 0 (never computed)
LOW
Milestone column naming: contract order vs indexer milestone_order
7. Deployment & Infrastructure
ID
Issue
Location
INF-H01
Wildcard CORS on indexer (Access-Control-Allow-Origin: *)
index.ts:36
INF-H02
Unauthenticated POST /admin/sync-kyc FIXED - requireAdminAuth guard added
routes.ts:365-366
INF-H03
No rate limiting on any API endpoint
index.ts
ID
Issue
Location
INF-M01
Deploy script has no mainnet guard / chain verification
deploy-testnet.sh:13
INF-M02
Docker ports exposed to all interfaces FIXED - Bound to 127.0.0.1 in both docker-compose files
docker-compose.yml
INF-M03
Docker container runs as root
indexer/Dockerfile
INF-M04
setup.sh token duplication on re-run
setup.sh:52-61
INF-M05
Webhook tokens stored in plaintext in SQLite
schema.ts:286
INF-M06
No backup/recovery mechanism for indexer database
docker-compose.yml:46
ID
Issue
INF-L01
OpenClaw gateway image not pinned (latest tag)
INF-L02
All dependencies use caret ranges + package-lock.json gitignored
INF-L03
next-env.d.ts not in .gitignore
No hardcoded secrets anywhere in codebase
.env files properly gitignored
Cryptographically secure token generation (openssl rand -hex 32)
Environment variable validation in setup.sh
Multi-stage Docker build
set -e / set -euo pipefail in shell scripts
Sensitive database files gitignored
Estimated Coverage by Component
Component
Tests
Coverage
Status
agentcore contract
67
~80%
onTransfer, ownership, claim deposits tested
agentfeed contract
44
~70%
Recalculation, rate limiting, score calculation, cleanup tested
agentvalid contract
37
~75%
Challenge resolution, slashing, accuracy tracking tested
agentescrow contract
45
~75%
Timeouts, milestones, arbitrator-less fallback tested
SDK
183
~95%
safeParseInt, parseXpr edge cases added
OpenClaw plugin
52
~65%
maxTransferAmount enforcement, confirmation gate tested
Indexer
28
~60%
Handler tests for all 4 contracts, schema, transfers, event logging
Frontend
0
0%
Zero test coverage
Integration (test-actions.sh)
~70
~70%
Missing: timeouts, context feedback, funded challenge timeout
Total
456
Resolved Test Gaps (Phase 4 - completed 2026-02-08)
All CRITICAL and HIGH test gaps from the original audit have been resolved:
Indexer: 0% coverage - 28 handler tests added (schema, agent, feedback, validation, escrow, transfers, events)
Contract onTransfer handlers - 15 agentcore onTransfer tests (claim deposits, malformed memos, excess refunds)
Challenge resolution + slashing - 9 agentvalid tests (slash on upheld, stake forfeiture, accuracy tracking, dispute period)
Job timeout / acceptance timeout - 7 agentescrow tests (acceptance timeout, deadline timeout, milestone approval)
Paginated recalculation - 10 agentfeed tests (single/multi-batch, offset validation, cancellation, expiry, blocking)
KYC-weighted scoring - 5 agentfeed tests (avg_score calculation, perfect/minimum scores, dispute subtraction)
OpenClaw maxTransferAmount enforcement - 6 OpenClaw tests (register, feedback, stake, create job, fund job, within-limit)
Frontend: 0% coverage - React components untested
Integration test-actions.sh - Missing timeout and context feedback paths
Indexer API routes - REST endpoint response format untested
Indexer webhook dispatcher - Retry logic and auto-disable untested
9. Recommended Fix Priority
Phase 1 - Blockers (fix before any testnet deployment) - ALL DONE
#
Fix
Effort
Status
1
Add re-init guards to agentfeed.init() and agentescrow.init()
2 lines
Done
2
Fix SDK schema: add pending_challenges, funded_at, active_disputes
~30 lines
Done
3
Add expirefunded handler to indexer
~20 lines
Done (already existed)
4
Fix OpenClaw protocol description
1 line
Done
Phase 2 - Security hardening (fix before public testnet) - ALL DONE
#
Fix
Effort
Status
5
Add CORS allowlist to indexer
5 lines
Done
6
Add auth to /admin/sync-kyc or remove it
3 lines
Done - requireAdminAuth guard
7
Add SSRF protection for webhook URLs
20 lines
Done - isValidWebhookUrl()
8
Bind Docker ports to 127.0.0.1
2 lines
Done - both compose files
9
Add missing params to agentescrow.setConfig()
15 lines
Done - setconfig updated
10
Add dispute timeout mechanism to agentescrow
50 lines
Done - resolvetmout action
11
Add missing indexer columns (pending_challenges, active_disputes, funded_at)
10 lines
Done
12
Add session null guard to OpenClaw write tools
20 lines
Done
13
Wire validateUrl to URI/endpoint fields
10 lines
Done
#
Fix
Effort
Status
14
Add rate limiting to indexer API (express-rate-limit)
15 lines
Done
15
Add security headers to frontend (CSP, X-Frame-Options)
20 lines
16
Run Docker as non-root user
5 lines
Done
17
Add JSON.parse try-catch in frontend registry.ts
10 lines
18
Fix parseXpr floating-point precision
10 lines
Done - xprToSmallestUnits() integer math
19
Add parseInt NaN guards throughout SDK
30 lines
Done - safeParseInt()
20
Pin Docker images and commit lockfiles
5 lines
21
Add pause checks to remaining agentescrow actions
10 lines
22
Block arbitrator deactivation with active disputes
2 lines
23
Lower default maxTransferAmount to 100 XPR
1 line
Phase 4 - Test coverage (COMPLETED 2026-02-08)
#
Area
Priority
Status
24
Indexer test suite (handlers, API, webhooks)
CRITICAL
Done (28 tests)
25
Contract onTransfer handler tests
CRITICAL
Done (15 tests)
26
Challenge resolution + slashing tests
CRITICAL
Done (9 tests)
27
Job timeout / acceptance timeout tests
CRITICAL
Done (7 tests)
28
Paginated recalculation tests
HIGH
Done (10 tests)
29
KYC-weighted scoring tests
HIGH
Done (5 tests)
30
OpenClaw maxTransferAmount enforcement tests
HIGH
Done (6 tests)
Phase 5 - E2E audit swarm fixes (COMPLETED 2026-02-08)
#
Fix
Severity
Status
31
Add platform_fee <= 1000 upper bound to agentescrow.init()
CRITICAL
Done
32
Fix agentescrow.init() positional arg coercion (false → 0 for arb_unstake_delay)
HIGH
Done
33
Add resolvetmout indexer handler (job state 8, dispute resolution, active_disputes decrement)
HIGH
Done
34
Add resolveTimeout() SDK method on EscrowRegistry
HIGH
Done
35
Add xpr_resolve_timeout OpenClaw tool with confirmation gate
HIGH
Done
36
Add confirmation gate to xpr_accept_job
HIGH
Done
37
Fix floating-point amount conversion across all indexer handlers (integer math)
MEDIUM
Done
38
Fix floating-point amount conversion in all OpenClaw tools (xprToSmallestUnits())
MEDIUM
Done
39
Fix job state 3 description: ACTIVE → INPROGRESS in OpenClaw
MEDIUM
Done
40
Fix xpr_dispute_feedback description (reviewer can also dispute)
MEDIUM
Done
41
Fix validator stake memo matching (stake or stake:* prefix)
MEDIUM
Done
42
Update OpenClaw test counts (43→44 tools, 13→14 escrow)
TEST
Done
This audit was conducted by 8 specialized agents running in parallel:
Smart Contract Agent - Read all 4 contracts line-by-line checking for auth flaws, reentrancy, integer overflow, state machine violations, economic exploits
SDK Agent - Audited type safety, cross-contract schema consistency, input validation, key exposure
Indexer Agent - Checked for SQL injection, webhook SSRF, DoS, data integrity, WebSocket security
OpenClaw Plugin Agent - Audited confirmation gates, amount limits, input validation, starter kit security
Frontend Agent - Checked for XSS, CSRF, SSR issues, wallet state management, URL rendering
Cross-Component Agent - Verified schema consistency, state machine agreement, fee handling, memo parsing across all 6 components
Test Coverage Agent - Analyzed every test file against source code to find gaps
Deployment/Infra Agent - Audited scripts, Docker configs, secret handling, dependency supply chain
Each agent read every relevant source file in its domain and cross-referenced findings against the contract source of truth.