Skip to content
Back to skills

Path Safety

BSecurity

Prevent path traversal (arbitrary file read or write) in server and agent code. Use when writing or reviewing code that turns a client-, user- or LLM-supplied name into a filesystem path - download or upload routes, agent file tools (read, list, write), archive extraction, data loaders and connectors, workspace or sandbox setup. Ships a stdlib confine() helper with tests.

  • 3 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 16, 2026
ai-agentspythonrustgonodeflask

Works with

  • cli

Security analysis

B75/100
  • criticalAccesses sensitive system or user directories

Pro scans all 2 files and shows the line behind each finding

Scanned October 16, 2026

npx -y skills add ahmadhammoud56/MOAA --skill path-safety --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Path Safety?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Path Safety
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/ahmadhammoud56-path-safety/badge)](https://www.skillsdirectory.com/skills/ahmadhammoud56-path-safety)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: path-safety
description: Prevent path traversal (arbitrary file read or write) in server and agent code. Use when writing or reviewing code that turns a client-, user- or LLM-supplied name into a filesystem path - download or upload routes, agent file tools (read, list, write), archive extraction, data loaders and connectors, workspace or sandbox setup. Ships a stdlib confine() helper with tests.
---

# Path safety

Any name that crosses a trust boundary (HTTP parameter, form field, archive entry, blob key, or an argument an LLM wrote for a tool call) is hostile until confined. One rule covers most of it: **resolve once inside a fixed root, check containment with a path-aware test, then use exactly that resolved path.**

`scripts/confine.py` implements that rule with no dependencies. Copy `confine()` into the project or port it; run `python scripts/confine.py --self-check` to see it pass, and `python scripts/confine.py ROOT NAME` to test one name. If the project already has a confinement helper, use that one everywhere instead and check it against the cases below.

## Rules

1. **Confine before every filesystem call.** Never `Path(root) / name` or `os.path.join(root, name)` on raw input. Joining an absolute path discards the root (`Path("/srv") / "/etc/passwd"` is `/etc/passwd`), and `..` walks out.
2. **Check containment on resolved paths, never with string prefixes.** `str(p).startswith(str(root))` accepts `/srv/ws_evil` for root `/srv/ws`. Use `resolved.is_relative_to(root.resolve())` (Python 3.9+); in Node, `path.relative(root, p)` must not start with `..` or be absolute.
3. **Use the path you checked.** Pass the confined path to `open` or `send_file`. A helper that takes the raw name again, such as Flask's `send_from_directory(dir, name)`, parses it a second time with different rules, so your check and the real access can disagree.
4. **LLM tool arguments are user input.** An agent's read, list or write tools get a confined root from the entry point and call `confine()` on every argument. Do not hand-roll a join and `relative_to` inside each tool.
5. **Extract archives entry by entry.** Zip and tar names can be `../x` or absolute ("zip slip"), and tar members can be links. Confine each member name; for tar on Python 3.12+ (and patched older releases) also pass `filter="data"` to `extractall`.
6. **Gate host-filesystem features by deployment.** A loader that reads any local path is fine in a single-user desktop app and is an open file-read on a shared server. Disable such features unless the deployment is single-user, and fail closed when the mode is unknown.
7. **Path checks do not contain generated code.** If LLM-written code runs in the server process, it can open any file. Multi-user deployments need a real sandbox (container or separate user); default to it in deployment templates.

## Failure modes the helper rejects

| Input | Why it is dangerous |
| --- | --- |
| `../x`, `a/../../x`, `..\x` | Walks out of the root. Backslash is a separator on Windows. |
| `/etc/passwd`, `C:\x`, `C:x`, `\\srv\share` | Absolute, drive or UNC path replaces the root. |
| `f.txt:stream`, `f.txt::$DATA` | NTFS alternate data streams. |
| `CON`, `nul.txt`, `COM1.log` | Windows device names, at any depth and with any extension. |
| `a.`, `a ` | Windows strips trailing dots and spaces, so these alias another file and dodge extension checks. |
| A symlink or junction inside the root that points out | Caught because the check runs after `resolve()`. |
| NUL byte, empty string | Truncation tricks and "the root itself". |

## Known limits

- **Link races.** `confine()` checks, then you open. If an untrusted party can create symlinks inside the root, they can swap one in between. Do not let untrusted users create links in served directories; on POSIX, opening with `O_NOFOLLOW` closes the last component.
- **Name policy is separate.** `confine()` decides *where* a file may live, not *which* names are allowed. Add an extension or name allow-list when serving only certain types.

## Quick review check

Search the change for risky patterns, then confirm each hit goes through the confinement helper:

```
rg -n "send_from_directory|startswith\(|os\.path\.join\(|extractall\(|Path\([^)]*\) */" <changed paths>
```

| Writing | Must do |
| --- | --- |
| Download route | `confine()` then `send_file(confined)`. |
| Upload or write | `confine()`, create parents under the confined path, set your own filename policy. |
| Agent file tool | Confined root from the entry point; `confine()` per argument. |
| Archive import | `confine()` per member; tar `filter="data"`. |
| Local-folder loader | Deployment gate; off on shared servers. |
| Deployment config | Sandbox on whenever more than one user shares the server. |

Files in this skill

  • SKILL.md4.7 KB
  • scripts/confine.py4.2 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.