Skip to content

Latest commit

 

History

History
159 lines (140 loc) · 10.4 KB

CI_SECURITY.md

File metadata and controls

159 lines (140 loc) · 10.4 KB

CI and security operations

Required coverage and trust boundaries

Java CI gate requires the clean JDK 17 reactor build, JSP/Jakarta source parity, CI policy tests, the Docker/Selenium browser test, exact reactor JAR inclusion in the Jakarta WAR. Packaged consumer gate requires JDK 17 artifact preparation/API and Java 8 signature checks, package guard tests, the original packaged bytes on Java 8/11/17/21/25, and core/JSP unit tests with the Java 8 JVM and coverage. JDK 21/25 build probes remain advisory. Require both gates: neither observes the other workflow. The gates run with always() and accept only success for every expected dependency; missing, failed, skipped and cancelled jobs fail.

Both workflows run on pushes to main, pull requests, weekly schedules and manual dispatch, with bounded timeouts and per-workflow/ref concurrency cancellation. There are no path filters that could silently omit a required check. Changes to workflow topology must update the gate dependencies and required check names only after the replacement checks have succeeded.

Every checkout disables credential persistence. PRs use pull_request, never pull_request_target or privileged workflow_run execution. Fork tokens are read-only and receive no repository secrets; all external contributors require run approval. A fork's artifacts are consumed only within that same unprivileged run. Artifact download has no cross-run/repository/token input, preserving the original packaged bytes. No privileged job restores PR artifacts or caches.

All Maven invocations use a fresh runner-temporary repository. Shared Maven caching is deliberately disabled, including Java 8's necessary install, all scanner builds, and intentional release commits. Setup Java's pinned v6.1.0 does expose cache controls, but this configuration does not rely on any of them. See local quarantine guidance.

Baseline main runs Java CI 36220505798 and consumers 36220505783 had 14 Maven cache misses and one hit (Java 8 install job). Build took 3m14s; preparation took 85s; Java 8 tests 87s; runtimes 12–16s. The separate clean test-compilation and install lifecycles are now one clean verify lifecycle.

Scanning and dependency updates

Advanced CodeQL in codeql.yaml is the single analysis owner; leave default setup unconfigured. Java uses a manual JDK 17 build of all three libraries and the optional testJakarta application; Actions and Python tooling use extraction without a build. It runs on PRs, main, a weekly schedule and manual dispatch. Only analysis jobs request security-events: write; fork PRs use GitHub's restricted PR SARIF upload path, not a general write token. Reports are retained as artifacts and in Code scanning. No scanner secrets are required.

Dependency submission runs only for the trusted default branch, including manual dispatch. Its only elevated permission is job-level contents: write for the snapshot API. It builds its own checkout without caches or imported artifacts. Separate correlators submit the normal reactor and the optional Jakarta profile. The pinned Maven submission action includes all resolved project scopes, including runtime, test and provided dependencies. Maven dependency plugin 3.11.0 resolve-plugins separately resolves the source-controlled allowlist of plugins actually invoked by verification, consumer installation, metadata checks, and release staging. The optional Jakarta app is resolved from its own smaller package-gate allowlist, so inherited but inactive Boot plugin-management entries are not submitted. The disabled Site plugin is likewise not represented as an executed dependency. The five separately invoked consumer dependency fixtures inherit a local toolchain parent which pins the downloader's plugin realm; that parent is resolved separately so any drift from the submitted root closure remains visible. scripts/build-dependency-snapshot.py submits those closures as development dependencies, once for shared root tooling and only the differing plugin closures for child or fixture POMs. Graph reports and submission JSON are retained for inspection. Inspect representative Jakarta Spring/Tomcat dependencies in the resulting graph; alert counts are not gates.

The immutable 1.6.0 Java Encoder artifacts used by japicmp are intentional development-only comparison inputs. Keep them visible in the build graph: an alert on a baseline artifact describes that historical input, not a dependency shipped to current development consumers, and must be assessed rather than hidden with a graph filter.

All submissions use detector encoder-maven-build-graph with distinct, stable correlators. Keep the action's detector inputs synchronized with the Python build snapshot: GitHub merges correlators from the same detector, but selects between different detectors for a POM. Different detectors can hide runtime dependencies behind build-only results despite successful submissions. Check the final SBOM after both matrix jobs finish, including runtime versions and development dependencies together, not just the snapshot API status.

Dependabot checks all current library POMs, the parent, optional app and compatibility fixture toolchain parent weekly, with separate Maven and SHA-pinned Actions groups and grouped Maven security updates. Normal review and complete CI apply to automated PRs; no automatic merging is configured. Review new action source and transitive downloads as well as pins. Do not dismiss alerts merely to reduce the count. Correct versions or graph semantics, submit the new graph, and let GitHub close packages that are no longer present. Baseline-sensitive API, JSP-engine and build-plugin dependencies and the Maven distribution used by the wrapper are excluded from the broad Maven version-update group, so unsuppressed proposals receive individual review. Reviewed incompatible minor and major proposal classes use ignore.update-types; GitHub applies those rules only to version updates, so security updates remain eligible and the security-update group is unchanged. Dependabot classifies repeated Maven coordinates from their lowest occurrence; where historical and current inputs share a coordinate, a current-line patch can therefore fall inside a suppressed update class. Review those coordinates manually during dependency/release maintenance and for every advisory. Revisiting a suppressed version class requires a deliberate compatibility change and removal or narrowing of its rule. See dependency decisions for the exact scopes, current contracts, PR dispositions and conditions for reconsideration. The nonstandard XML files under compatibility/dependencies remain explicit manual compatibility fixtures. In particular Felix 5.6.12 is an intentional OSGi R6/Java 8 baseline, not a production dependency; the Maven ignore prevents an automatic baseline replacement. Inspect any advisory against its actual local bundle-loading test scope, and record a specific disposition. Do not suppress advisories across all Felix versions or application deployments.

The retired encoder-esapi module is absent from Dependabot configuration and the submitted 1.5 dependency graph. Its historical artifacts are not rewritten; see the retirement notice. Optional Scorecard publication, best-practices registration and another scheduled scanner are follow-ups, not prerequisites for these operating controls.

Repository controls and recovery

Before changes, snapshot repository/ruleset, Actions, Pages, collaborator/team and webhook settings. Store webhook configuration privately; do not publish endpoint tokens. Record changes and negative tests in issue #169.

Use read-only default Actions tokens, no bot PR approvals, supported secret scanning/push protection, all-external-contributor run approval, and an allowlist of the exact action repositories/paths in the action inventory. Enable SHA enforcement after pins and the allowlist are ready. Re-read both Actions endpoints afterward: updating the general permissions endpoint may reset github_owned_allowed to true; reapply the exact allowlist and test rejection of an unlisted GitHub-owned action as well as a mutable action tag. When adding a reviewed action, update the allowlist before merging the workflow. Old open PRs must refresh their workflows before rerunning under the SHA policy.

Main requires both complete CI gates and successful CodeQL language jobs in a separate ruleset with no bypass. The review rule keeps one independent approval, dismisses stale approvals, requires approval of the latest push, and requires resolution of review threads. The verified maintainers Jim Manico and Jeremy Long have only PR-based, audit-visible emergency review bypass; it cannot bypass required checks or directly push main. An emergency bypass is not independent approval. CODEOWNERS enforcement remains with #127's ownership validation; do not name unverified owners or treat review rules as CODEOWNERS validation.

All tags prohibit update/deletion with no bypass; new signed release tags still follow RELEASING.md. GitHub required_signatures checks commit signatures, not annotated tag signatures. The September validation records disabling the legacy Codacy and Travis webhooks, with their private configuration retained for recovery. Maven Site sources and publishing are retired; the implicit Site lifecycle remains explicitly pinned and disabled.

For recovery, an administrator uses Settings or the REST API, records the actor, reason and affected PR in #169, makes the narrowest temporary change, then restores and verifies the controls. Do not introduce privileged PR workflows to repair policy failures. Check names can be repaired without removing review requirements; action patterns can be added without disabling SHA enforcement. There is no standing direct-push bypass. Never move a published tag or blanket disable tag protection. A disposable unpublished policy-test tag can be removed using an exclusion for that exact tag only, then immediately removing the exclusion. Review branch cleanup against current main and open PRs individually.