Chapter 14 — Application and software supply chain security: methods I
Lesson map
Lesson 14 published. Map, example, EX040.
flowchart TD
N0["Learning outcomes"]
N1["14.1 Why uploads matter"]
N2["14.2 The checks"]
N3["Lab — upload checks"]
N4["14.3 Lab result"]
N5["14.4 Testing"]
N6["Reviewing the rules"]
N7["Shared module"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7
N7 --> EX["Worked example"]
EX --> Q["Exercise EX040"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["14.1 Why uploads matter"]
N2["14.2 The checks"]
N3["Lab — upload checks"]
N4["14.3 Lab result"]
N5["14.4 Testing"]
N6["Reviewing the rules"]
N7["Shared module"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7 --> Q["EX040"]
[Learning outcomes]
|
v
[14.1 Why uploads matter]
|
v
[14.2 The checks]
|
v
[Lab — upload checks]
|
v
[14.3 Lab result]
|
v
[14.4 Testing]
|
v
[Reviewing the rules]
|
v
[Shared module]
|
v
[Worked example] --> [Exercise EX040]
Learning outcomes
You will (1) validate file uploads safely; (2) check type, content and size; (3) store uploads safely; (4) test the checks with synthetic files.
14.1 Why uploads matter
The Greenleaf flaw was in upload handling. Even with a patched library, Brightwave wants its own checks so that a future library flaw is less likely to be reachable. OWASP ASVS 4.0.3 section V12 covers files and resources. The January flaw showed that Brightwave relied entirely on upload-kit to handle uploads safely. Adding its own checks means the next library flaw would have to get past Brightwave's rules as well, which makes the flaw less likely to be reachable from the public booking form.
14.2 The checks
Allow only listed types (PNG, JPEG, PDF); check that the file content matches the claimed type by its leading bytes; limit size to 5 MB; give stored files new random names; store them outside the web root and serve them with a safe download header. Each of these checks addresses a different problem with uploaded files. The type allowlist rejects files the clinic never needs, the content check rejects files whose leading bytes do not match their name, the size limit protects storage and processing, and random names stop an upload from choosing where it lands.
Lab — upload checks
"""Validate synthetic uploaded files: extension allowlist, size limit and content signature."""
SIG = {".png": b"\x89PNG", ".pdf": b"%PDF"}
files = [("xray.png", b"\x89PNG....", 120_000), ("report.pdf", b"%PDF-1.7", 900_000),
("notes.pdf", b"PK\x03\x04", 40_000), ("scan.png", b"\x89PNG....", 9_000_000), ("run.exe", b"MZ..", 10_000)]
for name, head, size in files:
ext = name[name.rfind("."):]
if ext not in SIG: verdict = "reject: type not allowed"
elif size > 5_000_000: verdict = "reject: too large"
elif not head.startswith(SIG[ext]): verdict = "reject: content does not match type"
else: verdict = "accept"
print(f"{name:11} {verdict}")
Run with python3 lab_upload.py. Verified output:
xray.png accept
report.pdf accept
notes.pdf reject: content does not match type
scan.png reject: too large
run.exe reject: type not allowed
14.3 Lab result
xray.png and report.pdf are accepted. notes.pdf is rejected because its content does not match a PDF. scan.png is rejected as too large. run.exe is rejected because the type is not allowed. Every rejection names its reason, which helps support staff explain it to clinic users. The order of checks matters: run.exe is rejected on type before its content is ever examined. The lab's signature table covers only PNG and PDF, so JPEG support would need its own entry before JPEG uploads could be accepted.
14.4 Testing
Synthetic test files cover each rule. Tests run in the build on every change. For each rule there is at least one file that should pass and one that should fail, mirroring the five lab files. A change that loosens a rule therefore fails the build, and the reviewer sees exactly which test broke. The synthetic files are tiny and contain no real patient data. Diego adds a test whenever a support case shows a file the rules handled wrongly.
14.5 Limits
Content checks reduce risk but cannot prove a file is harmless; storage and serving rules provide the second layer. A PDF with correct leading bytes can still contain unexpected content, so a passing check is not a guarantee. That is why accepted files are stored outside the web root with new names and served as downloads, so even a harmful file is never run by the server or rendered as part of the site. The second layer is what protects clinics if a future content check misses something new.
Reviewing the rules
The allowed types and size limit are reviewed with each client, since clinics, museums and cafes need different files. Every change to the rules goes through review with updated tests.
Shared module
The checks live in one shared module used by all client projects, so a fix reaches everyone at the next deployment.
Worked example
After the checks go live, the Greenleaf support log shows clinic users occasionally uploading oversized phone photos. Brightwave adds a clear message suggesting how to reduce size instead of raising the limit.
Synthetic case. Files are synthetic.
Errors and limits
Keep the allowed list short; every added type adds risk.
Exercises
- EX040 — Lab rejections. Why was each file rejected? Rubric: 3 = notes mismatch; scan too large; run.exe type; 2 = two; 1 = one; 0 = none.
- EX041 — Second layer. What protects if a checked file is still harmful? Rubric: 3 = storage outside web root; safe serving; random names; 2 = partial; 1 = vague; 0 = none.
- EX042 — Oversize response. How was oversized photo uploads handled? Rubric: 3 = clear message, not raised limit; 2 = partial; 1 = vague; 0 = none.
Synthesis
Validate uploads on type, content and size, and store them safely.
Continue studying — chapter 14 of 30
← Chapter 13 (draft) · Course chapter index · Chapter 15 (draft) →