Repository navigation
Security Considerations
This page covers two distinct subjects: how to use the kit safely (its probe batteries fire real attack payloads), and the kit's own security posture (execution surface, supply chain, publication safety). Every statement below is traced to a source line in commit 7e17a11 or to a test executed during this wiki run (test IDs like T14 refer to the logs in research/evidence/ of the run workspace).
- Threat model
- The payload inventories
- The kit's own execution surface
- Data you might leak, and where it lands
- Publication safety
- Hardening checklist for a run
- Known limitations (honest section)
- See also
Important
The probe stack sends path-traversal, XSS, SSTI, command-injection, SQLi and CRLF payloads at whatever URL you point it at. Run batteries only against software you own or are explicitly authorized to test — in practice, against the throwaway staged instance that lib/serve.py creates, never against a production host or a third party's site.
The kit touches security in five places:
| # | Surface | What can go wrong | Mitigation built into the kit |
|---|---|---|---|
| 1 | Probe batteries (probe/webapp_probe.py) |
Attack payloads hitting an unauthorized target | Batteries run against a ServedApp staged copy on 127.0.0.1; --base-url exists for live targets and is the operator's responsibility |
| 2 | Staged instances (lib/serve.py) |
Port collisions serving stale state; leftover processes | Verified-free port (raises if a preferred port is busy, lib/serve.py:58), temp docroot, killpg on exit (lib/serve.py:176) |
| 3 | Executing subject code | The subject is untrusted code running in your sandbox | Staging copies only the files you list; the run happens in a temp dir; PITFALLS B10 refuses to probe when the runtime cannot boot the subject (setup/check_runtime.py, exit 2) |
| 4 | Evidence files | Probe JSON/MD capture response excerpts that may contain sensitive data | Excerpts are truncated to 180 chars (probe/webapp_probe.py:91); .gitignore blocks run artefacts (workspace/, *.bundle, uploads/, v3.*/, workspace-fallback/) from being committed into the kit |
| 5 | Publishing (publish/publish.sh) |
Destroying an owner's wiki history; leaking credentials | Never prompts, never force-pushes, QA-gates before committing, and produces a bundle instead of requiring credentials |
The batteries ship two fixed payload lists. They are generic, well-known test vectors — the same class of payloads a documentation author needs in order to state, with evidence, whether an escaping or path-validation layer actually holds.
| Category | Examples from the list | Count |
|---|---|---|
| Classic relative/absolute |
../../../etc/passwd, /etc/passwd, C:\windows\win.ini
|
5 |
| URL-encoded |
..%2f..%2f..%2fetc%2fpasswd, %2e%2e%2f%2e%2e%2fetc%2fpasswd
|
2 |
| Double-encoded | ..%252f..%252fetc%252fpasswd |
1 |
| UTF-8 overlong |
%c0%ae%c0%ae/etc/passwd, ..%c0%af..%c0%afetc/passwd
|
2 |
| Filter-evasion |
....//....//....//etc/passwd, valid/../../../etc/passwd, ./../../etc/passwd
|
3 |
| NUL byte |
..%00/..%00/etc/passwd, etc/passwd%00.png, \x00
|
3 |
| Sensitive procfs | /proc/self/environ |
1 |
| Overlong paths | 40×.., 60×a/, 300-char tail |
3 |
Classification is deliberately body-based, not status-based: a refusal may legitimately return HTTP 200 with a denial banner (deliberate non-disclosure), so battery_traversal matches deny_markers in the body and separately checks leak markers (root:x:0:0, [boot loader], DB_PASSWORD, BEGIN RSA PRIVATE KEY) — probe/webapp_probe.py:171.
| Category | Example | Count |
|---|---|---|
| Reflected/DOM XSS |
<script>alert(1)</script>, <img src=x onerror=alert(1)>, <svg onload=alert(1)>, <iframe src="javascript:alert(1)">, <details open ontoggle=…>, <body onload=…>, <input onfocus=… autofocus>
|
7 |
| Event-handler/attribute breakout |
<a href="x" onclick="alert(1)">y</a>, "'><script>…
|
2 |
| DOM clobbering | <form id=x><input name=y> |
1 |
| URI scheme | javascript:alert(1) |
1 |
| SSTI |
{{7*7}}, ${7*7}, <%= 7*7 %>, #{7*7}
|
4 |
| Command injection |
$(id), backtick-id, ;id;, pipe-id |
4 |
| SQLi |
' OR '1'='1, 1; DROP TABLE users--
|
2 |
| CRLF/header injection |
%0d%0aX-Injected: yes, a\r\nb
|
2 |
| HTML oddities |
<!-- comment -->, <plaintext>, <noscript>x
|
3 |
classify_injection (probe/webapp_probe.py:97) returns one verdict per payload: REFLECTED-VERBATIM (the finding that matters), escaped, reflected-case-changed, TAG-SURVIVED, SSTI-49, empty-body, not-reflected.
Note
These batteries make the kit a documentation-evidence tool, not a substitute for a professional penetration test. There is no coverage guarantee: 26 payloads is a probe of whether escaping exists at all, not a fuzzing campaign.
Run against the reference subject MD.Viewer 2.18.0 (staged copy, PHP 8.2.34, full php-mdviewer suite in 0.46 s of wall time):
| Battery | Measured result |
|---|---|
| Traversal (20 payloads) | 17 denied by body marker; 2 answered 301 (harmless normalisation); 1 NUL-byte payload failed at transport level (ERR); 0 leaked sensitive content
|
| Injection (26 payloads) | 19 survived verbatim into the response — raw-HTML passthrough confirmed, matching the figure the kit's README documents from the original run |
| Security headers |
/md.php missing CSP, X-Content-Type-Options, X-Frame-Options, HSTS, Referrer-Policy, Permissions-Policy |
| Leak headers |
X-Powered-By: PHP/8.2.34 present |
| Upload matrix (16 filenames) |
bad.php → 400 "Only .md files are allowed."; .hidden.md → 400 forbidden pattern; ../evil.md → 200 stored as evil.md (basename sanitisation); unicode filenames accepted |
This is the kit's verification model at work: the wiki for MD.Viewer could state its security behaviour with reproduced evidence instead of transcribed claims. The findings belong to the subject, not to the kit — keep those two separated when reading examples/.
Where the tools spawn processes or open sockets (all verified by reading the code at 7e17a11):
| Tool | exec/network behaviour | Risk assessment |
|---|---|---|
lib/serve.py |
subprocess.Popen(cmd.split(), preexec_fn=os.setsid); serves on 127.0.0.1 only (default host); kills the process group on exit |
The serve command comes from the operator (--serve-cmd), not from the subject. No shell interpolation |
lib/req.py, lib/session.py
|
stdlib urllib against operator-supplied URLs; no redirects by default |
No listener; requests only where you point them |
qa/qa_wiki.py |
bash -n with the block passed via input= (not as an argument); compile() for Python blocks — never executes wiki code |
Syntax checking without execution; a malicious shell block in a wiki cannot run through the auditor |
setup/check_runtime.py |
shell=True on fixed command strings (php -r …, node -v) |
No untrusted input reaches the shell |
qa/verify_commands.py |
run() with shell=True for documented commands — by design it executes what the wiki says |
Template-shaped harness; you review the command list before running it |
setup/bootstrap.sh |
apt-get update + installs; optionally adds the Sury APT repository (PHP 8.3 recipe) |
Installs distro packages; the Sury path adds a third-party signing key — review before enabling --with-php83
|
publish/publish.sh |
git clone https://github.com/OWNER/REPO.wiki.git; no credentials used or stored |
Egress limited to github.com; fails safely into the bundle path |
The kit makes no LLM API calls — there is no network client in any tool other than the HTTP probe helpers and git/apt in the setup and publish scripts. The full external-host inventory extracted by scan/repo_scan.py from the subject tree confirms this: runtime hosts are github.com, raw.githubusercontent.com, api.github.com (bootstrap egress check), packages.sury.org (recipe text) and distro apt mirrors; every other host found by the scan (img.shields.io, cdn.jsdelivr.net, fonts.googleapis.com, cheatsheetseries.owasp.org) appears only inside examples/sample-pages/ — delivered-wiki samples, not code the tools execute.
Tip
The scaffolded Home.md template embeds a shields.io badge image (scaffold/new_page.py, HOME_TMPL). If your wiki must not reference third-party hosts, delete the badge line before publishing; the QA tool does not require it.
-
Evidence files (
research/probe.json,*.md) contain request logs, response excerpts and header dumps from the subject. If the subject echoes secrets in responses (session tokens, API keys in bodies), they land in your evidence. Treatresearch/as sensitive until reviewed. -
The staged docroot lives in a temp dir (
probe-app-*) and is deleted on exit unless you pass--keep; with--keep, review what the subject wrote to disk (battery_disklists it) before archiving. -
The delivered wiki is public (for public repos). Never paste API keys, customer data or unpatched-exploit detail into pages.
PROMPT.mdhas aSENSITIVE:header field exactly for declaring what must not be published. -
publish/output contains a full clone plus a git bundle of the wiki; both are as public as the wiki itself.
The .gitignore in the kit defends the other direction — a real near-miss recorded in CHANGELOG.md 1.2.1: a 184-file, 4.4 MB run workspace (v3.2/, containing a subject's uploads and bundles) sat untracked-but-not-ignored at the repository root, one git add -A away from being published. CI now refuses to pass when such a directory is present (.github/workflows/selftest.yml, workspace-refusal step).
publish/publish.sh encodes four rules (all exercised in test T15):
-
QA gate first —
qa_wiki.py --strictmust exit 0 before anything is copied; a wiki with dead links is not published. -
Never force-push — the
.wiki.gitrepository may contain the owner's commits (GitHub creates anInitial commitwhen the wiki is initialised through the UI). The script clones, commits on top, and hands over. -
No credential requirement — the artefacts are a prepared clone with
originconfigured, a verified bundle (git bundle verifyoutput printed), andPUBLISH.mdwith three push options. The push itself happens where credentials exist. -
Self-describing hand-over —
PUBLISH.mdrecords branch (master), commit SHA, remote URL, bundle path and the B8 warning that cloning a bundle directly needsgit clone -b master …(a bundle carries no HEAD symref; without-byou get an empty tree and every later check passes vacuously).
- Read
setup/PITFALLS.mdbefore writing any probe — its first three entries produce silently wrong results. - Run
bash setup/bootstrap.sh --subject <dir>so the runtime-floor gate can refuse a subject the interpreter cannot boot (exit 2). - Probe staged copies (
--repo), not live third-party hosts (--base-urlonly with written authorization). - Review
research/evidence/for secrets before committing anything anywhere. - Keep run artefacts inside
workspace/— the kit's.gitignorecovers that path; a stray copy at the repo root is the 1.2.1 near-miss. - Snapshot at phase boundaries (
setup/snapshot.sh workspace "phase N done") so a bad bulk edit costs minutes. - Before publishing, re-read pages that quote error strings, keys or paths verbatim — verbatim subject strings win over terminology rules, but they still must not contain live secrets.
- No SECURITY.md and no signed-release process exist in the subject repository at
7e17a11(verified: file absent, zero tags). Report issues through the repository's issue tracker; there is no vulnerability-disclosure channel documented. - Payload lists are static. A subject is not "secure" because 20 traversal payloads were denied; the lists detect absent defence, they do not prove present defence against everything.
-
qa_wiki.pychecks shell blocks withbash -n(syntax only) and Python blocks withcompile()(parse only). A syntactically valid but destructive command passes the auditor — the re-run step of Phase 4 (in a clean sandbox) is where documented commands are actually executed, and you decide what is safe to run. - The auditor's ban/terms matching is substring-based on a scrubbed view with a negation heuristic; it is a discipline aid, not a proof of factual accuracy.
- Research and Probing Guide — how to run batteries and write custom suites
- Sandbox Survival Guide — the PITFALLS system and recovery discipline
- Publishing and Delivery — the full hand-over flow
- Tool Reference — per-tool flags and exit codes
- Features and Verification Model — the one rule these protections serve
github-wiki-llm-writer 1.2.1 · MIT · Repository · Report an issue · Home · Glossary · FAQ · Troubleshooting
Facts in this wiki are verified against the source at commit 7e17a11; see Changelog and Versioning.
Home Getting Started Features and Verification Model Comparison with Alternatives
Installation and Setup Configuration Reference
The Four Phase Workflow Research and Probing Guide Writing Pages Guide QA and Audit Guide
Publishing and Delivery Security Considerations Sandbox Survival Guide Extending the Kit
Tool Reference Architecture Troubleshooting FAQ Changelog and Versioning Glossary
1.2.1 · MIT