The Payment Page Script Nobody Pinned — JavaScript Bug Hunt
Modelled on the British Airways data breach of 2018, attributed to the Magecart group.
- Language: JavaScript
- Layer: Frontend
- Difficulty: Medium
- Concepts: Security, Validation
- Modelled on: British Airways · 2018
- Visible tests: a script matching its pin loads; a tampered script is refused
- Reward: 50 XP for a complete fix
Briefing
Modelled on the British Airways data breach of 2018, attributed to the Magecart group. Attackers modified a JavaScript file that BA's website loaded, so that card details typed into the payment page were also sent to a server the attackers controlled. The UK Information Commissioner's Office later fined BA £20 million over the breach.
This project is a reconstruction of one defence that stops a tampered script from running: Subresource Integrity. loader.js fetches a script for the payment page along with the integrity value it was pinned to, and the locked sri.js computes a script body's integrity value (a small stand-in for SHA-256, so the project has no dependencies). The loader notices a mismatch — and loads the script anyway.
Fix loadScript so only a script whose integrity matches its pin is ever added to the page.
Bug report
BUG-BA-SRI · Priority: Critical (card data) · Reported by: security review
loadScript(page, src, integrity, fetch) — page is { scripts: [], warnings: [] }; fetch(src) returns the script body. Returns { loaded, reason }:
- integrity missing or empty -> { loaded: false, reason: "missing-integrity" }
- sri.integrityOf(body) !== integrity -> { loaded: false, reason: "integrity-mismatch" }
- otherwise push { src, body } onto page.scripts and return { loaded: true, reason: "" }
A refused script is NEVER added to page.scripts.
Observed: a modified vendor script produced a warning and was executed on the payment page.
Logs
[checkout] integrity mismatch for /cms/js/modernizr-2.6.2.min.js (expected sri-4be1…, got sri-9c07…)
[checkout] scripts loaded: 14 (0 refused)The code as shipped
src/checkout/loader.js (editable)
var sri = require("./sri");
exports.loadScript = function (page, src, integrity, fetch) {
var body = fetch(src);
if (integrity && sri.integrityOf(body) !== integrity) {
page.warnings.push("integrity mismatch for " + src);
}
page.scripts.push({ src: src, body: body });
return { loaded: true, reason: "" };
};
Read-only context: src/checkout/sri.js.
Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More JavaScript bug hunts.