Back to skill

Security audit

Arc Security - Agent Trust Protocol

Security checks for vulnerabilities and agentic risk

Overview

This skill is purpose-aligned with blockchain-based skill payments and trust checks, but it handles wallet keys, signs transactions, and installs remote skill packages with insufficient safeguards.

Review this carefully before installing. Use only a dedicated low-value wallet, verify every RPC and contract address, avoid non-Arc CCTP flows until they are fully implemented, and do not trust downloaded skill packages unless their publisher and exact bytes are independently verified.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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 (5)

T03 · Remote Payload Retrieval and Execution

Error
Location
x402_client.py:150
Finding
Unverified Remote Skill Packages Are Downloaded and Installed<![CDATA[ ## Vulnerability Details **File Location**: `x402_client.py:55-56`, `x402_client.py:150-166` **Vulnerability Type**: Unverified remote payload installation **Risk Level**: High ### Vulnerable Code ```python url = f"{self.server_url}/skills/{skill_id}" response = requests.get(url) ``` ```python response = requests.get(url, headers=headers) if response.status_code == 200: content_type = response.headers.get('Content-Type', '') if 'application/zip' in content_type: filename = f"{skill_id}.zip" with open(filename, 'wb') as f: f.write(response.content) # Unzip and install (simulated) import zipfile install_dir = os.path.join(os.getcwd(), skill_id) with zipfile.ZipFile(filename, 'r') as zip_ref: zip_ref.extractall(install_dir) return { 'success': True, 'txHash': tx_hash, 'content': f"Saved to {install_dir}" } ``` ### Technical Analysis The client accepts package bytes from the configurable `X402_SERVER_URL` and installs the returned archive without validating an immutable package digest, publisher signature, trusted manifest, archive size, or expected file list. This creates a remote payload channel whose effective contents can change after the Skill itself has been reviewed. The payment server, its DNS or TLS infrastructure, or an operator controlling `X402_SERVER_URL` can substitute an arbitrary ZIP archive for the requested Skill. Although the downloaded files are not directly executed by this function, they are installed as a Skill and may subsequently be loaded or executed by the agent. The trust check only evaluates an on-chain skill identifier; it does not cryptographically bind the downloaded bytes to the audited artifact. The use of `extractall()` also lacks explicit validation of archive member paths and types. The primary issue is unverified remote installation, but archive entries should additionally be c ...[truncated 1061 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require HTTPS and reject non-HTTPS payment server URLs. 2. Restrict package servers to an explicit, administrator-controlled allowlist. 3. Bind each on-chain Skill record to an immutable package digest and publisher identity. 4. Verify a cryptographic publisher signature and expected digest before writing or extracting the package. 5. Download into a newly created, permission-restricted temporary directory. 6. Enforce maximum response and uncompressed archive sizes. 7. Validate every archive member before extraction: - Reject absolute paths. - Reject paths containing traversal components. - Reject entries resolving outside the installation root. - Reject symbolic links, device files, and other unexpected file types. 8. Validate the package manifest and expected file inventory. 9. Perform atomic installation only after all validation succeeds. 10. Keep the downloaded package quarantined until it passes malware and policy scanning. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
x402_client.py:69
Finding
Remote Server Can Override the Payment Amount After User Confirmation<![CDATA[ ## Vulnerability Details **File Location**: `main.py:83-89`, `x402_client.py:69-94`, `x402_client.py:123` **Vulnerability Type**: Transaction amount validation failure **Risk Level**: Critical ### Vulnerable Code ```python print(f"\nProceeding with payment from {selected_chain}...") response = input("Confirm payment of 0.1 USDC? (yes/no): ") if response.lower() != 'yes': print("Cancelled.") return result = x402_client.request_skill(skill_id, chain=selected_chain) ``` ```python # Step 2: Parse payment details payment_data = response.json().get('payment', {}) amount_wei = int(payment_data.get('amount', 100000)) # 0.1 USDC memo = payment_data.get('memo', f"skill:{skill_id}") print(f"Payment required: {amount_wei / 1e6} USDC") tx_hash = None # Step 3: Execute payment if chain == 'arc-testnet': # Direct payment on Arc print("Paying directly on Arc Testnet...") # 1. Approve USDC to Arc contract usdc_address = self.cctp_client.USDC_ADDRESSES['arc-testnet'] usdc_abi = [{"inputs":[{"name":"spender","type":"address"},{"name":"amount","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"type":"function"}] usdc_contract = self.arc_contract.w3.eth.contract(address=Web3.to_checksum_address(usdc_address), abi=usdc_abi) approve_tx = usdc_contract.functions.approve( Web3.to_checksum_address(self.arc_contract.contract_address), amount_wei ).build_transaction({ ``` ```python result = self.cctp_client.bond_skill( skill_id, amount_wei / 1e6, chain ) # Reuse bond_skill for burn ``` ### Technical Analysis The user is asked to confirm a fixed payment of 0.1 USDC before the x402 payment terms are retrieved. After confirmation, the remote server supplies the actual `amount`, which is converted to an integer and used without checking that it equals the confirmed fee. There is no upper bound, no comparison with a locally trusted fee, and no second confirmation showing the exact transa ...[truncated 1451 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define the expected usage fee locally or read it from a trusted on-chain contract. 2. Require `amount_wei` to equal the expected fee exactly; reject any mismatch. 3. Parse the amount as a bounded integer and reject: - Negative values. - Zero. - Fractional or malformed values. - Values above a strict maximum. 4. Retrieve and validate payment terms before asking the user for confirmation. 5. Display the exact amount, token contract, source chain, recipient contract, destination domain, and estimated gas in the confirmation prompt. 6. Never execute a different amount after confirmation. 7. Cryptographically authenticate server payment terms and bind them to the requested Skill and a short-lived nonce. 8. Simulate or estimate the transaction before signing it. 9. Add automated tests proving that a server-supplied amount other than 100,000 base units is rejected. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
cctp_client.py:121
Finding
Incomplete CCTP Flow Can Burn or Strand Funds While Reporting Success<![CDATA[ ## Vulnerability Details **File Location**: `cctp_client.py:121-143`, `cctp_client.py:153-176`, `x402_client.py:123-141`, `main.py:134-140` **Vulnerability Type**: Incomplete cross-chain settlement and placeholder attestation **Risk Level**: High ### Vulnerable Code ```python signed_burn = w3.eth.account.sign_transaction(burn_tx, self.private_key) tx_hash = w3.eth.send_raw_transaction(signed_burn.raw_transaction) print(f"Burn transaction sent: {w3.to_hex(tx_hash)}") return { 'success': True, 'txHash': w3.to_hex(tx_hash), 'message': 'CCTP transfer initiated' } ``` ```python def wait_for_attestation(self, tx_hash: str, source_chain: str, timeout: int = 300) -> dict: """ Wait for CCTP attestation after burn """ print(f"Waiting for CCTP attestation for {tx_hash}...") # In a real implementation, we would poll Circle's API # For now, we simulate with a sleep and return placeholder bytes # which would be needed by the Arc contract's receiveMessage. start_time = time.time() while time.time() - start_time < timeout: # Simulation: wait 30 seconds time.sleep(30) # Return placeholder attestation data # In production, this comes from iris-api.circle.com return { 'success': True, 'attestation': '0x' + '0' * 128, 'message': '0x' + '0' * 128 } return {'success': False, 'error': 'Timed out waiting for attestation'} ``` ```python # Wait for attestation att_result = self.cctp_client.wait_for_attestation(burn_tx_hash, chain) if not att_result['success']: return att_result # Call authorizeUsageCCTP on Arc print("Completing payment on Arc...") tx = self.arc_contract.contract.functions.authorizeUsageCCTP( skill_id, att_result['message'], att_result['attestation'], self.cctp_client.DOMAIN_IDS[chain], Web3.to_bytes(hexstr=burn_tx_hash), memo ).build_transaction({ ``` ### Technical Analysis The CCTP imple ...[truncated 2002 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable all CCTP commands until the end-to-end protocol is fully implemented and tested. 2. Wait for the source-chain receipt and require `receipt.status == 1`. 3. Parse and validate the expected CCTP `MessageSent` or equivalent event from the confirmed receipt. 4. Retrieve the real attestation from Circle's supported API using the emitted message hash. 5. Validate API status, message bytes, attestation format, source domain, destination domain, amount, token, recipient, and nonce. 6. Submit the validated message and attestation to the correct destination contract. 7. Wait for a successful destination receipt and verify expected settlement or bond events. 8. Report success only after the destination state reflects the requested operation. 9. Persist recoverable transaction state so interrupted transfers can safely resume. 10. Add timeout and retry behavior that never fabricates successful attestation data. 11. Use separate, semantically correct functions for bonding and usage payments rather than reusing `bond_skill()` for payment burns. 12. Replace all zero-address and placeholder domain configuration before enabling the relevant chain. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
arc_contract.py:119
Finding
Reverted Blockchain Transactions Are Reported as Successful<![CDATA[ ## Vulnerability Details **File Location**: `arc_contract.py:119-126`, `arc_contract.py:146-151`, `arc_contract.py:171-176`; callers at `main.py:151-154`, `main.py:177-180`, `main.py:209-213` **Vulnerability Type**: Missing transaction receipt status validation **Risk Level**: Medium ### Vulnerable Code ```python # Wait for receipt receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash) # Extract claimId from events (placeholder - assuming simplified return for demo) return { 'success': True, 'txHash': self.w3.to_hex(tx_hash), 'claimId': 1, # Should parse from logs 'status': receipt['status'] } ``` ```python receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash) return { 'success': True, 'txHash': self.w3.to_hex(tx_hash), 'status': receipt['status'] } ``` ```python receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash) return { 'success': True, 'txHash': self.w3.to_hex(tx_hash), 'status': receipt['status'] } ``` The callers only inspect the `success` field: ```python if result['success']: print(f"\n✓ Claim submitted successfully") print(f"Transaction: {result.get('txHash', 'N/A')}") print("Voting window is now open for 72 hours.") ``` ### Technical Analysis An EVM transaction receipt with `status == 0` means the transaction reverted. The wrapper methods nevertheless return `success: True` for any mined receipt and merely include the failed status as an unused field. The CLI checks only `result['success']`. As a result, reverted claim submissions, votes, and earnings withdrawals are represented to the user as successfully completed operations. The claim submission method additionally returns a hardcoded `claimId` of `1` instead of deriving it from a confirmed event, further weakening state verification. ### Attack Path 1. A transaction is built and signed for a claim, vote, or earnings withdrawal. 2. On-chain state changes between pre-check and execution, or supplied param ...[truncated 839 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat a receipt as successful only when `receipt.status == 1`. 2. Return `success: False` with the transaction hash and a clear error when status is zero. 3. Perform an `eth_call` simulation or gas estimation before signing to detect predictable reverts. 4. Parse expected contract events from the receipt and verify their parameters. 5. For claims, derive the actual claim identifier from the `ClaimSubmitted` event rather than returning a hardcoded value. 6. Verify the resulting on-chain state after event parsing where financially important. 7. Have every caller check both the wrapper result and confirmed receipt status. 8. Add tests for mined-but-reverted transactions and race conditions. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Dependencies Are Mutable and Not Integrity-Pinned<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-3`, installation guidance at `README.md:67-70` **Vulnerability Type**: Unpinned software supply-chain dependencies **Risk Level**: Medium ### Vulnerable Code ```text web3>=6.0.0 requests>=2.31.0 python-dotenv>=1.0.0 ``` ```bash pip install -r requirements.txt ``` ### Technical Analysis All Python dependencies use open-ended lower bounds. A future installation can therefore resolve to versions that did not exist when this Skill was reviewed. No lockfile or package hashes provide reproducible resolution or artifact integrity. This is particularly sensitive because imported dependency code executes in a process that reads `PRIVATE_KEY` and signs blockchain transactions. A compromised upstream release, dependency account, package index, or transitive dependency could consequently run with access to the Skill process and environment. The audit did not identify a currently known malicious dependency in the supplied list. The vulnerability is the absence of controls preventing future, unaudited dependency changes. ### Attack Path 1. A direct or transitive dependency publishes a compromised release, or its package-distribution account is taken over. 2. A user installs the Skill at a later date with `pip install -r requirements.txt`. 3. The open-ended constraints permit the compromised version to be selected. 4. The malicious package executes during installation or import. 5. It inherits the Python process's user privileges and may access environment variables, including `PRIVATE_KEY`. 6. It can exfiltrate secrets, alter transaction construction, or perform unauthorized network and filesystem operations. ### Impact Assessment Compromised dependency code would run with the same operating-system privileges as the Skill. It could read the wallet private key from the environment, sign transactions, access user-readable files, and communicate over the network. The scope includes the wallet ...[truncated 135 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every direct dependency to an exact reviewed version. 2. Generate and commit a lockfile that includes all transitive dependencies. 3. Require cryptographic hashes for downloaded distributions, such as with `pip --require-hashes`. 4. Install only from trusted package indexes over authenticated TLS. 5. Prefer reviewed wheel artifacts and prohibit unexpected source builds. 6. Run dependency vulnerability and provenance scanning in CI. 7. Update dependencies through controlled pull requests with security review and regression testing. 8. Use an isolated virtual environment with minimal filesystem and network privileges. 9. Avoid exposing high-value wallet keys to the same long-lived process that imports broad third-party dependency trees. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (27)

Missing User Warnings

High
Confidence
97% confidence
Finding
The code signs and submits blockchain approval and authorizeUsage transactions automatically based on payment instructions from the server's 402 response. This can cause unintended token approvals and spending without an explicit consent checkpoint, and in this context the server controls key payment parameters such as amount and memo, making the automation materially risky.

Missing User Warnings

High
Confidence
97% confidence
Finding
The cross-chain flow automatically initiates a burn/payment process and then finalizes authorization on Arc without requiring the user to confirm the multi-step transfer. Because cross-chain operations are harder to reverse and rely on attestations and external infrastructure, automatic execution increases the risk of accidental fund loss or abuse if the remote service or configuration is malicious or compromised.

Missing User Warnings

High
Confidence
99% confidence
Finding
The client automatically downloads and extracts a ZIP returned by a remote server immediately after payment, with no confirmation or inspection step. In a skill-installation context this is especially dangerous because a malicious or compromised server can deliver an archive containing harmful files, path traversal entries, or executable content that the user did not knowingly approve.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The README instructs users to perform value-bearing blockchain actions such as paying, staking USDC, voting on claims, and withdrawing earnings, but it does not warn that these actions can be irreversible, may transfer real funds, and may expose users to wallet or network-selection mistakes. In a security-themed skill, users may infer the workflow is trustworthy and low-risk, which increases the chance of unsafe financial actions without informed consent.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The README tells users to set a PRIVATE_KEY environment variable for transaction signing but provides no guidance on secure handling, storage, or scoping of that credential. This is dangerous because private keys grant direct control over wallet funds, and exposing them through shell history, process environments, logs, shared machines, or misconfigured deployment environments can lead to complete wallet compromise.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The documentation instructs users to provide a wallet private key, which is an extremely sensitive credential, but gives no guidance on secure handling, storage, or use of safer alternatives. In a skill that performs blockchain payments and contract calls, this omission materially increases the chance of key exposure, wallet compromise, and unauthorized fund movement.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The `use` command is described as automatically paying a fee and downloading a skill package, while also selecting a payment path based on wallet balances, but it does not warn users that it will spend funds and fetch external content. Because the downloaded package is a skill artifact and the trust model explicitly labels skills as 'Safe to use,' this can encourage users to treat an automated paid download as low risk when it can still expose them to malicious or unreviewed content.

Intent-Code Divergence

Medium
Confidence
81% confidence
Finding
The class docstring says this is an interface to the SkillSecurityRegistry contract on Arc, but the bundled fallback ABI only defines getSkillInfo while the class also implements getClaimInfo, pendingEarnings, submitClaim, voteOnClaim, and claimEarnings. This creates a direct documentation-to-code inconsistency about what interface is actually supported and can mislead callers about available contract operations.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The code reads a sensitive credential from the PRIVATE_KEY environment variable, but there is no confirmation prompt, visible logging, or comment/docstring warning users that wallet credentials will be accessed for signing transactions. For a code file, credential access should include some disclosure unless clearly warned elsewhere in accompanying markdown, which is not present in this file.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The module signs and broadcasts a blockchain transaction immediately once submit_claim is called, with no explicit consent, dry-run, or transaction review step. In an agent skill context, this is dangerous because any upstream prompt injection, misuse, or accidental invocation can cause irreversible on-chain actions and consume funds from the loaded private key.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This method signs and sends a vote transaction without any explicit approval or review by the user. In a skill or agent environment, silent transaction submission can be abused to manipulate governance actions or simply drain gas through repeated calls, and blockchain transactions are typically irreversible once broadcast.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The earnings-claim path broadcasts a signed transaction immediately, again with no explicit confirmation, despite moving value or affecting fund flows. In the context of an agent skill that may run automatically, this creates a meaningful risk of unauthorized asset movement, gas expenditure, or mistakes involving the destination chain domain.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The function advertises waiting for a CCTP attestation but instead returns hardcoded placeholder message and attestation bytes with success=true almost immediately. In a cross-chain transfer client, this can cause downstream code to submit invalid finalization transactions, misreport bridge completion, and potentially trigger loss of funds, stuck funds, or unsafe automation decisions.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The receive_message method signs and broadcasts a blockchain transaction that completes the cross-chain transfer, but unlike bond_skill it provides no print/log message, confirmation prompt, or explanatory comment warning the user that an on-chain state-changing action is being sent. For a code file, this is a safety-critical operation with potential cost and irreversible effects, and there is no visible disclosure in this method.

Intent-Code Divergence

Medium
Confidence
90% confidence
Finding
The file is presented as a 'USDC Security Skill' checker/router, but it also initiates paid skill acquisition, bonding/staking, reporting, voting, and earnings withdrawal. This mismatch is dangerous because users may grant trust or run the tool expecting passive security assessment when it actually triggers financially sensitive actions.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
Labeling a bonded, unflagged skill as 'Safe to use' makes an unjustified security claim based only on bonding/flag state, which is not equivalent to a real security review. In this context, that output can materially mislead users into executing or paying for potentially unsafe skills, making the issue more dangerous than a mere wording problem.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The code submits an on-chain vote immediately after only an eligibility check, without a dedicated confirmation prompt summarizing the claim ID, vote direction, wallet, and transaction consequences. In a wallet-connected CLI, this increases the risk of accidental governance actions or social-engineered invocation that causes irreversible or reputation-impacting votes.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The trigger "check" is vague and can match many benign requests, making unintended invocation plausible. Because this skill is positioned as security infrastructure and may query blockchain, registry, or payment-related systems, ambiguous activation can expose users to incorrect trust decisions, unnecessary external calls, or unsafe follow-on actions.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The command trigger "use" is extremely generic and likely to collide with ordinary user language, increasing the chance that the skill is invoked unintentionally. In a security-sensitive skill that can perform trust, bonding, reporting, and claims-related actions, accidental activation could lead to confusing, misleading, or risky workflows being initiated without clear user intent.

Tainted flow: 'headers' from requests.get (line 160, network input) → requests.get (network output)

Medium
Category
Data Flow
Content
'X-Payment-Proof': json.dumps(payment_proof)
            }
            
            response = requests.get(url, headers=headers)
            
            if response.status_code == 200:
                content_type = response.headers.get('Content-Type', '')
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The comment claims ZIP handling is only simulated, but the code actually writes a server-supplied archive to disk and extracts it. This mismatch is dangerous because it can mislead reviewers and users into underestimating the risk of processing untrusted archive content, including path traversal or malicious payload installation.

Unpinned Dependencies

Low
Category
Supply Chain
Content
web3>=6.0.0
requests>=2.31.0
python-dotenv>=1.0.0
Confidence
90% confidence
Finding
The dependency `web3>=6.0.0` is not pinned to an exact version, which makes builds non-reproducible and can cause different environments to install different releases over time. In a security-sensitive package like `web3`, this increases the chance of unintentionally pulling in a vulnerable or behavior-changing version.

Unverifiable Dependency: web3 has 2 known advisory(ies) (CVE-2026-40072 (web3.py: SSRF via CCIP Read (EIP-3668) OffchainLookup URL handling); CVE-2026-40072 (web3.py: SSRF via CCIP Read (EIP-3668) OffchainLookup URL handling)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
86% confidence
Finding
`web3` has known advisories, and because the manifest does not pin a specific version, it is impossible to verify from this file whether the installed release is affected. This is more concerning in skill code that may interact with blockchain endpoints or off-chain resolution mechanisms, where an affected `web3` version could expose the environment to issues such as SSRF.

Unpinned Dependencies

Low
Category
Supply Chain
Content
web3>=6.0.0
requests>=2.31.0
python-dotenv>=1.0.0
Confidence
94% confidence
Finding
The dependency `requests>=2.31.0` is unpinned, so future installs may resolve to different versions with different security properties. Because `requests` is commonly used for outbound HTTP and has a history of security advisories, leaving it unpinned increases supply-chain and patch-verification risk.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
91% confidence
Finding
`requests` has multiple known advisories, but the unpinned requirement prevents determining whether the deployed version is safe. Since `requests` directly handles HTTP interactions, an affected version could expose credentials, alter TLS verification behavior, or mishandle attacker-controlled URLs depending on how the skill uses it.

Static analysis

No suspicious patterns detected.