Dev.to AI πŸ€– Ai πŸ‘ 0 πŸ“– 10 min read

The Files You Attach Are Exhibits

You should treat every file you send a coding model as an exhibit that can outlive the chat. A context window is not a private whiteboard, even when the editor tab still looks like home. The useful response is a local pr

You should treat every file you send a coding model as an exhibit that can outlive the chat. A context window is not a private whiteboard, even when the editor tab still looks like home. The useful response is a local preflight that blocks secrets, internal maps, and live credentials before any request is built.

A coding assistant answers in your project's voice, so it is tempting to brief it like a teammate. It does not join your incident channel, sign your NDA, or rotate a token when a paste goes wrong. You are handing exhibits to a stranger with excellent memory and a disk you do not own. That stranger may be a local process, a vendor runtime, or a free shared server you never inventory.

The threat is not a cinematic jailbreak; it is ordinary oversharing under deadline pressure in a real repository. You attach a supposedly blank example file and accidentally include the neighboring live environment file. You paste a failing test that still embeds a staging JWT inside a fixture header that looks like noise. You drop a stack trace that prints an internal hostname, a customer email, and a bearer token together.

Think of the model session as a deposition room with the recorder already running on the table. Anything you mark as context can be stored, summarized, or replayed under a policy you did not write. You will not receive a reliable inventory of those exhibits after the request has already left. The only inventory that matters is the one you generate locally, with a tool that works offline.

Four rooms, one courier

Draw four rooms before you paste, even if you only draw them in a comment above the prompt. Room one is the workstation and the files that should never leave the disk you still control. Room two is the prompt builder, including editor plugins that pack nearby files you did not actually read. Room three is the model runtime, which may be local, billed, or a free hosted server. Room four is everything that comes back, including the completion, the tool trace, and the client history file.

Secrets jump rooms without asking you, because the courier is helpful and slightly nosy. A plugin that adds nearby files for better context is a courier, not a reviewer with a warrant. A free server that accepts a zip of your tree is a loading dock with someone else's cameras. Your job is to search the courier's bag before it reaches that dock, every time, with a command a teammate can rerun.

Keep the scanner boring on purpose, because clever classifiers fail open when you are tired and late. Regular expressions will not replace a security review, and they will not satisfy a regulator sitting on a real incident. They will catch the embarrassing class of mistakes that still dominate writeups: keys in fixtures, PEM blocks in temporary folders, and Authorization headers in copied curl. Treat every hit as a blocker until a human says the line is actually fiction.

Room What actually lives there Default stance before a send
Workstation Private keys, live .env, customer exports Do not copy into the attach set
Prompt builder Editor plugins, extra β€œnearby” files, chat history Inspect the packed bag, not the tab title
Model runtime Local process or hosted GPU path you do not audit Send only synthetic or already scanned exhibits
Return path Completions, tool traces, local session transcripts Store encrypted or disable when the repo is sensitive

A preflight you can refuse to skip

Save the following proposal as preflight_context.py and run it against a staging directory that contains only the files you intend to attach. It is unlabeled production code in the sense that you can execute it, but you should still read it before you trust it on a repository you care about. The script prints blockers on stdout and returns exit status two when the bag is still dirty.

#!/usr/bin/env python3
"""Local preflight for files you might send to a coding model.

Proposal: refuse the upload if any rule hits. This is not a compliance program.
"""
from __future__ import annotations

import argparse
import pathlib
import re
import sys

RULES = [
    ("pem_private_key", re.compile(r"-----BEGIN ([A-Z0-9 ]+)?PRIVATE KEY-----")),
    ("aws_access_key", re.compile(r"\bAKIA[0-9A-Z]{16}\b")),
    ("generic_bearer", re.compile(r"(?i)\bbearer\s+[A-Za-z0-9\-._~+/]+=*")),
    (
        "jwt_like",
        re.compile(
            r"\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b"
        ),
    ),
    (
        "env_assignment",
        re.compile(
            r"(?i)^(api|auth|secret|token|password|private)_?[a-z0-9_]*\s*="
        ),
    ),
    (
        "private_ipv4",
        re.compile(
            r"\b10\.\d{1,3}\.\d{1,3}\.\d{1,3}\b"
            r"|\b192\.168\.\d{1,3}\.\d{1,3}\b"
            r"|\b172\.(1[6-9]|2\d|3[0-1])\.\d{1,3}\.\d{1,3}\b"
        ),
    ),
    ("internal_host", re.compile(r"(?i)\b[a-z0-9.-]+\.(internal|corp|lan|local)\b")),
    (
        "email_like",
        re.compile(r"\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b", re.I),
    ),
]

SKIP_DIRS = {".git", "node_modules", "dist", "build", ".venv", "__pycache__"}
TEXT_SUFFIXES = {
    ".py", ".js", ".ts", ".tsx", ".go", ".rb", ".java", ".env",
    ".yml", ".yaml", ".json", ".md", ".txt", ".toml", ".ini", ".cfg", ".sh",
}

def iter_files(root: pathlib.Path):
    for path in root.rglob("*"):
        if not path.is_file():
            continue
        if any(part in SKIP_DIRS for part in path.parts):
            continue
        if path.suffix.lower() not in TEXT_SUFFIXES and path.name not in {
            ".env",
            "id_rsa",
            "id_ed25519",
        }:
            continue
        yield path

def scan_file(path: pathlib.Path) -> list[tuple[int, str, str]]:
    hits: list[tuple[int, str, str]] = []
    try:
        text = path.read_text(encoding="utf-8", errors="replace")
    except OSError as exc:
        return [(0, "unreadable", str(exc))]
    for lineno, line in enumerate(text.splitlines(), start=1):
        for name, pattern in RULES:
            if pattern.search(line):
                hits.append((lineno, name, line.strip()[:160]))
    return hits

def main() -> int:
    parser = argparse.ArgumentParser(
        description="Refuse model context that still looks like production evidence."
    )
    parser.add_argument("root", type=pathlib.Path)
    parser.add_argument("--allow-email", action="store_true")
    args = parser.parse_args()
    blocked = 0
    for path in iter_files(args.root.resolve()):
        for lineno, name, snippet in scan_file(path):
            if name == "email_like" and args.allow_email:
                continue
            blocked += 1
            print(f"BLOCK {path}:{lineno} [{name}] {snippet}")
    if blocked:
        print(
            f"preflight failed: {blocked} exhibit(s) still in the bag",
            file=sys.stderr,
        )
        return 2
    print("preflight ok: no rule hits in attach set")
    return 0

if __name__ == "__main__":
    raise SystemExit(main())

Run it against a staging folder, not against the whole monorepo on the first pass, because a flood of findings trains you to click through them. Copy only the two or three files that actually exhibit the bug, then let the scanner argue with that smaller bag. If a fixture still holds a live token, replace it with an obviously fake constant before you copy, and scan again until the command is quiet.

mkdir -p /tmp/model-exhibits
cp app/payment_retry.py tests/test_payment_retry.py /tmp/model-exhibits
python3 preflight_context.py /tmp/model-exhibits
echo $?
# Replace a live fixture token, then scan again:
# sed -i 's/eyJ[^"]*/test_token_not_secret/' /tmp/model-exhibits/test_payment_retry.py

When the scanner is quiet, you still read the files once with your own eyes, because customer identifiers hide in comments that no regex author predicted. A string like test_token_not_secret is easier to review than a redacted blob that still looks like a JWT in passing. If the model only needs a shape, give it a shape you would be willing to paste into a public issue tracker.

What must not ride along

Some objects should never enter room three, even after you rename them or call them examples. Production private keys, customer exports, unreleased vulnerability notes, and session cookies are inventory, not context for a completion. If the assistant needs an object graph, you synthesize one with fake names and impossible account numbers. Realism is not a gift you owe a model that cannot take your pager.

Internal architecture can be a secret even when it contains no key material at all. A complete service map, a hostname convention, and a comment about which jump host bypasses MFA will help an attacker more than they help a retry loop. You can describe backoff math without delivering the wiring diagram of the plant that runs it. If a file is useful only because it is real, it is probably too real to attach.

Logs deserve the same suspicion you already apply to copied traces in an incident channel, because logs are just structured pastes with timestamps. A single application line can carry a cookie, a user id, and an internal URL that should never leave room one. If you must show a failure, rebuild a minimal log from fake identifiers and the one stack frame that exhibits the bug. Do not strip a token by eye in a twelve-hundred-line file while the model waits.

The session transcript on your machine is also an exhibit, including the prompt you decided not to send. Clients that keep history will store the dirty draft beside the clean one, often in a JSON file the next plugin will happily reread. Point that history at an encrypted path you control, or disable it when the repository is sensitive. A redaction gate that writes findings to a world-readable log has only moved the secret to a friendlier filename.

When the runtime is free, the bag still is not

Local models shrink room three, but they do not delete plugin packing or the local transcript risk you already have. Hosted models widen room three to a network path you cannot audit with ps and a flashlight. Free model access and a free server make that widening easy to ignore, because no invoice arrives to remind you that packets left the building.

MonkeyCode's free model access and free server option are relevant here only as a concrete hosted setting for the same preflight, not as a substitute for the scanner. Disclosure: This article was prepared as part of MonkeyCode's product outreach. If you use that option, keep preflight_context.py and the exhibit folder on the workstation, and send only a directory that already returned exit status zero. The hosted runtime can still log prompts on its side; your scanner cannot see that log, which is why the bag must be clean before it travels.

A reasonable loop looks like copy, scan, read, send, then delete the exhibit folder so the next paste cannot inherit yesterday's fixtures. You can wrap that loop in a shell function if your team already lives in the terminal and reviews functions like this. You should not wrap it in a browser extension you have not read, because the extension becomes another courier with its own disk.

preflight_send() {
  src="$1"
  stage="$(mktemp -d /tmp/exhibits.XXXXXX)"
  cp -R "$src" "$stage/attach"
  python3 "$HOME/bin/preflight_context.py" "$stage/attach" || {
    rm -rf "$stage"
    echo "refusing to send; scanner blocked the attach set" >&2
    return 2
  }
  echo "attach set staged at $stage/attach"
  echo "send that directory only, then: rm -rf $stage"
}

Limits, and who should not stop here

This workflow will miss secrets that are split across lines, encrypted at rest, or encoded in screenshots and design exports. It will also flag harmless addresses in license headers if you forget --allow-email on a docs folder. Regex preflight is a seatbelt, not an airbag, and it does not create a processing agreement with anyone who runs the model. Green output means no rule hit, not that the file is harmless in a courtroom.

Do not use this approach as your only control if you handle regulated health data, payment card data, or classified government material. Those regimes need contractual terms, approved vendors, and often an air gap, not a volunteer script in /tmp. Do not use a quiet scan to justify pasting a customer database because the completion would be more accurate with the real rows.

Teams that already operate an allowlisted enterprise assistant with DLP in the proxy may find the script redundant at the network edge. Run it anyway on the laptop if the editor plugin can attach files that the proxy never sees as a distinct upload. The plugin is still room two, and room two is where most oversharing starts during a normal afternoon.

The core conclusion does not change when the model gets better at producing patches you would almost merge. Better completions increase the volume of files you are willing to attach, which increases the volume of exhibits you might leak. Keep the preflight on your side of the elevator, even when the stranger on the other side answers for free.

πŸ“° Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.