From 301f714fd15acaef48701ff9bf5bc135e274b3bf Mon Sep 17 00:00:00 2001 From: fabiodalez-dev Date: Mon, 17 Aug 2026 14:03:11 +0200 Subject: [PATCH 1/2] ci(zap): stop failing on the ISBN/EAN-13 PII-Disclosure false positive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OWASP ZAP rule 10062 (PII Disclosure) intermittently fails the packaged-app passive scan with a HIGH "credit card number" alert. The match is a book's public EAN-13 on a catalogue page (observed: 4131672754818 on /de/verlag/Puffin+Books): a 13-digit catalogue identifier inherently collides with the credit-card Luhn pattern, and which page the crawl reaches varies run to run — so the same code passes or fails at random and every release needs a re-run. Exclude rule 10062 from the blocking gate only; every other medium/high passive alert still fails the build, and real secret/PII exposure is covered by the secret-scanning, Semgrep and CodeQL jobs. --- .github/workflows/ci-browser-security.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-browser-security.yml b/.github/workflows/ci-browser-security.yml index 9aa3ccf56..1ebfea3f3 100644 --- a/.github/workflows/ci-browser-security.yml +++ b/.github/workflows/ci-browser-security.yml @@ -163,12 +163,24 @@ jobs: - name: Fail on medium or high ZAP alerts run: | test -s report_json.json || { echo "ZAP JSON report is missing"; exit 1; } - blocking=$(jq '[.site[]?.alerts[]? | select((.riskcode | tonumber) >= 2)] | length' report_json.json) + # Rule 10062 (PII Disclosure) is excluded from the blocking gate. This + # is a book catalogue: every catalogue/publisher/book page lists + # ISBN/EAN-13 identifiers, which are 13-digit numbers that inherently + # collide with the credit-card Luhn pattern ZAP's PII rule matches + # (observed: the public EAN 4131672754818 on /de/verlag/Puffin+Books + # flagged as a "credit card number"). It is a public catalogue code, + # not leaked PII, and it fires intermittently depending on which + # catalogue pages the passive crawl reaches. Real secret/PII exposure + # is covered by the secret-scanning, Semgrep and CodeQL jobs. Every + # OTHER medium/high passive alert still fails the build. + pii=$(jq '[.site[]?.alerts[]? | select((.pluginid // "") == "10062")] | length' report_json.json) + [ "$pii" -gt 0 ] && echo "note: ignoring $pii PII-Disclosure alert(s) (rule 10062) — ISBN/EAN-13 Luhn false positive" + blocking=$(jq '[.site[]?.alerts[]? | select((.riskcode | tonumber) >= 2) | select((.pluginid // "") != "10062")] | length' report_json.json) if [ "$blocking" -gt 0 ]; then - jq -r '.site[]?.alerts[]? | select((.riskcode | tonumber) >= 2) | "[\(.riskdesc)] \(.alert): \(.desc)"' report_json.json + jq -r '.site[]?.alerts[]? | select((.riskcode | tonumber) >= 2) | select((.pluginid // "") != "10062") | "[\(.riskdesc)] \(.alert): \(.desc)"' report_json.json exit 1 fi - echo "ZAP found no medium/high passive-scan alerts" + echo "ZAP found no blocking medium/high passive-scan alerts" - name: Upload browser and server diagnostics if: always() From 8802cc4966886127533039e8d075eaadd638d3a8 Mon Sep 17 00:00:00 2001 From: fabiodalez-dev Date: Mon, 17 Aug 2026 14:37:52 +0200 Subject: [PATCH 2/2] ci(zap): narrow the rule-10062 allowlist to ISBN/EAN-13 catalogue false positives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per review: don't exclude every PII-Disclosure (rule 10062) alert — a real credit-card/SSN leak must still fail. The alert is now ignored ONLY when EVERY instance is a 13-digit number (ISBN/EAN-13 length) on a bibliographic route (publisher/book/author/catalogue across the five locales). A 15/16-digit card number, or a 13-digit value on any other page (login, admin, forms), stays blocking. Verified against the real ZAP report (EAN 4131672754818 on /de/verlag/Puffin+Books → allowlisted) and two synthetic reports (a 16-digit Visa, and a 13-digit value on /admin/settings → both still blocking). --- .github/workflows/ci-browser-security.yml | 29 +++++++++++------------ 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci-browser-security.yml b/.github/workflows/ci-browser-security.yml index 1ebfea3f3..054da3054 100644 --- a/.github/workflows/ci-browser-security.yml +++ b/.github/workflows/ci-browser-security.yml @@ -163,24 +163,23 @@ jobs: - name: Fail on medium or high ZAP alerts run: | test -s report_json.json || { echo "ZAP JSON report is missing"; exit 1; } - # Rule 10062 (PII Disclosure) is excluded from the blocking gate. This - # is a book catalogue: every catalogue/publisher/book page lists - # ISBN/EAN-13 identifiers, which are 13-digit numbers that inherently - # collide with the credit-card Luhn pattern ZAP's PII rule matches - # (observed: the public EAN 4131672754818 on /de/verlag/Puffin+Books - # flagged as a "credit card number"). It is a public catalogue code, - # not leaked PII, and it fires intermittently depending on which - # catalogue pages the passive crawl reaches. Real secret/PII exposure - # is covered by the secret-scanning, Semgrep and CodeQL jobs. Every - # OTHER medium/high passive alert still fails the build. - pii=$(jq '[.site[]?.alerts[]? | select((.pluginid // "") == "10062")] | length' report_json.json) - [ "$pii" -gt 0 ] && echo "note: ignoring $pii PII-Disclosure alert(s) (rule 10062) — ISBN/EAN-13 Luhn false positive" - blocking=$(jq '[.site[]?.alerts[]? | select((.riskcode | tonumber) >= 2) | select((.pluginid // "") != "10062")] | length' report_json.json) + # OWASP ZAP rule 10062 (PII Disclosure) is allowlisted NARROWLY, never + # globally. A book catalogue renders ISBN/EAN-13 identifiers on its + # bibliographic pages, and a 13-digit code inherently collides with the + # credit-card Luhn pattern the rule matches (observed: EAN 4131672754818 + # on /de/verlag/Puffin+Books, mis-classified as "Visa" because BIN + # 413167 is a Visa range). An alert is ignored ONLY when EVERY instance + # is a 13-digit number on a bibliographic route — a real card leak + # (15/16 digits) or a 13-digit value on any other page still fails the + # build. Real secret/PII exposure is also covered by the secret-scanning, + # Semgrep and CodeQL jobs; every other medium/high alert stays blocking. + FILTER='def is_isbn_fp: (.pluginid // "") == "10062" and ([ .instances[]? | ((.evidence // "") | test("^[0-9]{13}$")) and ((.uri // "") | test("/(auteur|author|autor|autore|book|buch|catalog|catalogo|catalogue|editore|editori|genere|genre|katalog|libro|libri|livre|publisher|verlag)"; "i")) ] | (length > 0 and all)); [ .site[]?.alerts[]? | select((.riskcode | tonumber) >= 2) | select(is_isbn_fp | not) ]' + blocking=$(jq "$FILTER | length" report_json.json) if [ "$blocking" -gt 0 ]; then - jq -r '.site[]?.alerts[]? | select((.riskcode | tonumber) >= 2) | select((.pluginid // "") != "10062") | "[\(.riskdesc)] \(.alert): \(.desc)"' report_json.json + jq -r "$FILTER"' | .[] | "[" + (.riskdesc // "") + "] " + (.alert // "") + ": " + (.desc // "")' report_json.json exit 1 fi - echo "ZAP found no blocking medium/high passive-scan alerts" + echo "ZAP found no blocking medium/high passive-scan alerts (allowlisted: 13-digit ISBN/EAN codes on bibliographic pages)" - name: Upload browser and server diagnostics if: always()