From d131b7d960ef87e7605f01472fe7ac7214ddb915 Mon Sep 17 00:00:00 2001 From: Ian Jhumel Bautista Date: Fri, 5 Jun 2026 16:52:09 +0800 Subject: [PATCH] docs(network): VAI-011 rationale + OAI-016 structural retrofit Add vercel_ai/network.md (VAI-011), rewrite the OAI-016 section in openai_sdk/network.md for the structural predicate (dropping the stale TypeScript provisional note), and regenerate the POLICY_INDEX files. --- POLICY_INDEX.md | 31 ++++--- docs/Policy/openai_sdk/network.md | 37 +++++--- docs/Policy/vercel_ai/network.md | 148 ++++++++++++++++++++++++++++++ vercel_ai/POLICY_INDEX.md | 25 ++--- 4 files changed, 201 insertions(+), 40 deletions(-) create mode 100644 docs/Policy/vercel_ai/network.md diff --git a/POLICY_INDEX.md b/POLICY_INDEX.md index 6c410b1..79a5426 100644 --- a/POLICY_INDEX.md +++ b/POLICY_INDEX.md @@ -47,9 +47,9 @@ Users can contribute their own policies by: | LangChain / LangGraph | 11 | 3 | 0 | 1 | 15 | [langchain/POLICY_INDEX.md](langchain/POLICY_INDEX.md) | | CrewAI | 7 | 6 | 0 | 1 | 14 | [crewai/POLICY_INDEX.md](crewai/POLICY_INDEX.md) | | AutoGen / AG2 | 6 | 5 | 0 | 1 | 12 | [autogen/POLICY_INDEX.md](autogen/POLICY_INDEX.md) | -| Vercel AI SDK | 5 | 3 | 0 | 1 | 9 | [vercel_ai/POLICY_INDEX.md](vercel_ai/POLICY_INDEX.md) | +| Vercel AI SDK | 6 | 3 | 0 | 1 | 10 | [vercel_ai/POLICY_INDEX.md](vercel_ai/POLICY_INDEX.md) | | Pydantic AI | 7 | 4 | 0 | 1 | 12 | [pydantic_ai/POLICY_INDEX.md](pydantic_ai/POLICY_INDEX.md) | -| **All** | **102** | **49** | **2** | **11** | **164** | | +| **All** | **103** | **49** | **2** | **11** | **165** | | ## All rules @@ -206,16 +206,17 @@ Users can contribute their own policies by: | 149 | VAI-006 | Vercel AI | agent | vercel_ai_agent | Vercel AI agent wires a provider shell / computer / code-execution tool | high | 0.85 | 59.5 | [vercel_ai/agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/agent_safety.yaml) | | 150 | VAI-007 | Vercel AI | agent | vercel_ai_agent | Vercel AI agent tool loop has no step bound | medium | 0.60 | 24.0 | [vercel_ai/agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/agent_safety.yaml) | | 151 | VAI-008 | Vercel AI | agent | vercel_ai_agent | Vercel AI agent forces a provider execution tool every step | medium | 0.65 | 26.0 | [vercel_ai/agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/agent_safety.yaml) | -| 152 | VAI-012 | Vercel AI | repo | vercel_ai | Vercel AI project ships no agent-guidance doc (AGENTS.md/CLAUDE.md) | low | 0.90 | 13.5 | [vercel_ai/repo_hygiene.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/repo_hygiene.yaml) | -| 153 | PYD-001 | Pydantic AI | tool | pydantic_ai_tool | Pydantic AI tool has no description | low | 0.90 | 13.5 | [pydantic_ai/tool_definition.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/tool_definition.yaml) | -| 154 | PYD-002 | Pydantic AI | tool | pydantic_ai_tool | Pydantic AI tool parameters are not type-annotated | medium | 0.85 | 34.0 | [pydantic_ai/tool_definition.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/tool_definition.yaml) | -| 155 | PYD-003 | Pydantic AI | tool | pydantic_ai_tool | Pydantic AI tool body spawns a subprocess | high | 0.85 | 59.5 | [pydantic_ai/shell_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/shell_safety.yaml) | -| 156 | PYD-004 | Pydantic AI | tool | pydantic_ai_tool | Pydantic AI tool body evaluates dynamic code | high | 0.85 | 59.5 | [pydantic_ai/code_execution.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/code_execution.yaml) | -| 157 | PYD-005 | Pydantic AI | tool | pydantic_ai_tool | Pydantic AI tool fetches a caller-controlled URL (SSRF) | high | 0.80 | 56.0 | [pydantic_ai/ssrf.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/ssrf.yaml) | -| 158 | PYD-006 | Pydantic AI | tool | pydantic_ai_tool | Pydantic AI tool network call has no timeout | high | 0.85 | 59.5 | [pydantic_ai/network.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/network.yaml) | -| 159 | PYD-007 | Pydantic AI | tool | pydantic_ai_tool | Mutating Pydantic AI tool has no idempotency key | medium | 0.55 | 22.0 | [pydantic_ai/idempotency.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/idempotency.yaml) | -| 160 | PYD-101 | Pydantic AI | agent | pydantic_ai_agent | Pydantic AI agent has no structured output validation | low | 0.70 | 10.5 | [pydantic_ai/agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/agent_safety.yaml) | -| 161 | PYD-102 | Pydantic AI | agent | pydantic_ai_agent | Pydantic AI agent wires the code-execution native tool | high | 0.85 | 59.5 | [pydantic_ai/agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/agent_safety.yaml) | -| 162 | PYD-103 | Pydantic AI | agent | pydantic_ai_agent | Pydantic AI agent wires a model-driven URL-fetching native tool | medium | 0.75 | 30.0 | [pydantic_ai/agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/agent_safety.yaml) | -| 163 | PYD-105 | Pydantic AI | agent | pydantic_ai_agent | Pydantic AI agent retries with the exhaustive end strategy | low | 0.70 | 10.5 | [pydantic_ai/agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/agent_safety.yaml) | -| 164 | PYD-201 | Pydantic AI | repo | pydantic_ai | Pydantic AI project ships no agent-guidance doc (AGENTS.md/CLAUDE.md) | low | 0.90 | 13.5 | [pydantic_ai/repo_hygiene.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/repo_hygiene.yaml) | +| 152 | VAI-011 | Vercel AI | tool | vercel_ai_tool | Vercel AI tool HTTP call has no timeout | high | 0.60 | 42.0 | [vercel_ai/network.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/network.yaml) | +| 153 | VAI-012 | Vercel AI | repo | vercel_ai | Vercel AI project ships no agent-guidance doc (AGENTS.md/CLAUDE.md) | low | 0.90 | 13.5 | [vercel_ai/repo_hygiene.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/repo_hygiene.yaml) | +| 154 | PYD-001 | Pydantic AI | tool | pydantic_ai_tool | Pydantic AI tool has no description | low | 0.90 | 13.5 | [pydantic_ai/tool_definition.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/tool_definition.yaml) | +| 155 | PYD-002 | Pydantic AI | tool | pydantic_ai_tool | Pydantic AI tool parameters are not type-annotated | medium | 0.85 | 34.0 | [pydantic_ai/tool_definition.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/tool_definition.yaml) | +| 156 | PYD-003 | Pydantic AI | tool | pydantic_ai_tool | Pydantic AI tool body spawns a subprocess | high | 0.85 | 59.5 | [pydantic_ai/shell_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/shell_safety.yaml) | +| 157 | PYD-004 | Pydantic AI | tool | pydantic_ai_tool | Pydantic AI tool body evaluates dynamic code | high | 0.85 | 59.5 | [pydantic_ai/code_execution.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/code_execution.yaml) | +| 158 | PYD-005 | Pydantic AI | tool | pydantic_ai_tool | Pydantic AI tool fetches a caller-controlled URL (SSRF) | high | 0.80 | 56.0 | [pydantic_ai/ssrf.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/ssrf.yaml) | +| 159 | PYD-006 | Pydantic AI | tool | pydantic_ai_tool | Pydantic AI tool network call has no timeout | high | 0.85 | 59.5 | [pydantic_ai/network.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/network.yaml) | +| 160 | PYD-007 | Pydantic AI | tool | pydantic_ai_tool | Mutating Pydantic AI tool has no idempotency key | medium | 0.55 | 22.0 | [pydantic_ai/idempotency.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/idempotency.yaml) | +| 161 | PYD-101 | Pydantic AI | agent | pydantic_ai_agent | Pydantic AI agent has no structured output validation | low | 0.70 | 10.5 | [pydantic_ai/agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/agent_safety.yaml) | +| 162 | PYD-102 | Pydantic AI | agent | pydantic_ai_agent | Pydantic AI agent wires the code-execution native tool | high | 0.85 | 59.5 | [pydantic_ai/agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/agent_safety.yaml) | +| 163 | PYD-103 | Pydantic AI | agent | pydantic_ai_agent | Pydantic AI agent wires a model-driven URL-fetching native tool | medium | 0.75 | 30.0 | [pydantic_ai/agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/agent_safety.yaml) | +| 164 | PYD-105 | Pydantic AI | agent | pydantic_ai_agent | Pydantic AI agent retries with the exhaustive end strategy | low | 0.70 | 10.5 | [pydantic_ai/agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/agent_safety.yaml) | +| 165 | PYD-201 | Pydantic AI | repo | pydantic_ai | Pydantic AI project ships no agent-guidance doc (AGENTS.md/CLAUDE.md) | low | 0.90 | 13.5 | [pydantic_ai/repo_hygiene.yaml](https://github.com/trustabl/trustabl-rules/blob/main/pydantic_ai/repo_hygiene.yaml) | diff --git a/docs/Policy/openai_sdk/network.md b/docs/Policy/openai_sdk/network.md index 910f436..6d4e947 100644 --- a/docs/Policy/openai_sdk/network.md +++ b/docs/Policy/openai_sdk/network.md @@ -105,30 +105,40 @@ False positives: code that wraps `urlopen` behind a helper that injects a timeou ### OAI-016 — TypeScript tool fetch call has no AbortSignal timeout (Severity: high, Confidence: 0.6, Fix type: code) -**What we detect:** a TypeScript tool body that calls `fetch(` with no -`AbortSignal` / `AbortController` / `signal:` / `AbortSignal.timeout` present -(`has_body_text` for `fetch(` AND `not has_body_text` for the abort markers). +**What we detect:** a TypeScript tool `execute` body that makes an HTTP call +(`fetch` / `axios` / `got` / `undici`) with no timeout bound — the call carries no +options object with a `signal`, `timeout`, or `abortSignal` key +(`has_http_call_without_timeout: true`). The fact is **structural**: +`tsHandlerFacts` walks the handler, recognizes a recognized HTTP-client +`call_expression`, and inspects its argument objects for a timeout-bearing key, so +a bare `fetch(url)` or `fetch(url, { method })` fires while `fetch(url, { signal: +AbortSignal.timeout(ms) })` does not. This replaced the original brittle +`has_body_text` substring check. **Why it is flaggable:** Node's and the browser's `fetch` have no implicit timeout; a slow host blocks the tool's `execute` callback — and the agent run loop — indefinitely, burning the turn's wall-clock budget and tying up the worker. -Because these templates often interpolate the URL from tool arguments, the -un-cancellable call also amplifies SSRF/exfiltration impact. +When the URL is interpolated from tool arguments, the un-cancellable call also +amplifies SSRF/exfiltration impact. **Real-world consequence:** a TS `web_fetch` tool hangs the whole agent turn when the model supplies a slow or hostile URL. **Why severity is high and not medium:** the failure denies the agent loop, with -no in-band mitigation short of an explicit signal. +no in-band mitigation short of an explicit signal/timeout. **Fix type — code:** pass `signal: AbortSignal.timeout(ms)` (or an -`AbortController`) to `fetch`. - -**Confidence 0.6:** `has_body_text` is a brittle substring check — it can miss a -timeout wired through a wrapper, or fire when the abort lives in a helper. - -**Provisional (TypeScript):** this rule loads and validates today but will not -fire until the engine's TypeScript tool parser ships; it is load-validated only. +`AbortController`) to `fetch`; `axios`/`got` take a `timeout` option directly. + +**Confidence 0.6:** the structural check is precise about the call shape (no +substring guesswork), but it cannot see a timeout reached indirectly. **False +positives** (fires though bounded): an options object passed by identifier +(`fetch(url, opts)`), a `signal` / `AbortController` defined on a separate line, a +`Promise.race([fetch(url), timeout])`, or an `axios.create({ timeout })` instance. +**False negatives** (silent though unbounded): `signal: req.signal` with no +deadline behind it, or `timeout: 0` (axios "no timeout"). These indirection blind +spots — not substring brittleness — are why it sits with the TS dynamic-URL rule +OAI-024 at 0.6 rather than higher. ### OAI-018 — Tool builds outbound URL from non-literal value (Severity: medium, Confidence: 0.55, Fix type: code) @@ -218,6 +228,7 @@ the call all escape the first-argument check on a known callee. - Network calls made transitively — the tool calls a helper that performs the request without a timeout. The predicate inspects the tool body directly and does not follow calls into other modules. - Retries without backoff. A tool that times out cleanly but retries in a tight loop is still a denial-of-budget hazard; that is OAI-009 / idempotency territory, not this policy. - For OAI-024: HTTP clients outside the recognized `fetch`/`axios`/`got`/`undici` set (`node:http`/`https` `request`, `superagent`, `ky`, a wrapped client), a URL constructed via `new URL(base, modelValue)` before the call, and a model-supplied value passed positionally into a helper that performs the fetch — all escape the first-argument check on a known callee. +- For OAI-016: a timeout reached indirectly — an options object passed by identifier, a `signal`/`AbortController` bound on a separate line, a `Promise.race` deadline, or an `axios.create({ timeout })` instance — is not seen, so the rule fires on some already-bounded calls; conversely a non-deadline `signal: req.signal` or an `axios` `timeout: 0` ("no timeout") is treated as bounded and does not fire. --- diff --git a/docs/Policy/vercel_ai/network.md b/docs/Policy/vercel_ai/network.md new file mode 100644 index 0000000..c9f4d36 --- /dev/null +++ b/docs/Policy/vercel_ai/network.md @@ -0,0 +1,148 @@ +--- +policy_id: vercel_ai_network +category: vercel_ai +topic: network +rules: + - id: VAI-011 + severity: high + confidence: 0.6 + scope: tool + fix_type: code +references: [LLM10, LLM06] +--- + +# Policy Rationale: Vercel AI SDK Network Safety + +**Policy ID:** `vercel_ai_network` +**File:** `vercel_ai/network.yaml` +**Rules:** VAI-011 +**Severities:** high +**Fix types:** code +**References:** LLM10 (Unbounded Consumption), LLM06 (Excessive Agency) + +--- + +## What this policy covers + +Vercel AI SDK tools whose `execute()` body makes an outbound HTTP call with no +timeout. **VAI-011** fires on the `has_http_call_without_timeout` fact: a handler +that calls `fetch` / `axios` / `got` / `undici` and passes no options object +carrying a `signal`, `timeout`, or `abortSignal` key. A call that attaches any of +those — `fetch(url, { signal: AbortSignal.timeout(ms) })`, `axios.get(url, { +timeout: ms })` — does not fire. The fact is structural: discovery walks the +`execute` handler, recognizes the HTTP-client call, and inspects its argument +objects for a timeout-bearing key. + +--- + +## Why an unbounded HTTP call is a distinct concern in Vercel AI tools + +Node's `fetch` (and the browser's) has no implicit timeout: with no `signal`, a +request waits until the OS gives up on the socket, which on Linux is the +`tcp_syn_retries` window — minutes. In a normal web handler a parent request can +be cancelled; inside a Vercel AI agent the tool call sits in the model's tool +loop, and there is no external caller to abort it. While the call hangs, the turn +cannot advance: the user sees nothing, no other tool runs, and the whole turn's +wall-clock and token budget drains against one stalled endpoint. The serverless +runtime's own per-invocation limit may then kill the function mid-turn, losing +all progress. + +The hazard compounds with two others. Agents retry: when the call finally errors, +the model often calls the tool again, multiplying the stall and threatening the +request-worker pool. And it stacks with SSRF (VAI-003): a model-controlled URL +that *also* cannot time out is the ideal target for an injected instruction that +points the fetch at an internal host which simply never answers — a slow-loris +into the agent's own network. + +--- + +## Rule-by-rule defense + +### VAI-011 — Tool HTTP call has no timeout (Severity: high, Confidence: 0.6, Fix type: code) + +**What we detect:** a Vercel AI tool `execute()` handler that calls +`fetch`/`axios`/`got`/`undici` with no options object carrying a `signal`, +`timeout`, or `abortSignal` key (`has_http_call_without_timeout`). A bare +`fetch(url)` or `fetch(url, { method })` fires; any of the three timeout keys +clears it. + +**Why it is flaggable:** the SDK and the runtime inject no timeout, so a missing +one is observable in source and means the call can block the tool loop until the +OS or the platform kills it. + +**Real-world consequence:** a `fetchUrl({ url })` tool that does `await +fetch(url)` is handed a URL on a sinkhole host; the turn hangs until the +serverless function's wall-clock limit terminates it, and the user's request is +lost with no partial answer. + +**Why severity is high and not medium:** the failure denies the agent loop, not +just one response — there is no in-band mitigation once the call is in flight, and +the kernel default is "minutes." Medium is reserved for output-quality issues; +this freezes the turn. + +**Fix type — code:** the fix is a source edit on the call — +`signal: AbortSignal.timeout(ms)` for `fetch`, or `timeout:` for axios/got. No +guardrail, hook, or runtime config can inject a deadline into an in-flight call. + +**Confidence 0.6:** the check is precise about the call shape (no substring +guesswork), but it cannot follow indirection. **False positives** (fires though +bounded): an options object passed by identifier (`fetch(url, opts)`), a +`signal`/`AbortController` defined on a separate line, a `Promise.race([fetch(url), +timeout])`, or an `axios.create({ timeout })` instance whose per-call site shows +no `timeout` key. **False negatives** (silent though unbounded): `signal: +req.signal` with no deadline behind it, or an axios `timeout: 0` (which means "no +timeout"). The rule shares its predicate and its 0.6 calibration with the OpenAI +TS sibling OAI-016. + +--- + +## What this policy does not cover + +- A timeout reached indirectly — options passed by identifier, a signal/controller + bound on a separate line, a `Promise.race` deadline, or an `axios.create({ + timeout })` instance — is not seen, so the rule fires on some already-bounded + calls. +- A non-deadline `signal: req.signal`, or an axios `timeout: 0`, is treated as a + timeout and does not fire. +- HTTP clients outside the recognized `fetch`/`axios`/`got`/`undici` set, or a + call made in a helper in another module (discovery sees the `execute` body, not + a wrapper elsewhere). +- TypeScript only: a tool defined in plain `.js`/`.mjs` is not AST-parsed, so its + `execute()` HTTP calls are a coverage gap. +- Retry-without-backoff and unbounded response-body reads are separate + budget-exhaustion hazards this rule does not model. + +--- + +## Recommendations beyond the fix + +```typescript +import { tool } from "ai"; +import { z } from "zod"; + +export const getStatus = tool({ + description: "Fetch a status path from the vetted API host.", + inputSchema: z.object({ path: z.string() }), + execute: async ({ path }) => { + const url = new URL(`/${path.replace(/^\/+/, "")}`, "https://api.example.com"); + // Bound the call: abort after 10s so a slow host cannot hang the turn. + const res = await fetch(url, { + redirect: "error", + signal: AbortSignal.timeout(10_000), + }); + // Cap how much we read so a slow-drip body cannot drain the budget either. + const body = (await res.text()).slice(0, 500_000); + return { status: res.status, body }; + }, +}); +``` + +1. Attach `AbortSignal.timeout(ms)` (5–30s) to every `fetch`; for runtimes without + it, drive an `AbortController` from a `setTimeout` and clear it in a `finally`. + axios and got take a `timeout` option directly. +2. Surface the resulting `AbortError` / `TimeoutError` as a structured tool result + the model can branch on, rather than letting the promise hang or throwing raw. +3. Cap the response body size so a timely-but-slow-drip server cannot exhaust the + budget the timeout was meant to protect. +4. Pair with VAI-003: validate the destination host so a model-controlled URL + cannot aim the (now time-bounded) request at an internal address. diff --git a/vercel_ai/POLICY_INDEX.md b/vercel_ai/POLICY_INDEX.md index 6633e70..5867432 100644 --- a/vercel_ai/POLICY_INDEX.md +++ b/vercel_ai/POLICY_INDEX.md @@ -1,18 +1,19 @@ # Vercel AI SDK policy index -9 rules — 5 tool · 3 agent · 1 repo +10 rules — 6 tool · 3 agent · 1 repo Risk score = `severity_weight × confidence × 100` (engine formula; weights: low=0.15, medium=0.40, high=0.70). Higher = worse. -| | Id | SDK/ADK | Scope | Applies To | Policy | Severity | Confidence | Risk | Source | -| - | ------- | --------- | ----- | --------------- | ----------------------------------------------------------------------- | -------- | ---------- | ---- | ----------------------------------------------------------------------------------------------------------- | -| 1 | VAI-001 | Vercel AI | tool | vercel_ai_tool | Vercel AI tool execute() spawns a subprocess | high | 0.85 | 59.5 | [shell_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/shell_safety.yaml) | -| 2 | VAI-002 | Vercel AI | tool | vercel_ai_tool | Vercel AI tool execute() evaluates code (eval / new Function) | high | 0.90 | 63.0 | [code_execution.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/code_execution.yaml) | -| 3 | VAI-003 | Vercel AI | tool | vercel_ai_tool | Vercel AI tool execute() fetches a model-controlled URL | high | 0.75 | 52.5 | [ssrf.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/ssrf.yaml) | -| 4 | VAI-004 | Vercel AI | tool | vercel_ai_tool | Vercel AI tool has no description | low | 0.90 | 13.5 | [tool_definition.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/tool_definition.yaml) | -| 5 | VAI-005 | Vercel AI | tool | vercel_ai_tool | Vercel AI tool accepts untyped input | medium | 0.80 | 32.0 | [tool_definition.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/tool_definition.yaml) | -| 6 | VAI-006 | Vercel AI | agent | vercel_ai_agent | Vercel AI agent wires a provider shell / computer / code-execution tool | high | 0.85 | 59.5 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/agent_safety.yaml) | -| 7 | VAI-007 | Vercel AI | agent | vercel_ai_agent | Vercel AI agent tool loop has no step bound | medium | 0.60 | 24.0 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/agent_safety.yaml) | -| 8 | VAI-008 | Vercel AI | agent | vercel_ai_agent | Vercel AI agent forces a provider execution tool every step | medium | 0.65 | 26.0 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/agent_safety.yaml) | -| 9 | VAI-012 | Vercel AI | repo | vercel_ai | Vercel AI project ships no agent-guidance doc (AGENTS.md/CLAUDE.md) | low | 0.90 | 13.5 | [repo_hygiene.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/repo_hygiene.yaml) | +| | Id | SDK/ADK | Scope | Applies To | Policy | Severity | Confidence | Risk | Source | +| -- | ------- | --------- | ----- | --------------- | ----------------------------------------------------------------------- | -------- | ---------- | ---- | ----------------------------------------------------------------------------------------------------------- | +| 1 | VAI-001 | Vercel AI | tool | vercel_ai_tool | Vercel AI tool execute() spawns a subprocess | high | 0.85 | 59.5 | [shell_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/shell_safety.yaml) | +| 2 | VAI-002 | Vercel AI | tool | vercel_ai_tool | Vercel AI tool execute() evaluates code (eval / new Function) | high | 0.90 | 63.0 | [code_execution.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/code_execution.yaml) | +| 3 | VAI-003 | Vercel AI | tool | vercel_ai_tool | Vercel AI tool execute() fetches a model-controlled URL | high | 0.75 | 52.5 | [ssrf.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/ssrf.yaml) | +| 4 | VAI-004 | Vercel AI | tool | vercel_ai_tool | Vercel AI tool has no description | low | 0.90 | 13.5 | [tool_definition.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/tool_definition.yaml) | +| 5 | VAI-005 | Vercel AI | tool | vercel_ai_tool | Vercel AI tool accepts untyped input | medium | 0.80 | 32.0 | [tool_definition.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/tool_definition.yaml) | +| 6 | VAI-006 | Vercel AI | agent | vercel_ai_agent | Vercel AI agent wires a provider shell / computer / code-execution tool | high | 0.85 | 59.5 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/agent_safety.yaml) | +| 7 | VAI-007 | Vercel AI | agent | vercel_ai_agent | Vercel AI agent tool loop has no step bound | medium | 0.60 | 24.0 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/agent_safety.yaml) | +| 8 | VAI-008 | Vercel AI | agent | vercel_ai_agent | Vercel AI agent forces a provider execution tool every step | medium | 0.65 | 26.0 | [agent_safety.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/agent_safety.yaml) | +| 9 | VAI-011 | Vercel AI | tool | vercel_ai_tool | Vercel AI tool HTTP call has no timeout | high | 0.60 | 42.0 | [network.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/network.yaml) | +| 10 | VAI-012 | Vercel AI | repo | vercel_ai | Vercel AI project ships no agent-guidance doc (AGENTS.md/CLAUDE.md) | low | 0.90 | 13.5 | [repo_hygiene.yaml](https://github.com/trustabl/trustabl-rules/blob/main/vercel_ai/repo_hygiene.yaml) |