Back to skill

Security audit

Bridge

Security checks for vulnerabilities and agentic risk

Overview

This skill is not obvious malware, but its physical-task escrow and verification system is misleadingly weak for money- or reputation-affecting use.

Treat this as a prototype only. Do not rely on it for real payments, escrow, worker reputation, disputes, or physical-world proof unless authentication, authorization, real settlement, durable storage, privacy controls, and trusted proof verification are added.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (6)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
bridge/app.py:110
Finding

Unauthenticated Task and Escrow-State Control

Content
View full analysis
TaskResponse: """Worker accepts a task — status moves to 'accepted'.""" task = get_task(task_id) if task is None: raise HTTPException(status_code=404, detail=f"Task not found: {task_id}") if task.status != "posted": raise HTTPException(status_code=409, detail=f"Task cannot be accepted in state '{task.status}'") update_task_status(task_id, "accepted", worker_id=worker_id) return _task_to_response(get_task(task_id)) @app.post("/v1/tasks/{task_id}/verify", response_model=VerifyResponse) async def verify(task_id: str, request: VerifyRequest) -> VerifyResponse: """Submit proof and verify against criteria.""" task = get_task(task_id) if task is None: raise HTTPException(status_code=404, detail=f"Task not found: {task_id}") if task.status not in ("posted", "accepted"): raise HTTPException( status_code=409, detail=f"Task cannot be verified in state '{task.status}' — must be 'posted' or 'accepted'", ) update_task_status(task_id, "proof_submitted", worker_id=request.worker_id) all_passed, results = verify_all(task.verification_criteria, request.proofs) if all_passed: update_task_status(task_id, "completed", escrow_status="released") escrow_action = "release" record_worker_outcome(request.worker_id, "completed", earned=task.budget_usdc - task.fee_usdc) ``` ```python @app.post("/v1/tasks/{task_id}/dispute", response_model=DisputeResponse) async def file_dispute(task_id: str, request: DisputeRequest) -> DisputeResponse: """Dispute a task — freezes escrow.""" ...[truncated 2200 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
bridge/verifier.py:23
Finding

Physical-World Proof Can Be Forged Using Self-Asserted Data

Content
View full analysis
CriterionResult: """Verify worker was at the required location.""" params = criterion.params proof_data = proof.data required_lat = float(params.get("latitude", 0)) required_lon = float(params.get("longitude", 0)) radius_m = float(params.get("radius_m", 100)) submitted_lat = float(proof_data.get("latitude", 0)) submitted_lon = float(proof_data.get("longitude", 0)) distance = _haversine(required_lat, required_lon, submitted_lat, submitted_lon) passed = distance <= radius_m ``` ```python def verify_photo_proof(criterion: VerificationCriterion, proof: ProofItem) -> CriterionResult: """Verify required number of photos submitted with unique hashes.""" min_photos = int(criterion.params.get("min_photos", 1)) hashes = proof.data.get("photo_hashes", []) unique_hashes = set(hashes) passed = len(unique_hashes) >= min_photos ``` ```python def verify_timestamp_proof(criterion: VerificationCriterion, proof: ProofItem) -> CriterionResult: """Verify task completed within time limit.""" max_hours = float(criterion.params.get("max_hours", 24)) accepted = proof.data.get("accepted_at", "") completed = proof.data.get("completed_at", "") if not accepted or not completed: return CriterionResult(type="timestamp_proof", passed=False, detail="Missing timestamps") from datetime import datetime try: t_accepted = datetime.fromisoformat(accepted.replace("Z", "+00:00")) t_completed = datetime.fromisoformat(completed.replace("Z", "+00:00")) elapsed_hours = (t_completed - t_accepted).total_seconds() / 3600 passed = elapsed_hours <= max_hours ``` ```python def ver ...[truncated 2641 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
bridge/verifier.py:129
Finding

Empty Verification Criteria Cause Fail-Open Escrow Release

Content
View full analysis
tuple[bool, list[CriterionResult]]: """Verify all criteria against submitted proofs. Returns (all_passed, results). """ proof_by_type = {p.type: p for p in proofs} results: list[CriterionResult] = [] for criterion in criteria: proof = proof_by_type.get(criterion.type) if proof is None: results.append(CriterionResult( type=criterion.type, passed=False, detail=f"No proof submitted for {criterion.type}", )) continue verifier = _VERIFIERS.get(criterion.type) if verifier is None: results.append(CriterionResult( type=criterion.type, passed=False, detail=f"Unknown proof type: {criterion.type}", )) continue results.append(verifier(criterion, proof)) all_passed = all(r.passed for r in results) return all_passed, results ``` ```python all_passed, results = verify_all(task.verification ...[truncated 1376 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
bridge/state.py:115
Finding

Replayable Milestones Permit Duplicate and Excessive Release

Content
View full analysis
tuple[BridgeTask | None, Decimal]: """Mark a milestone as verified. Returns (task, budget_released).""" task = _tasks.get(task_id) if task is None or not task.milestones: return None, Decimal("0") if milestone_index < 0 or milestone_index >= len(task.milestones): return None, Decimal("0") task.milestones_completed += 1 ms = task.milestones[milestone_index] pct = Decimal(ms.budget_pct if hasattr(ms, 'budget_pct') else ms.get("budget_pct", 0)) released = (task.budget_usdc * pct / Decimal("100")).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP) task.budget_released_usdc += released if task.milestones_completed >= task.milestones_total: task.status = "completed" task.escrow_status = "released" return task, released ``` ```python @app.post("/v1/tasks/{task_id}/verify-milestone", response_model=VerifyResponse) async def verify_milestone(task_id: str, request: VerifyMilestoneRequest) -> VerifyResponse: task = get_task(task_id) if task is None: raise HTTPException(status_code=404, detail=f"Task not found: {task_id}") if not task.milestones: raise HTTPException(status_code=400, detail="Task has no milestones") if task.status in ("completed", "refunded", "disputed"): raise HTTPExceptio ...[truncated 2412 chars]
Remediation
View remediation

other

Error
Location
SKILL.md:37
Finding

Documentation Misrepresents In-Memory Status as Financial Escrow

Content
View full analysis
= task.milestones_total: task.status = "completed" task.escrow_status = "released" ``` ### Technical Analysis The implementation contains no wallet integration, smart contract, payment provider, custody mechanism, durable ledger, blockchain transaction, balance reservation, or transfer operation. Escrow status and released amounts are fields in process memory. The documentation nevertheless states that escrow is automatically locked, held, released, frozen, and refunded. A service restart discards the entire `_tasks` dictionary. The reviewed code also contains no deadline-expiration process implementing the documented refund behavior. The API therefore simulates escrow labels rather than securing or settling actual funds. ### Attack Path 1. A requester creates a task and receives an API response showing `escrow_status: locked`. 2. A worker interprets this response and the documentation as confirmation that funds are secured. 3. The worker performs the physical task. 4. The service may report release without transferring funds, or restart and lose the task and accounting state. 5. Neither party has a durable settlement record or actual custody guarantee. ### Impact Ass ...[truncated 286 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:4
Finding

Runtime Dependencies Are Unpinned and Lack Integrity Verification

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (11)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill declares runtime/install metadata and appears capable of accessing environment data, but it does not declare any explicit tool scope or permissions boundaries. In an agent setting, missing scope declarations can cause the host to over-grant capabilities or make operators unaware that code execution may access local secrets or configuration.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill handles highly sensitive real-world verification data including GPS coordinates, photos, timestamps, and signatures, but the description does not warn users about privacy, consent, retention, or misuse risks. This can lead to unsafe collection or disclosure of personal/location data without informed operator review.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 20)May include surrounding context.

Create a task with verification criteria

bash
curl -s -X POST http://localhost:8015/v1/tasks \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Pick up package at 123 Main St, photograph it",

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 63)May include surrounding context.

Create a task with verification criteria

bash
curl -s -X POST http://localhost:8015/v1/tasks \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Pick up package at 123 Main St, photograph it",

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The documentation states that escrow is automatically locked and later released, frozen, or refunded based on platform logic, but it does not present this as an operational or financial risk warning. Users may trigger irreversible or hard-to-reverse fund state changes without understanding when money becomes unavailable or automatically disbursed.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

This endpoint transmits sensitive proof material, including precise GPS data and photo hashes, to the verification service. Even though the example uses localhost, the skill normalizes submission of real-world verification data without any mention of transport security, authentication, minimization, or retention controls, which increases privacy and integrity risk if deployed remotely.

Content

Scanner excerpt · SKILL.md (reported line 37)May include surrounding context.

Submit proof and verify

bash
curl -s -X POST http://localhost:8015/v1/tasks/TASK_ID/verify \
  -H "Content-Type: application/json" \
  -d '{
    "worker_id": "worker-1",

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 53)May include surrounding context.

Dispute a task

bash
curl -s -X POST http://localhost:8015/v1/tasks/TASK_ID/dispute \
  -H "Content-Type: application/json" \
  -d '{"reason": "GPS proof appears faked"}' | jq

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
87% confidence
Finding

The skill explicitly describes autonomous escrow state changes where verification becomes impossible during dispute and payments are released automatically when criteria pass. In a financial workflow tied to proofs that may be spoofed or incomplete, autonomous decision-making can cause wrongful payout denial or release without sufficient human review.

Content

Scanner excerpt · SKILL.md (reported line 58)May include surrounding context.

-d '{"reason": "GPS proof appears faked"}' | jq

text

Freezes escrow. No verification possible while disputed.

## Milestone-based tasks (partial payment)

External Transmission

Medium
Category
Data Exfiltration
Confidence
79% confidence
Finding

Milestone verification transmits proof data that can directly trigger partial escrow release, making the submission both privacy-sensitive and financially consequential. Without documented safeguards such as authentication, anti-replay protections, and secure transport, this pattern can enable unauthorized or manipulated verification submissions in real deployments.

Content

Scanner excerpt · SKILL.md (reported line 78)May include surrounding context.

Verify milestones individually — each releases its share of the budget:

bash
curl -s -X POST http://localhost:8015/v1/tasks/TASK_ID/verify-milestone \
  -H "Content-Type: application/json" \
  -d '{"worker_id": "w", "milestone_index": 0, "proofs": [{"type": "gps_proof", "data": {"latitude": 37.77, "longitude": -122.42}}]}' | jq

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The function named and documented as verifying a cryptographic signature only checks that the signature field is non-empty. In an escrow and payment-release system, this creates a direct integrity failure: an attacker can submit arbitrary text as a 'signature' and satisfy the criterion, defeating any trust assumption that a real signer approved the work.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

This code verifies submitted GPS location data, which is sensitive user data, but the file contains no confirmation prompt, user-facing warning, or explicit disclosure about handling location information. For code files, safety-relevant handling of sensitive data should include some visible disclosure unless clearly covered elsewhere.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.