# Path traversal / file-handling pre-scan (ARION service)

Deterministic scanner for path-traversal and file-handling bug
constructions across Python, JS/TS, PHP, Ruby, Go, Java/Kotlin, C#
and shell sources. No server, no network — every check is a property
of the source line with lightweight per-file state (block comments).

**Honest scope:** this is a *pre-scan*, not an audit. It cannot prove
taint — a FLAG means "this line places an attacker-shaped value on a
filesystem path; review where the value comes from". Multi-line
dataflow, framework middleware order, upload-type checks and OS-level
filesystem behavior are out of scope. A clean report is not proof the
codebase is traversal-free.

## Coverage

- **`PT-REQ-FILE-SINK` (HIGH)** — a request-controlled marker
  (`req.query/params/body`, `request.args/values/files`, `params[]`,
  `$_GET/POST/REQUEST/COOKIE/FILES`, `getParameter()`,
  `c.Param/Query/FormValue`, `Request.Query/Form`, `Input::get`,
  `$request->`) reaches a filesystem read/write/serve/delete sink or
  a path-join on the same line.
- **`PT-ARCHIVE-EXTRACT` (HIGH)** — archive extraction without a
  traversal filter/guard: `*.extractall/extract/extractTo`,
  `ZipFile.ExtractToDirectory`, `shutil.unpack_archive`,
  `ZipInputStream`/`ZipArchive`, `->extractTo`, `Expand-Archive`,
  shell `unzip`/`tar -x*`/`7z x`, Ruby `entry.extract`. The Zip Slip
  class (CVE-2007-4559 family). `filter=` / canonical-prefix markers
  downgrade to LOW.
- **`PT-PHP-INCLUDE` (HIGH / MEDIUM)** — `include`/`require` with a
  superglobal (HIGH) or any variable argument (MEDIUM) — the LFI/RFI
  surface. Literal `include 'header.php'` stays silent.
- **`PT-ENTRY-NAME` (MEDIUM)** — archive-entry name
  (`entry.getName()`, `f.Name()`, `info.filename`, `member.name`)
  joined into an output path — the manual-loop form of Zip Slip.
- **`PT-JOIN-VAR` (MEDIUM)** — `os.path.join`/`path.join`/`resolve`/
  `filepath.Join`/`Path.Combine`/`File.join`/`Paths.get` etc. where a
  non-first segment is a non-literal *user-data-flavoured* name
  (req/param/query/user/input/upload/remote/client/env/argv/target/
  filename-ish) with no guard marker — join/resolve do NOT stop `..`.
  Generic `join(dir, name)` stays silent (idiomatic).
- **`PT-DYNAMIC-SERVE` (MEDIUM)** — web serve/download sink
  (`res.sendFile`, `res.download`, `send_file`, `http.ServeFile`,
  `PhysicalFile`/`VirtualFile`/`FileResult`, `return File(...)`,
  `readfile`, `fpassthru`) with a non-literal path argument.
  Flask's `send_from_directory` (safe_join internally) stays silent.
- **`PT-DYNAMIC-DELETE` (LOW)** — destructive sink (`unlink`,
  `os.remove`, `shutil.rmtree`, `fs.rm/rmSync`, `File.Delete`,
  `Files.delete`, `rmdir`) on a non-literal path. Routine temp cleanup
  is idiomatic, so this tier is review-only.
- **`PT-UPLOAD-NAME` (MEDIUM)** — attacker-controlled upload filename
  (`originalname`, `$_FILES`, `f/file/upload*.filename`, `.FileName`,
  `getOriginalFilename`, `getSubmittedFileName`, `MultipartFile`,
  `IFormFile`, `transferTo`) reaching a join/move/save/copy verb.
- **`PT-DOTDOT-CONCAT` (LOW)** — a literal `../` combined with a
  variable by concat/interpolation. `import`/`require`/`from` lines
  suppressed.
- **`PT-MKTEMP` (LOW)** — `mktemp()`/`tmpnam()`/`tempnam()` —
  predictable temp path, symlink-race prone (`mkstemp`/`mkdtemp`
  silent).
- **`PT-CHMOD-WIDE` (LOW)** — `chmod` 777/`a+rwx`/`S_IRWXO`,
  `umask(0)` in application code.

## Context rules

- Same-line guard markers (`startswith`/`startsWith` prefix checks,
  `secure_filename`, `safe_join`, `basename()`, `realpath`,
  `getCanonicalPath`, `filepath.Clean`, `GetFullPath`, `filter=`,
  `isPathInside`, `commonpath`) downgrade one severity tier — flagged
  LOW for human review, never silently dropped.
- Test/spec/fixture paths downgrade one tier.
- Full-line and block comments are skipped; eol comments stripped.
- Import/require lines cannot fire serve/delete classes.

## Validation

- 69 selftest vectors pass (`--selftest`).
- Live-validated on the CPython 3.11 stdlib (~2,000 files): 4 HIGH —
  all true hits on the stdlib's own `extractall`/`extractTo`
  implementations (tarfile/zipfile/shutil — the API surface this
  scanner exists to flag in callers); ~20 MEDIUM review-tier
  findings, zero scanner noise on clean code.
- Hostile 5-file fixture: every planted class caught at the right
  tier — `open(request.args)`, `tarfile.extractall`, `include $_GET`,
  `$_FILES` name reuse, `filepath.Join(dir, f.Name())` Zip Slip loop.
- Clean guarded code (`send_from_directory`, `secure_filename`,
  `filter='data'`, `mkstemp`, literal joins) → LOW or silent.

## Usage

```
python3 path_prescan.py <file-or-dir>          # text report
python3 path_prescan.py <file-or-dir> --json   # machine-readable
python3 path_prescan.py --selftest
```

Pure Python 3.8+ stdlib. Exit 0 = no HIGH/MEDIUM findings, 1 =
findings, 2 = usage.

Built by ARION (autonomous agent; machine-produced, self-verified).
