Skip to content

Commit 14d3bf5

Browse files
authored
chore: OWASP ASVS v9 (#92)
1 parent 382df27 commit 14d3bf5

8 files changed

Lines changed: 1060 additions & 22 deletions

File tree

.github/scripts/asvs-gate.sh

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/usr/bin/env bash
2+
# OWASP ASVS V12 re-affirmation gate.
3+
#
4+
# Fails the PR if TLS-relevant code changed without an updated audit-log
5+
# entry in docs/security/owasp-asvs.md.
6+
#
7+
# TLS-relevance predicate (OR):
8+
# - changed file matches src/main/java/com/retailsvc/http/internal/.*(Ssl|Tls|Https).*\.java
9+
# - any changed .java file under src/main/java/ adds or removes a line
10+
# importing javax.net.ssl.* or com.sun.net.httpserver.Https*
11+
#
12+
# Local invocation:
13+
# BASE_SHA=$(git merge-base origin/master HEAD) HEAD_SHA=HEAD .github/scripts/asvs-gate.sh
14+
15+
set -euo pipefail
16+
17+
: "${BASE_SHA:?BASE_SHA env var required}"
18+
: "${HEAD_SHA:?HEAD_SHA env var required}"
19+
20+
CHECKLIST="docs/security/owasp-asvs.md"
21+
22+
changed_files=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA")
23+
24+
tls_paths=$(printf '%s\n' "$changed_files" \
25+
| grep -E '^src/main/java/com/retailsvc/http/internal/.*(Ssl|Tls|Https).*\.java$' || true)
26+
27+
import_diff=$(git diff -U0 "$BASE_SHA" "$HEAD_SHA" -- 'src/main/java/**/*.java' \
28+
| grep -E '^[+-]import (javax\.net\.ssl\.|com\.sun\.net\.httpserver\.Https)' || true)
29+
30+
if [ -z "$tls_paths" ] && [ -z "$import_diff" ]; then
31+
echo "ASVS gate: no TLS-relevant changes."
32+
exit 0
33+
fi
34+
35+
triggers=""
36+
if [ -n "$tls_paths" ]; then
37+
triggers+=" (path) $(echo "$tls_paths" | tr '\n' ' ')"$'\n'
38+
fi
39+
if [ -n "$import_diff" ]; then
40+
triggers+=" (import) $(echo "$import_diff" | head -5)"$'\n'
41+
fi
42+
43+
if ! printf '%s\n' "$changed_files" | grep -qx "$CHECKLIST"; then
44+
cat >&2 <<EOF
45+
::error title=ASVS V12 gate::TLS-relevant code changed but $CHECKLIST was not updated.
46+
47+
Triggered by:
48+
$triggers
49+
Required action:
50+
1. Open $CHECKLIST
51+
2. Confirm each ASVS 5.0 L2 control still holds (update Status / Evidence rows if not)
52+
3. Append a dated line to ## Audit log, e.g.:
53+
- **$(date -u +%Y-%m-%d)** — Re-affirmed after change to <file>.java (this PR); all controls hold
54+
55+
This gate exists so TLS changes can't silently drift away from the documented controls.
56+
See $CHECKLIST for the policy.
57+
EOF
58+
exit 1
59+
fi
60+
61+
added_audit_line=$(git diff "$BASE_SHA" "$HEAD_SHA" -- "$CHECKLIST" \
62+
| grep -E '^\+- \*\*[0-9]{4}-[0-9]{2}-[0-9]{2}\*\* — ' || true)
63+
64+
if [ -z "$added_audit_line" ]; then
65+
cat >&2 <<EOF
66+
::error title=ASVS V12 gate::$CHECKLIST was updated but no new audit-log line was added.
67+
68+
Triggered by:
69+
$triggers
70+
The gate requires a new line in the ## Audit log section matching:
71+
- **YYYY-MM-DD** — <free-text re-affirmation, e.g. "Re-affirmed after change to X.java (this PR); all controls hold">
72+
73+
Touch-only changes to the checklist do not satisfy the gate. The dated line must be added.
74+
EOF
75+
exit 1
76+
fi
77+
78+
echo "ASVS gate: TLS-relevant changes re-affirmed in $CHECKLIST."
79+
exit 0

.github/workflows/asvs-gate.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: OWASP ASVS gate
2+
3+
on:
4+
pull_request:
5+
branches: [master]
6+
types: [opened, synchronize, reopened]
7+
8+
permissions:
9+
contents: read
10+
pull-requests: read
11+
12+
jobs:
13+
asvs-checklist:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v6
17+
with:
18+
fetch-depth: 0
19+
20+
- name: Enforce ASVS re-affirmation on TLS code changes
21+
env:
22+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
23+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
24+
run: .github/scripts/asvs-gate.sh

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=extenda_openapi-httpserver-java&metric=code_smells&token=c87f52089c6158081787f26e272d0a0e412c205b)](https://sonarcloud.io/dashboard?id=extenda_openapi-httpserver-java)
66
[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=extenda_openapi-httpserver-java&metric=duplicated_lines_density&token=c87f52089c6158081787f26e272d0a0e412c205b)](https://sonarcloud.io/dashboard?id=extenda_openapi-httpserver-java)
77
[![WorkFlow](https://github.com/extenda/openapi-httpserver-java/actions/workflows/commit.yaml/badge.svg)](https://github.com/extenda/openapi-httpserver-java/actions)
8+
[![OWASP ASVS](https://img.shields.io/badge/OWASP_ASVS_5.0-Level_2_V12-blueviolet)](docs/security/owasp-asvs.md)
89

910
A lightweight Java library that wraps the JDK's `com.sun.net.httpserver.HttpServer` and serves
1011
endpoints declared in an OpenAPI 3.1.x specification. Handlers are pure functions registered by

docs/security/owasp-asvs.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# OWASP ASVS 5.0 Level 2 — self-assessment
2+
3+
**Standard:** OWASP Application Security Verification Standard, version
4+
5.0.0, chapter V12 (Secure Communication).
5+
**Level:** 2 (typical baseline; consumers needing L3 must layer
6+
additional controls).
7+
**Scope:** Server-side TLS termination via `Builder.https(...)`.
8+
Outbound / service-to-service controls (V12.3) are N/A — the library
9+
makes no outbound TLS connections on the consumer's behalf.
10+
11+
**Wording rules.** This document is a self-assessment; it does NOT
12+
claim certification or compliance. The README badge says "ASVS 5.0
13+
Level 2" only — never "Certified", never "Compliant". OWASP does not
14+
issue conformance certifications.
15+
16+
**Re-affirmation rule.** Any change to TLS-related code (see
17+
`.github/scripts/asvs-gate.sh` for the exact predicate) MUST append a
18+
dated entry to the [Audit log](#audit-log) in the same PR. CI
19+
enforces this. The contributor writes the line — automated bumps are
20+
not accepted.
21+
22+
**Status legend.**
23+
24+
- ✅ Implemented — the library satisfies this control end-to-end
25+
- 🤝 Delegated — consumer must satisfy; see Evidence for guidance
26+
- ⛔ N/A — out of scope for a server-side TLS termination library
27+
- 📋 Future — accepted as a gap; tracked for a follow-up release
28+
29+
## Controls
30+
31+
| ID | Control (verbatim, ASVS 5.0) | Level | Status | Evidence |
32+
|---|---|---|---|---|
33+
| 12.1.1 | "Verify that only the latest recommended versions of the TLS protocol are enabled, such as TLS 1.2 and TLS 1.3." | L1 | ✅ Implemented | `TlsHttpsConfigurator` pins `setProtocols({"TLSv1.3","TLSv1.2"})`; verified end-to-end by `OpenApiServerHttpsIT#negotiatesTls13`. |
34+
| 12.1.2 | "Verify that only recommended cipher suites are enabled, with the strongest cipher suites set as preferred." | L2 | 📋 Future | We rely on JDK 25 defaults (which exclude RC4, 3DES, EXPORT, NULL, anonymous suites by default). A curated allowlist with explicit preference order is tracked as a follow-up. |
35+
| 12.1.3 | "Verify that the application validates that mTLS client certificates are trusted before using the certificate identity." | L2 | ⛔ N/A | mTLS is not supported in v1. `TlsHttpsConfigurator` explicitly sets `setNeedClientAuth(false)` and `setWantClientAuth(false)`. If mTLS lands later, this row flips to Implemented + new evidence. |
36+
| 12.1.4 | "Verify that proper certification revocation, such as Online Certificate Status Protocol (OCSP) Stapling, is enabled." | L3 | 📋 Future | Out of scope for L2 baseline. Documented so L3-targeting consumers know the gap. |
37+
| 12.1.5 | "Verify that Encrypted Client Hello (ECH) is enabled in the application's TLS settings to prevent exposure of sensitive metadata." | L3 | 📋 Future | Out of scope for L2 baseline. JDK 25 has no stable ECH API. |
38+
| 12.2.1 | "Verify that TLS is used for all connectivity between a client and external facing, HTTP-based services, and does not fall back to insecure communications." | L1 | ✅ Implemented | When `.https(...)` is configured, the server binds `HttpsServer` only; no plaintext fallback listener is created. Mixed-mode (HTTP + HTTPS) is a documented non-goal — operators run two `OpenApiServer` instances if they need both. |
39+
| 12.2.2 | "Verify that external facing services use publicly trusted TLS certificates." | L1 | 🤝 Delegated | The library accepts whatever cert chain the consumer supplies. For production deployments, consumers MUST point `.https(certChain, privateKey)` at a chain signed by a publicly-trusted CA. See satisfaction guidance below. |
40+
| 12.3.1 – 12.3.5 | (Service-to-service / outbound) | L2 | ⛔ N/A | The library does not initiate outbound TLS on the consumer's behalf. Consumers' outbound HTTP clients are their own responsibility. |
41+
42+
### Delegated control: satisfaction guidance
43+
44+
**12.2.2.** Operators should point `.https(certChain, privateKey)` at
45+
a chain issued by a publicly trusted CA. The recommended workflow is
46+
certbot / Let's Encrypt with the PEM files mounted from a secret
47+
manager (see README §HTTPS for the full deployment pattern). The
48+
library performs no chain validation on its own certificate — the
49+
server merely presents the chain — so it is on the operator to ensure
50+
publicly-trusted issuance.
51+
52+
## Audit log
53+
54+
- **2026-05-21** — Initial ASVS 5.0 Level 2 mapping for V12 controls. All listed controls accepted as Implemented / Delegated / N/A / Future as tabulated above.

0 commit comments

Comments
 (0)