Skip to content

Commit 4c316e5

Browse files
committed
Add CONTRIBUTING, CHANGELOG, and smoke-test workflow
Adds the artifacts needed to claim several OpenSSF Best Practices criteria (contribution_requirements, release_notes, test, test_invocation, test_continuous_integration). Smoke test runs the generator on Python 3.10/3.11/3.12 across obfuscation levels 0 and 4 and asserts 69 PDFs + 1 SVG with valid %PDF- headers.
1 parent 3e787e8 commit 4c316e5

3 files changed

Lines changed: 176 additions & 0 deletions

File tree

.github/workflows/smoke-test.yml

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
name: Smoke test
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
generate:
14+
name: Generate PDFs (Python ${{ matrix.python-version }}, obfuscate ${{ matrix.obfuscate }})
15+
runs-on: ubuntu-latest
16+
strategy:
17+
fail-fast: false
18+
matrix:
19+
python-version: ["3.10", "3.11", "3.12"]
20+
obfuscate: [0, 4]
21+
steps:
22+
- name: Check out repository
23+
uses: actions/checkout@v4
24+
25+
- name: Set up Python ${{ matrix.python-version }}
26+
uses: actions/setup-python@v5
27+
with:
28+
python-version: ${{ matrix.python-version }}
29+
30+
- name: Install dependencies
31+
run: |
32+
python -m pip install --upgrade pip
33+
pip install -r requirements.txt
34+
35+
- name: Generate PDFs
36+
run: |
37+
python3 malicious-pdf.py https://example.com --obfuscate ${{ matrix.obfuscate }}
38+
39+
- name: Verify expected number of PDFs produced
40+
run: |
41+
EXPECTED_PDF=69
42+
EXPECTED_SVG=1
43+
ACTUAL_PDF=$(find output -maxdepth 1 -name '*.pdf' -type f | wc -l | tr -d ' ')
44+
ACTUAL_SVG=$(find output -maxdepth 1 -name '*.svg' -type f | wc -l | tr -d ' ')
45+
echo "Expected $EXPECTED_PDF .pdf + $EXPECTED_SVG .svg, found $ACTUAL_PDF + $ACTUAL_SVG"
46+
if [ "$ACTUAL_PDF" -ne "$EXPECTED_PDF" ] || [ "$ACTUAL_SVG" -ne "$EXPECTED_SVG" ]; then
47+
echo "::error::Output file count mismatch"
48+
ls -la output/
49+
exit 1
50+
fi
51+
52+
- name: Verify all PDFs are non-empty and start with %PDF-
53+
run: |
54+
fail=0
55+
for f in output/*.pdf; do
56+
if [ ! -s "$f" ]; then
57+
echo "::error::$f is empty"
58+
fail=1
59+
continue
60+
fi
61+
head -c 5 "$f" | grep -q '%PDF-' || {
62+
echo "::error::$f does not start with %PDF- header"
63+
fail=1
64+
}
65+
done
66+
exit $fail

CHANGELOG.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Changelog
2+
3+
All notable changes to this project are documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Added
11+
- `CONTRIBUTING.md` describing the contribution workflow and testing policy.
12+
- `CHANGELOG.md` (this file).
13+
- CI smoke-test workflow that runs the generator and asserts the expected
14+
number of PDF files is produced on every push and pull request.
15+
16+
## [1.0.0] - 2026-04-20
17+
18+
First tagged release. Captures the state of the project at the time the
19+
changelog was introduced.
20+
21+
### Added
22+
- 67 PDF generators covering phone-home callbacks, SSRF, XSS, XXE, NTLM
23+
credential theft, and data exfiltration techniques.
24+
- `--obfuscate` flag with levels 0–4, including base64 JS payload staging
25+
(level 4) inspired by the April 2026 Adobe Reader 0-day analysis.
26+
- `--output-dir` and `--no-credit` flags.
27+
- Visible credit footer rendered on each generated PDF page.
28+
- Filename + short payload description printed during generation.
29+
- Test cases from 2025–2026 CVEs targeting server-side processors
30+
(Apache Tika, LibreOffice, Foxit, Apryse).
31+
- CodeQL and Semgrep static-analysis workflows.
32+
- Dependabot configuration.
33+
- `SECURITY.md` with PGP-encrypted private vulnerability reporting.
34+
35+
[Unreleased]: https://github.com/jonaslejon/malicious-pdf/compare/v1.0.0...HEAD
36+
[1.0.0]: https://github.com/jonaslejon/malicious-pdf/releases/tag/v1.0.0

CONTRIBUTING.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Contributing to malicious-pdf
2+
3+
Thanks for your interest in improving this project. This document describes how
4+
to report issues, propose changes, and add new test cases.
5+
6+
## Reporting bugs and requesting enhancements
7+
8+
- File non-security issues at https://github.com/jonaslejon/malicious-pdf/issues.
9+
- Search existing issues first to avoid duplicates.
10+
- Include: command run, expected vs. actual output, Python version, OS, and the
11+
PDF viewer / processor under test if relevant.
12+
- Bug reports and enhancement requests are accepted in **English**.
13+
- Maintainers aim to acknowledge new reports within 14 days.
14+
15+
## Reporting security vulnerabilities
16+
17+
Do **not** open a public issue for security vulnerabilities. Follow the private
18+
disclosure process documented in [`SECURITY.md`](SECURITY.md). The maintainer
19+
aims for an initial response within 14 days.
20+
21+
## Pull requests
22+
23+
1. Fork the repo and branch from `main`.
24+
2. Keep changes focused — one technique or fix per PR.
25+
3. Follow the project conventions documented in [`CLAUDE.md`](CLAUDE.md),
26+
especially the rules around test numbering, the `pdf_generators` dict, and
27+
post-processing (credit + obfuscation).
28+
4. Update `README.md` (test matrix and file count) and `CHANGELOG.md` when
29+
user-visible behavior changes.
30+
5. Confirm the smoke test passes locally:
31+
```bash
32+
python3 malicious-pdf.py https://example.com
33+
ls output/ | wc -l # should match the documented test count
34+
```
35+
6. Open a PR describing **what** changed and **why**, and link any relevant
36+
CVE, advisory, or research blog post.
37+
38+
## Adding a new test case
39+
40+
Before writing code, verify the technique is not already covered (action type,
41+
JS API, trigger mechanism, transport — see `CLAUDE.md` for the full checklist).
42+
Each PDF must exercise exactly one unique technique.
43+
44+
Then:
45+
46+
1. Add `def create_malpdfN(filename, host)` before `main()`.
47+
2. Register it in the `pdf_generators` dict in `main()`.
48+
3. Update the `README.md` test matrix and the documented file count.
49+
4. Add a short entry under `## [Unreleased]` in `CHANGELOG.md`.
50+
51+
## Testing policy
52+
53+
- Every PR that adds or changes a generator must keep the smoke test green
54+
(CI runs `python3 malicious-pdf.py https://example.com` on every push and
55+
asserts the expected number of `.pdf` files is produced).
56+
- New generators should be exercised at all supported obfuscation levels
57+
(`--obfuscate 0` through `--obfuscate 4`) before merging when feasible.
58+
- Manual viewer testing (Acrobat / PDF.js / Chrome PDFium) is encouraged for
59+
any change that affects an existing validated payload — note results in the
60+
PR description.
61+
62+
## Code style
63+
64+
- Pure Python 3, no external PDF libraries — PDFs are constructed as raw
65+
strings/bytes (see `CLAUDE.md`).
66+
- Match the existing style of nearby code; no large reformatting in functional
67+
PRs.
68+
- Keep generator functions self-contained; do not apply credit or obfuscation
69+
inside generators (those are post-processing steps).
70+
71+
## License
72+
73+
By submitting a contribution you agree that it will be released under the
74+
project's [MIT License](LICENSE).

0 commit comments

Comments
 (0)