Archive extraction preflight: zip-slip, duplicate destinations, symlink conflicts
Checks a manifest of up to 2000 archive entries (path, type, link target, sizes) before extraction, for an agent that unpacks zip, tar or OOXML files: ../ traversal (also through backslashes), absolute, drive-letter and UNC paths, entries that write through a symlink, symlink and hardlink targets that leave the root, duplicate destinations, case-only and NFC/NFD collisions, Windows reserved names, and declared-size limits. It reads only the manifest you supply: it does not open or decompress the archive, check file contents, or know the extractor's own sanitising, so a pass means the manifest has no listed hazard.
What you get
Zip-slip and symlink write-through are how an archive overwrites files outside the target folder. The check runs on the file list before anything is written.
Call it
Send the JSON body below to https://agents.writerslogic.com/api/v1/archive-preflight. An unpaid request answers 402 with the price and how to pay; a request that fails with a 5xx status is not charged.
curl -i -X POST https://agents.writerslogic.com/api/v1/archive-preflight \
-H 'content-type: application/json' \
-d '{"entries":[{"path":"app/","type":"dir"},{"path":"app/run.sh","type":"file","size":120},{"path":"app/Run.sh","type":"file","size":90},{"path":"app/link","type":"symlink","linkTarget":"../../etc"},{"path":"app/link/passwd","type":"file","size":5},{"path":"..\\..\\evil.dll","type":"file","size":1}]}'
Input
| Field | Type | Required | Description |
|---|---|---|---|
entries | array<object>; max 2000 items | yes | At most 200000 characters of paths and link targets in total. |
targets | array<string; one of "windows", "macos", "linux">; max 3 items | no | Filesystems to check collisions and name rules for. Default: windows, macos and linux. |
destinationRoot | string; length 0..1024 | no | Extraction directory; counted towards total path length. |
limits | object | no |
Example request body
{
"entries": [
{
"path": "app/",
"type": "dir"
},
{
"path": "app/run.sh",
"type": "file",
"size": 120
},
{
"path": "app/Run.sh",
"type": "file",
"size": 90
},
{
"path": "app/link",
"type": "symlink",
"linkTarget": "../../etc"
},
{
"path": "app/link/passwd",
"type": "file",
"size": 5
},
{
"path": "..\\..\\evil.dll",
"type": "file",
"size": 1
}
]
}
Result
The response is { "data": ..., "_provenance": ... }. data carries these fields; _provenance is a signed W3C Verifiable Credential with a C2PA-style manifest, checked free at POST /api/v1/verify-provenance.
| Field | Type | Required | Description |
|---|---|---|---|
status | string; one of "pass", "fail", "unknown", "unsupported" | no | pass: every rule held. fail: a rule was violated (see witnesses). unknown: the input lacks the evidence to decide. unsupported: outside what this engine covers. Missing evidence is never a pass. |
checked | integer | no | |
targets | array<string> | no | |
findings | array<object> | no | Capped at 100. Errors make the status fail; warnings do not. |
findingCount | integer | no | |
witnesses | array<object> | no | |
versions | object | no | Every semantic dependency of this answer (engine, tables and their as-of dates, tokenizer, calendar, rounding profile). |
limits | array<string> | no | |
deterministic | boolean | no | Always true: pure code, no model. |
Pay and connect
Available through: x402 (USDC on Base), MCP. MCP clients reach it through the infra-preflight-kit package.
Machine-readable
- This agent as JSON (schemas, example, OpenAPI operation)
- Free signed sample response
- OpenAPI 3.1 · Catalog · Index of every agent