diff --git a/.github/CI_SECURITY.md b/.github/CI_SECURITY.md index 1b62d41..a8ab2ce 100644 --- a/.github/CI_SECURITY.md +++ b/.github/CI_SECURITY.md @@ -55,7 +55,7 @@ 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.9.0 `resolve-plugins` separately resolves build/report plugins and their +3.11.0 `resolve-plugins` separately resolves build/report plugins and their transitives; `scripts/build-dependency-snapshot.py` submits those edges as development dependencies. Graph reports and submission JSON are retained for inspection. Inspect representative ESAPI/AntiSamy HTTP transitives and Jakarta diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index cbf3b4c..0310a72 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -42,7 +42,7 @@ jobs: - name: Test CI policy boundaries run: python3 -m unittest discover -s scripts/tests - name: Verify clean reactor including the required Docker/browser test - run: mvn -B -ntp clean verify -PtestJakarta 2>&1 | tee build.log + run: ./mvnw -B -ntp clean verify -PtestJakarta 2>&1 | tee build.log - name: Confirm the Jakarta application contains this reactor's exact JAR run: | python3 - <<'PY' @@ -66,6 +66,8 @@ jobs: **/target/surefire-reports/ **/target/failsafe-reports/ **/target/jsp-engine/ + **/target/site/jacoco/ + **/target/checkstyle-result.xml jakarta-test/target/packaged-war.log esapi-compatibility: @@ -100,7 +102,7 @@ jobs: java-version: '17' distribution: 'temurin' - name: Test ESAPI compatibility - run: mvn -B -ntp -pl esapi -am verify -Desapi.version=${{ matrix.esapi-version }} + run: ./mvnw -B -ntp -pl esapi -am verify -Desapi.version=${{ matrix.esapi-version }} gate: name: Java CI gate diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml index b92d881..99086a7 100644 --- a/.github/workflows/codeql.yaml +++ b/.github/workflows/codeql.yaml @@ -52,7 +52,7 @@ jobs: build-mode: ${{ matrix.build-mode }} - name: Compile all libraries and the optional Jakarta application if: matrix.language == 'java-kotlin' - run: mvn -B -ntp clean package -PtestJakarta -DskipTests + run: ./mvnw -B -ntp clean package -PtestJakarta -DskipTests - uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 with: category: /language:${{ matrix.language }} diff --git a/.github/workflows/consumer-compatibility.yaml b/.github/workflows/consumer-compatibility.yaml index f037944..45eb97f 100644 --- a/.github/workflows/consumer-compatibility.yaml +++ b/.github/workflows/consumer-compatibility.yaml @@ -37,7 +37,7 @@ jobs: distribution: temurin java-version: '17' - name: Verify libraries, Java 8 API signatures, and public API compatibility - run: mvn -B -ntp clean verify 2>&1 | tee build.log + run: ./mvnw -B -ntp clean verify 2>&1 | tee build.log - name: Prepare isolated packaged consumers run: python3 compatibility/consumers.py prepare --repository "$RUNNER_TEMP/m2" 2>&1 | tee prepare.log - name: Verify artifact guards reject broken packages @@ -58,6 +58,7 @@ jobs: **/target/failsafe-reports/ **/target/jsp-engine/ **/target/japicmp/ + **/target/site/jacoco/ runtime: name: Packaged consumers on Java ${{ matrix.java }} @@ -114,9 +115,9 @@ jobs: 8 17 - name: Build with JDK 17 - run: mvn -B -ntp -DskipTests install -pl core,jsp,esapi -am 2>&1 | tee build.log + run: ./mvnw -B -ntp -DskipTests install -pl core,jsp,esapi -am 2>&1 | tee build.log - name: Run unit tests and collect coverage with Java 8 - run: mvn -B -ntp -pl core,jsp,esapi jacoco:prepare-agent@prepare-agent surefire:test -Djvm="$JAVA_HOME_8_X64/bin/java" 2>&1 | tee java8-tests.log + run: ./mvnw -B -ntp -pl core,jsp,esapi jacoco:prepare-agent@prepare-agent surefire:test jacoco:report -Djvm="$JAVA_HOME_8_X64/bin/java" 2>&1 | tee java8-tests.log - name: Confirm Java 8 execution and coverage in every tested module run: | for module in core jsp esapi; do @@ -140,6 +141,9 @@ jobs: core/target/surefire-reports/ jsp/target/surefire-reports/ esapi/target/surefire-reports/ + core/target/site/jacoco/ + jsp/target/site/jacoco/ + esapi/target/site/jacoco/ core/target/jacoco.exec jsp/target/jacoco.exec esapi/target/jacoco.exec @@ -166,7 +170,7 @@ jobs: distribution: temurin java-version: ${{ matrix.java }} - name: Verify libraries on a newer build JDK - run: mvn -B -ntp clean verify 2>&1 | tee build.log + run: ./mvnw -B -ntp clean verify 2>&1 | tee build.log - name: Preserve build diagnostics including compiler warnings if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 @@ -178,11 +182,36 @@ jobs: **/target/failsafe-reports/ **/target/jsp-engine/ **/target/japicmp/ + **/target/site/jacoco/ + + wrapper: + name: Maven wrapper on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + with: + distribution: temurin + java-version: '17' + - name: Bootstrap Unix wrapper and reject wrong distribution hash + if: runner.os != 'Windows' + run: python3 scripts/check-wrapper.py + - name: Bootstrap Windows wrapper and reject wrong distribution hash + if: runner.os == 'Windows' + shell: pwsh + run: python scripts/check-wrapper.py gate: name: Packaged consumer gate if: ${{ always() }} - needs: [prepare, runtime, java8-unit-tests] + needs: [prepare, runtime, java8-unit-tests, wrapper] runs-on: ubuntu-latest timeout-minutes: 5 steps: @@ -192,4 +221,4 @@ jobs: - name: Require artifact preparation, every runtime and Java 8 unit tests env: NEEDS: ${{ toJSON(needs) }} - run: python3 scripts/check-ci-gate.py prepare runtime java8-unit-tests + run: python3 scripts/check-ci-gate.py prepare runtime java8-unit-tests wrapper diff --git a/.github/workflows/dependency-submission.yaml b/.github/workflows/dependency-submission.yaml index 2000849..a871ffb 100644 --- a/.github/workflows/dependency-submission.yaml +++ b/.github/workflows/dependency-submission.yaml @@ -58,7 +58,7 @@ jobs: - name: Resolve build plugins and their dependencies env: PROFILE: ${{ matrix.profile }} - run: mvn -B -ntp ${PROFILE:+"$PROFILE"} org.apache.maven.plugins:maven-dependency-plugin:3.9.0:resolve-plugins -DoutputFile=target/build-dependencies.txt + run: ./mvnw -B -ntp ${PROFILE:+"$PROFILE"} org.apache.maven.plugins:maven-dependency-plugin:3.11.0:resolve-plugins -DoutputFile=target/build-dependencies.txt - name: Submit build graph env: GH_TOKEN: ${{ github.token }} @@ -66,6 +66,14 @@ jobs: run: | python3 scripts/build-dependency-snapshot.py --correlator "encoder-build-$GRAPH" --output target/build-snapshot.json gh api --method POST "repos/$GITHUB_REPOSITORY/dependency-graph/snapshots" --input target/build-snapshot.json + - name: Resolve and submit the release plugin graph without signing or publishing + if: matrix.graph == 'libraries' + env: + GH_TOKEN: ${{ github.token }} + run: | + ./mvnw -B -ntp -Psign-artifacts dependency:resolve-plugins -DoutputFile=target/build-dependencies.txt + python3 scripts/build-dependency-snapshot.py --correlator encoder-build-release --output target/release-build-snapshot.json + gh api --method POST "repos/$GITHUB_REPOSITORY/dependency-graph/snapshots" --input target/release-build-snapshot.json - name: Preserve resolved graphs if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 @@ -74,4 +82,4 @@ jobs: path: | **/target/*dependency*.json **/target/build-dependencies.txt - target/build-snapshot.json + target/*build-snapshot.json diff --git a/.mvn/maven.config b/.mvn/maven.config new file mode 100644 index 0000000..7fa4242 --- /dev/null +++ b/.mvn/maven.config @@ -0,0 +1 @@ +--strict-checksums diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..a3c407f --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,4 @@ +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip +distributionSha256Sum=5af3b743dd8b876b5c45da33b676251e5f1687712644abb4ee519ca56e1d89ce diff --git a/BUILDING.md b/BUILDING.md new file mode 100644 index 0000000..a0d5857 --- /dev/null +++ b/BUILDING.md @@ -0,0 +1,101 @@ +# Building and checking Java Encoder + +Use JDK 17+ and the committed `./mvnw` (`mvnw.cmd` on Windows). The wrapper is +Apache Maven Wrapper 3.3.4's unmodified `only-script` distribution; no wrapper +JAR executes before the download check. It pins Maven 3.9.16 and its SHA-256. +The downloaded Maven ZIP was independently compared to the SHA-512 served at +[Apache's distribution site](https://downloads.apache.org/maven/maven-3/3.9.16/binaries/). +Wrapper archive SHA-256: `6cb584c2bc907b849a0b931d8266d3ff3214cdd3127115ed4f49fb7176413d36`. +Both bootstrap paths are CI gates, including a deliberately wrong checksum with +an empty wrapper cache. `.mvn/maven.config` requires strict transfer checksums. +These checks detect corruption against the recorded hash; checksums fetched from +the same publisher do not independently establish publisher identity. Review +wrapper scripts, distribution URLs and hashes together when upgrading. + +```sh +./mvnw -B -ntp clean verify +./mvnw -B -ntp clean verify -PtestJakarta # requires Docker +python3 compatibility/consumers.py prepare --repository /path/to/isolated/m2 +python3 compatibility/consumers.py run --runtime 17 +``` + +Run from the root, with `-pl core -am`, or from a module using `../mvnw`. +The wrapper's `.mvn` root also anchors Checkstyle paths. Maven's JVM must be 17+ +(Maven 3.9.16+); Java 8 is a **forked unit-test/consumer JVM**, never the build JVM. +Newer JDK build jobs remain advisory. Library class files retain releases 8/9. +The five library POMs share one Enforcer execution: tool minimums, duplicate +coordinates, dependency convergence, upper bounds and explicit plugin versions. +The separate Boot application applies those rules under its own parent/BOM. + +## Source policy + +Checkstyle plugin 3.6.0 with engine 12.3.1 checks main sources during validate: +headers, whitespace/newlines, illegal/redundant/unused imports, empty statements, +equals/hashCode and file/type names. No method-size rule forces encoder-loop +refactoring. Test sources remain out of scope. Checkstyle's current 13/14 engine +requires Java 21; 12.3.1 is the explicit Java 17 compatibility exception, not a +claim of upstream support for older engines. Review migration when the build JDK +changes. Its parser cannot parse module declarations, so `module-info.java` is +excluded from Checkstyle; compiler, packaged descriptor/source guards and actual +JPMS consumers cover it. Headers were added to those four descriptors and four +app files using their 2024 Jeremy Long introduction commits. All existing BSD +notices and original attribution remain. TLD license comments remain intact; +JSP server-side comments do not emit output. The app declares the same BSD license. + +## Coverage + +Normal `verify` writes HTML/XML/CSV in each library's `target/site/jacoco` and +checks per-module floors. The empty aggregator has no classes/data and is skipped. +Coverage measures unit-test execution only. Failsafe packaged/OSGi consumers and +forked JSP engines deliberately have no agent; neither do isolated runtime jobs. +Surefire uses late evaluation of both the prepared agent and caller `argLine`. +Empty defaults support `-Djacoco.skip=true`. Appending execution data intentionally +unions successive unit JVMs in one build; use `clean` for an independent baseline. +CI's Java 8 run starts in its own job/cache and uploads its own reports/data. + +| Module | Measured lines | Measured branches | Line floor | Branch floor | +| --- | --- | --- | --- | --- | +| core | 1228/1240 (99.032%) | 890/903 (98.560%) | 99.0% | 98.5% | +| jsp | 66/66 | no branches | 100% | 100% | +| jakarta | 66/66 | no branches | 100% | 100% | +| esapi | 27/28 (96.429%) | no branches | 96.4% | 100% | + +Floors round the current baseline down to 0.1 percentage points. They are not a +claim that every encoding behavior is covered. CI retains reports alongside test +results. Changes that intentionally alter these baselines require reviewed evidence. + +## Retired Maven Site + +The old Site/Reflow/Velocity/Doxia, FindBugs, PMD, JXR and report-only bindings +were dormant. Unique core/JSP examples moved to [docs/usage.md](docs/usage.md). +README/module docs and attached source/Javadoc JARs remain supported. Build-bound +Checkstyle/JaCoCo, Surefire XML, CodeQL and dependency submission replace useful +reports. Maven's implicit Site plugin is pinned to 3.22.0 and skipped to prevent +falling back to an old lifecycle default; `mvn site` is not a publishing path. +Existing GitHub Pages and the `gh-pages` branch remain available and unchanged. + +## Dependency signature trust decision + +Mandatory dependency/plugin PGP verification is deferred. An arbitrary key +retrieved on first use is not trusted merely because a signature verifies. A +future enforced policy needs reviewed full expected fingerprints per publisher, +rotation/revocation handling, expiring unsigned exceptions and a trusted verifier +bootstrap. A non-failing trial would only collect observations. No current rule +claims to verify the provenance of dependencies, plugins, Maven bootstrap or +extensions that execute before a verifier could run. Today the boundaries are +reviewed pins/checksums, TLS repositories, isolated caches and dependency/advisory +review. Release artifact PGP verification is a separate policy in RELEASING.md. + +The measured regression probe ran only `EncodeFacadeTest` with fresh execution +data: core line coverage fell to 76.0% and branch coverage to 61.5%; both checks +failed. A separate clean `-Djacoco.skip=true` verify passed without a test-agent +argument error. The full baseline data was kept separate from that probe. + +Lifecycle pins are in root `pluginManagement`, with explicit versions on API, +source-helper and signature plugins. Maven 4 prerelease plugins were deliberately +not selected for this Maven 3 build. The optional app inherits maintained plugin +pins from Boot 4.1.1 and adds explicit Enforcer/Checkstyle/disabled Site pins. +Review effective POMs for normal, `testJakarta`, and `sign-artifacts` profiles; +`dependency:resolve-plugins` feeds their resolved closures into dependency review. +Release-only publisher dependencies are submitted separately with the same +GitHub detector and a distinct correlator, so they do not hide runtime graphs. diff --git a/KEYS b/KEYS index 53621c2..b0196ca 100644 --- a/KEYS +++ b/KEYS @@ -67,3 +67,88 @@ rWQe0hepCDvS9Fen96HEc9d/cEFQrocwgJDBOEQOi2QLuFLGrpoUa+qoCtRFW7tN eNJraOZb9Q== =BuF1 -----END PGP PUBLIC KEY BLOCK----- + +Historical public keys +---------------------- +These keys are archival, not authorized for new releases. See VERIFYING.md for +observed version mapping, expiry and source authentication. The current project +key above remains unchanged. + +Versions: 1.2.2-1.2.3 +Fingerprint: F9514E84AE3708288374BBBE097586CFEA37F9A6 +Authentication record: Jeremy Long's OWASP Dependency-Check v6.0.0 CLI verification guide (2020) + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBFngHVEBCACWyS2Lm1Y+ia/xKYmGsYXH9Rz5i37p58GWjOmtMP1mAxh5o98M +OQV63K4/mn5uLUzkSMchIMemNVdmYqsQfA5ONJNooeqQSpGzjDOJt8RvylDNWrPs +z1QIAHc4eDCIf5qcutRom/qhKMm0IfGpyIz16TgkD6lwQdRwu/VswTQKJlabYsVJ +9Amz6xNif1BJjZLsOVAaCxuoptkfBl+vqWoXnMJAPu5m7HyrBuTry+EpKPX5vSLz +g7h1wnmF1CjOtp06Gcav+6otaZsjlpSvDKZVcUXkbnF+I4+jIowu60hB7TnHiu4w +27EbNxmYKM0F9he+hSgz7DyBR3OnS9QkPTbXABEBAAG0I0plcmVteSBMb25nIDxq +ZXJlbXkubG9uZ0BnbWFpbC5jb20+iQFUBBMBCAA+AhsDBQsJCAcCBhUICQoLAgQW +AgMBAh4BAheAFiEE+VFOhK43CCiDdLu+CXWGz+o3+aYFAl2kS0IFCQeGlPEACgkQ +CXWGz+o3+aas1wf+Ls3bTyhR4aZJMNqSEK8G+jPueZ0nmq9UiyxE5p7ACyYzZVqK +r7L/inIWqLwOtoT1OjwlfvPBUNzXuuqkCpPx8+hEJYJ1d0fpHVFXiOLxvm0ze3hT +qFYQnDRa7Qu/lgh4zQaxbXhTqUlQYEwxRr1pfsci3LKG1oRYS6igR1q6I5uUn8ou +Wb1iWr12fyKFOZsFxxFPMbufIsmf+USYk/JuuZUvE/9ThkFIY/xsuL6vZcbSFXgR +JCuPJXZ+n6nEnZJ1f/QFMQjlS27+rD1qHyo9YxXYJntigThpOBZBZbNJoYZ2+PXs +i905Gj57ytDHb3SSHxNNFeMJ/jSoxG06d8JzIQ== +=ueyh +-----END PGP PUBLIC KEY BLOCK----- + +Versions: 1.3.0-1.4.0 +Fingerprint: 259A55407DD6C00299E6607EFFDE55BE73A2D1ED +Authentication record: Existing Java Encoder KEYS record at b51c575 and Dependency-Check CLI guide + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGFewZgBEADauMBSYbKoa+A/uKbDFJsK6p7I7ff+DbcIntTzbR2mq/0fTr1Z +tylAn/NwizCyU+tsdJ94iVVfTPgZZad+ruOUlCmjrk4K6pOvOP943p5fUb58xJSN +xLi2sK/rtSS4cAEjNYlpeJTM47iDLZQQefKc7OCjNvXomtt+TXV0cqXTtrXgWURi +UN5tAjl8pzXCoXZarj8b69m09o8k8S13u+FjjMy/m/JK4jxogNOqiw8ZieIqcCId +BlFGB+AH1+zZzRrxdtoFtw/3Jr4kqjk6dsAvgdPa6LAl5HLuqlZB0tch48orYIm/ +X6SWEtnkgwta1i3YxKPyJz3yD3kbKOoNVtPFWegN8fwVZeNiDxe4smfPw5JXWUgH +OqlAzqRRRg7jqomhKmVPc4Y+ZnP/87Bq9nVaY1Y52DvFWOVjELpqH7xVRONJ2gtL +tR4yJ6GCXhscpREK8Gn1qb/TebuFsycxKyMFRI1+AHHWlsBShl9/I1tYKOQaCd3K +CrDOnKejll1ZBoLg4r9i5bAKG4B9w8PCXXEGgxqSwKpZU3rZpz067NP8s83iE4Uw +x5jvQE3YyWmWhLgETriq0bdXdNJw3EliAJK9a8rhyOO5HGt4nrvHNGkOSYGqxIVj +QbebDkUW9a/MOAUNaPCQcgEOqx/H3XsRa8a/RvDxeXAOMDtMa0UJC/83iQARAQAB +tCNKZXJlbXkgTG9uZyA8amVyZW15LmxvbmdAZ21haWwuY29tPokCTgQTAQgAOAIb +AwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgBYhBCWaVUB91sACmeZgfv/eVb5zotHt +BQJo6N/XAAoJEP/eVb5zotHt8psQAMdRPsSD3ksx35ziNfBaguiDzfCKp/8SJeAQ +4bIBJ08oA/Jgvb+UNTrqORMIFrcH/J0Lt4o6FK6rZTj1BUVVD2dEGmoxbJSjzNgk +ifI6IwqJY1VCpPC2VvAT2dw82QOvojiQFnzYLmJq1yZ0ybUZuB6Feqx9bxBvtEyY +hwn+tKnHMozJjJ0MBZwjcJ4GVtisX+KnKXC1fdX1fxaISyG+/ARH3edhc9sa8ltj +wDkgEevADt39reo7IqjN6H4CtDA+cRZ68OPwVAQuPg9AgYh5WLfR/FNJwXR6Xz67 +JRBbAyI1BIAylf3NYMoloyRwZQyoxIbtz9BeecWpLfgT8xfzNH+lWbXUsjp61yfX +lP6lD5qmegjKhMIbjmLYyRpc2q9RtJvYFzQqsYMbVRUlLKdqCD/vHdOp3jexcx6x +1RSy1iwiAOJebGZVONJOQDbQX0WVkupCemspT9q8imy0X7SI/xcuIXaoc9Ltp4Rl +yD7R10fv/sOIv92ELzMmVrgzd5Y2V2/iB674Qh6s4eoTQJq1rQXadSSCth1vuJpb +Gjg7P1mkkY7OoLiPJ8eu0vDDOnOM55Kq9rjOkDZZ5qRPDEEJAMw9rJnhYxiDScnD +Iht+flc9ut5N4q9n29/QqrAxADTLYF69lGmA0yK2Jl4tfVEnc7/C+I71mGEHcRni +15WgZtdsuQINBGFewZgBEADeRdiuWJ5fyJYufTpNKn/CSNRUdDc1305glAVu0yON +H4WgmcCA2KZ+C0VsqKcOm1tGFjEuctCL6ATTHlgmG7Jem4ANIl2pNHike0iLY2ej +D0MfETe5+eK/BcWZ+Paslnu4Myy1umYIznVTPUDS35jiJilY2fMbZYgc/MtAtX9s +EX6YF9tO/k7cCUnic+KDnXWz0OmN9YQNJchboHoBuIn8srrLsfIVlokWYylLW3nX +dAaVOc1wAi9YsjHpAPBWgCXPArkiEGPxlBP1n9ujwyP84MVA1TMv/Wq3VcmmNIPj +uigtfNYwjoos9OrvHFCTJ9RkULwW0Bwxs0uB18dph+JLs8zVz8FmgyXz/90St8ZO +JXPcVeoSHotw/98BQioOTpur21mPS/Rv25FLHdMpnHHNTxCS01t8DzkYGq4T9sse +n4aEWD3xpuYBEoHUHuEqf59STscYlG8ENx3UJlO75SxcHmKzmnZplX0ms9Xvi4jR +baVP1w2DMyFjW3fqZFkVHO8EhywIAPI/GaJPdoAQVfFLgX5MOxRLFnqAEX69tskB +e2Y1IvU41pgFxmF4jxwMsnhG4TbpjSd0INp3A3IhCMK3ATGnrEMQ7eiyfOh7fNv8 +OcbcOmCzsVHGFKOXsj2vH28fjMLgaSBNtIRXJxxmzUb9w4qO37Lciktr6iMYGZz1 +8QARAQABiQI2BBgBCAAgAhsMFiEEJZpVQH3WwAKZ5mB+/95VvnOi0e0FAmjo3/MA +CgkQ/95VvnOi0e2g6w//ag3IWPlRJb7YGW7ocdvRd3agDEgeDrqyYYdqUQVhr6xG +RAx8Xy6/IfRkJdbDUhbAWy2qGjWwH1s87gAjvx8CMNcXusD9g7sH9lUfQWlMOfS5 +UdlIpk6s1WZHcPsk1OFayO2yvsKNaPe8R+IwGiAV8YwwjXx73neS9Rpis+TOPSrL +lxv2WnDdWAKuOkmq58e/c5nBAEmWR/Li1FVCdog22sUDiLG6MJOXn4djlJTuWu6u +WJh9hIa+cIXue8H/FYyZoRFGWeNUy7pYyHFFfJeOcgPSs7jcLFtZWm8UqiPYItTp +SOFWUjt4sw5tcop1dQvrV/myVciARWy2IO50+EANe6b2g+lPUQKgonbdeyYC/44J +HgYSdss7Gif1H6QWZa4XEoCg3/LGLPPzOELrHWJ5TnHg/G/RoBTcFjLPXtz78Asv +nFP/LUMvn3nXdhFpUt9eB1JtrFRr85BYt/5m3fcA4v7WbMBgxMIMH71OscsWPl6o +lFw5z2PSk6Qm+zLmImrQkeuA/k3dxj68EeiNToEchL6UrqPZWtnS6Vw3VTWXEYNQ +VBAkgMpPsSeEBt75ivT4mGiEfoH5otmJZXUmyf3ZWrJmlpQMwObN3y3Vpr3p8Czh +YPqwlkjUBzDstzThJfdz1MGxy0KS8fxFmg0UjFjwMBri4o9Sl4amrpedxzCfc9M= +=gwVj +-----END PGP PUBLIC KEY BLOCK----- diff --git a/README.md b/README.md index 49b8761..b0067a0 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,8 @@ Happy Encoding! Security -------- Please report suspected vulnerabilities privately. See [SECURITY.md](SECURITY.md) for -the reporting channels, supported versions, and scope. +the reporting channels, supported versions, and scope. Verify downloads using the +[public release keys](KEYS) and [signature/checksum guide](VERIFYING.md). Building -------- @@ -94,13 +95,13 @@ If a future javac removes `--release 8`, a runtime baseline change requires a future-major-version decision; it does not change the 1.x baseline. Simply run: ```shell -mvn package +./mvnw verify ``` To validate that the Jakarta JSP tags and EL functions work correctly, run the integration test: ```shell -mvn verify -PtestJakarta +./mvnw verify -PtestJakarta ``` The integration test requires a running Docker-compatible container runtime. @@ -208,7 +209,7 @@ Development The OWASP Java Encoder project is a multi-module Maven project: ```bash -$ mvn package +$ ./mvnw verify ``` See [RELEASING.md](RELEASING.md) for signing, Maven Central publication, and release verification. @@ -291,3 +292,5 @@ We're happy to announce that version 1.1.1 has been released. Along with an impo ### 2013-02-14 - Version 1.1 released We're happy to announce that version 1.1 has been released. Along with a few minor encoding enhancements, we improved performance, and added a JSP tag and function library. + +Build policy, wrapper provenance, source checks, and coverage are documented in [BUILDING.md](BUILDING.md). The migrated [Java/JSP examples](docs/usage.md) preserve the former Maven Site usage guide. diff --git a/RELEASING.md b/RELEASING.md index 61488db..d86285f 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -48,8 +48,11 @@ existing Maven version. ## Prepare and verify -1. Use a clean checkout and JDK 17. Record the full Git commit, JDK distribution - and version, and Maven version. Build with a fresh Maven local repository so +1. Use a clean checkout, **Eclipse Temurin 17.0.20.1+1** and the committed + wrapper (**Maven 3.9.16**). The signing profile enforces vendor, exact runtime + and Maven versions, and a non-SNAPSHOT project version. Ordinary builds allow + JDK 17+; the release path is stricter because javac output changes on newer + JDKs. Record the full Git commit and operating system/architecture. Build with a fresh Maven local repository so locally installed artifacts cannot hide dependency or version errors. 2. Apply reviewed security fixes privately until publication is ready. 3. Set the version in the root POM, each library module's parent POM, and @@ -62,9 +65,15 @@ existing Maven version. 4. Update README dependency examples, the security policy's supported versions, and the release notes. Include security advisories, compatibility changes, all Maven coordinates, signing fingerprint, and verification commands. -5. Run `mvn -B -ntp -Dmaven.repo.local= clean verify`. - This checks unit tests and the packaged OSGi/JPMS consumers. With a running - Docker-compatible runtime, also run `mvn -B -ntp +5. Record a reviewed `project.build.outputTimestamp` (UTC timestamp of the + prepared release sources) in the release POM. Never use build wall-clock time. + Set `TZ=UTC`, `LC_ALL=C`, `LANG=C`, and + `MAVEN_OPTS="-Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8"`. + Run `./mvnw -B -ntp -Dmaven.repo.local= clean verify`. + This checks unit tests, coverage, source policy, API signatures and the + in-reactor OSGi/JPMS tests. Then run the isolated consumer commands in + [BUILDING.md](BUILDING.md) on the original JARs. With a running + Docker-compatible runtime, also run `./mvnw -B -ntp -Dmaven.repo.local= verify -PtestJakarta`. 6. Commit the release files before tagging. Verify the four binary JARs, their source and Javadoc JARs, and five POMs. The optional `jakarta-test` WAR is not a @@ -77,7 +86,7 @@ settings file using a Central Portal token. Select the full project-key fingerprint and dedicated GnuPG home explicitly: ```sh -mvn -B -ntp -s /private/path/settings.xml \ +./mvnw -B -ntp -s /private/path/settings.xml \ -Dmaven.repo.local=/private/path/release-cache \ -DperformRelease=true \ -Dgpg.homedir=/private/path/project-gnupg \ @@ -85,7 +94,27 @@ mvn -B -ntp -s /private/path/settings.xml \ clean deploy ``` -Use the GnuPG agent to unlock the project key. The current POM uses +Use the GnuPG agent to unlock the project key before starting the batch command; +GPG plugin 3.2.8 enables `bestPractices` and does not accept passphrases in POMs or +command-line properties. Noninteractive signing must fail when the key/agent is +unavailable; do not work around failure by skipping signatures for a production +bundle. Run `clean verify -DperformRelease=true` first to test signing without +deployment. For a local bundle, run signed `clean verify` first, then: + +```sh +python3 scripts/package-release.py --output /private/path/release-bundle.zip \ + --gnupg-home /private/path/project-gnupg \ + --fingerprint 1C5F632B86809F2F5DB25092BEA0075F94074A9B +``` + +The assembler verifies all seventeen signatures against that expected fingerprint, +checks the signed POMs match the source POMs, excludes the optional WAR, generates +four checksum types and refuses to overwrite an existing bundle. It never builds, +signs or uploads. Do **not** use `skipPublishing=true` as a bundle-generation +command: despite upstream documentation, 0.11.0 filters out every artifact before +bundling. It also requires a `central` server settings entry even in that mode; +the isolated validation used dummy values and confirmed no upload occurred. +The publisher extension loads only in the signing profile. The current POM uses `autoPublish=false`: deployment stages the release and does not publish it. Inspect the deployment in Central Portal and resolve validation errors before selecting **Publish**. @@ -159,3 +188,28 @@ rules do not verify annotated tag signatures: run `git verify-tag v` and check the project key fingerprint separately. Repository recovery and the limited, audit-visible emergency PR review bypass are documented in [CI/security operations](.github/CI_SECURITY.md#repository-controls-and-recovery). + + +## Reproduce the unsigned payload + +Use the reference toolchain above, the exact immutable source commit, and its +recorded timestamp. `python3 scripts/check-reproducible.py --commit +--directory ` exports that commit twice, uses separate fresh +Maven repositories, builds the twelve binary/source/Javadoc JARs and installs the +five POMs locally, then compares all seventeen files directly by SHA-256. It never +signs or uploads. The initial experiment is recorded in the batch 04 validation +record. Compare the same source revision, never a different historical release. +OS/architecture, locale, archive permissions and the JDK distribution/version are +part of the recorded reference environment; cross-platform byte identity is not +claimed without a separate comparison. Signatures contain signing-time data and +are verified separately from the deterministic unsigned payload. Never rebuild or +replace the retained 1.4.1 release to retrofit reproducibility. + +The publisher's isolated dependencies override Jackson core/databind 2.22.3 and +annotations 2.22, HttpClient 5.6.4 and HttpCore/httpcore5-h2 5.4.4. The upstream +0.11.0 dependency versions matched current OSV advisories; these six reviewed +replacement coordinates did not on 2026-09-26. Local signed bundle validation +exercises the overridden plugin. This is not an audit of every plugin dependency +or a claim that the live Central HTTP path has been tested. Namespace access and +a validated-then-dropped rehearsal remain tracked by #111. `autoPublish=false` +stays mandatory. The optional WAR is excluded and its install/deploy goals skip. diff --git a/VERIFYING.md b/VERIFYING.md new file mode 100644 index 0000000..fec330e --- /dev/null +++ b/VERIFYING.md @@ -0,0 +1,102 @@ +# Verify downloaded release artifacts + +Use the **full expected fingerprint**, authenticated through the project's release +record, alongside the signature. A keyserver is a public-key transport, not an +identity authority. Obtain [KEYS](KEYS) from a trusted project revision and check +the fingerprint before use. Never import private key material to verify a release. + +For 1.4.1 and later releases until a documented rotation, the expected project key +is `1C5F632B86809F2F5DB25092BEA0075F94074A9B`. The [1.4.1 release instructions](releases/1.4.1.md#verification) +cover its signed GitHub assets while Central publication remains pending. + +## Fresh public-only keyring + +Download the artifact, its original `.asc`, and the trusted `KEYS`. This example +verifies historical 1.4.0 to demonstrate the process; it is not a recommendation +to use that affected release. Substitute the expected project fingerprint and +artifact name for 1.4.1. Commands use GnuPG and `shasum` (or equivalent SHA tools). + +```sh +verify_home=$(mktemp -d) +chmod 700 "$verify_home" +gpg --homedir "$verify_home" --batch --import KEYS +expected_fingerprint=259A55407DD6C00299E6607EFFDE55BE73A2D1ED +artifact=encoder-1.4.0.jar +gpg --homedir "$verify_home" --fingerprint "$expected_fingerprint" +gpg --homedir "$verify_home" --batch --status-fd 1 \ + --verify "$artifact.asc" "$artifact" > signature.status || exit 1 +awk -v expected="$expected_fingerprint" \ + '$2 == "VALIDSIG" && ($3 == expected || $NF == expected) { valid=1 } + END { exit !valid }' signature.status || exit 1 +# After verification, remove this disposable public-only keyring. +rm -r "$verify_home" +``` + +`VALIDSIG` supplies the signing fingerprint and, for a subkey, its primary key's +fingerprint. An untrusted-owner warning is expected in a fresh keyring; do not +mark arbitrary keys trusted to hide it. Expired/revoked-key warnings require an +explicit historical review, not a blanket bypass. A signature establishes that +the bytes were signed by the selected key; it does not prove the software is safe. + +## Checksums: two distinct formats + +A Maven `.sha256` sidecar usually contains **only a hex digest**, not a filename. +After fetching it over the intended distribution channel, form a check manifest: + +```sh +artifact=encoder-1.4.0.jar +expected_hash=$(tr -d '[:space:]' < "$artifact.sha256") +printf '%s\n' "$expected_hash" | grep -Eq '^[[:xdigit:]]{64}$' || exit 1 +printf '%s %s\n' "$expected_hash" "$artifact" | shasum -a 256 --check || exit 1 +``` + +Use the analogous 128-digit check with `shasum -a 512` for `.sha512`. Old Central +versions may offer only MD5/SHA-1 sidecars; do not invent a stronger upstream +checksum or treat those as signature substitutes. In contrast, a release's +`SHA256SUMS` already contains filenames. Verify `SHA256SUMS.asc` with the expected +key as above, then run `shasum -a 256 --check SHA256SUMS`. The same applies to +`SHA512SUMS` with `-a 512`. A checksum alone, especially from the same download +origin, does not authenticate a publisher. + +## Historical signing-key evidence + +Reviewed 2026-09-26 against the original core JARs and detached signatures at +[Maven Central](https://repo.maven.apache.org/maven2/org/owasp/encoder/encoder/). +Every listed original signature mathematically verified in a fresh public-only +keyring; the pre-1.3 keys are now expired. This does not retrospectively authorize +those keys or change any artifact. The table records the primary fingerprint, +not a short key ID, and release-use periods rather than all possible key uses. + +| Releases | Observed primary fingerprint | Authentication/archival status | +| --- | --- | --- | +| 1.1 | `37D880CD406BAD34CA2A8DD61845EF37A3B6533A` | Expired; independent historical full-fingerprint authorization record still missing | +| 1.1.1 | `AD0C981AEE36D3880512E28F5AD6F7C8740E3CF2` | Expired; independent authorization record still missing | +| 1.2 | `C82AF58D3985677F9D575CEC9BC190E3DA071BD4` | Expired; independent authorization record still missing | +| 1.2.1 | `33F28D32BAB335D03EC5DAD6F7EBA8ECD6F22BFE` | Expired; independent authorization record still missing | +| 1.2.2–1.2.3 | `F9514E84AE3708288374BBBE097586CFEA37F9A6` | Expired 2021-10-13; archived in KEYS | +| 1.3.0, 1.3.1, 1.4.0 | `259A55407DD6C00299E6607EFFDE55BE73A2D1ED` | Historical personal key; archived in KEYS | +| 1.4.1 onward until rotation | `1C5F632B86809F2F5DB25092BEA0075F94074A9B` | Current dedicated project key | + +The 1.2.2–1.2.3 fingerprint is authenticated by Jeremy Long's contemporaneous +[OWASP Dependency-Check v6.0.0 verification guide](https://github.com/dependency-check/DependencyCheck/blob/b7040668cdc83976bb3f4e11d65e49f2a00d7ac4/cli/src/site/markdown/index.md.vm), +which records the full fingerprint; its public key verifies both Encoder releases. +The 1.3.0–1.4.0 fingerprint was already recorded in this project's +[KEYS at the rotation](https://github.com/OWASP/owasp-java-encoder/blob/b51c575/KEYS) +and is corroborated by the [maintainer's Dependency-Check guide](https://dependency-check.github.io/DependencyCheck/dependency-check-cli/index.html). +Retrieved keys were checked against those full records before adding minimal +public exports to KEYS. The first four keys were retrieved only to analyze the +signatures; they are **not** added to trusted archival KEYS without the missing +historical/project authorization records. This is the remaining evidence gap in +#110. Do not resolve it by treating keyserver availability or a matching UID as +project authorization. + +The three oldest signatures use SHA-1 and 1.1 uses a 1024-bit DSA key; these are +historical facts, not algorithms to use for new releases. Preserve their original +bytes/signatures. Current key custody, independent recovery and Central access +remain [MAINTAINERS.md](MAINTAINERS.md) / #111. Record a new authorized project's +full fingerprint before first use and retain the old public verification record. + +OpenPGP detached artifact signing is separate from Java `jarsigner`: it signs the +whole downloaded file without adding JAR entries. This project does not claim +that its JARs carry Java code-signing certificates or that a PGP signature creates +a Java runtime trust decision. diff --git a/compatibility/README.md b/compatibility/README.md index 490f44b..01d1d3d 100644 --- a/compatibility/README.md +++ b/compatibility/README.md @@ -30,7 +30,7 @@ JDK 17. The isolated packaged consumer job covers those four artifacts on Java 8 ## What runs -`consumers.py prepare` copies the four JARs produced by `mvn clean verify`, resolves +`consumers.py prepare` copies the four JARs produced by `./mvnw clean verify`, resolves the pinned fixture dependencies, and compiles consumers independently of reactor classes or test classpaths. Core consumers assert String and Writer output, including input that crosses internal buffer boundaries, plus JavaScript and URI @@ -81,7 +81,7 @@ classes must stay within each artifact's own package; test classes and embedded JARs are rejected. Negative fixture tests verify representative broken packages fail these guards. -During ordinary `mvn verify`, Animal Sniffer checks each library against the Java 8 +During ordinary `./mvnw verify`, Animal Sniffer checks each library against the Java 8 API signature. This catches linkage such as Java 9's covariant `CharBuffer.flip()` even when bytecode still has class version 52. japicmp checks public/protected API binary and source compatibility against **1.4.0**, the latest available Central @@ -110,14 +110,14 @@ and successful runtime matrix, including an actual Java 8 run, before publicatio With JDK 17, Maven, and Python 3.8+ on PATH: ```sh -mvn -B -ntp clean verify +./mvnw -B -ntp clean verify python3 compatibility/consumers.py prepare python3 -m unittest discover -s compatibility/tests python3 compatibility/consumers.py run --runtime 17 --java-home "$JAVA_HOME" python3 compatibility/consumers.py run --runtime 8 --java-home /path/to/jdk8 ``` -Preparation requires an empty `target/compatibility`; use `mvn clean verify` or +Preparation requires an empty `target/compatibility`; use `./mvnw clean verify` or choose a new `--directory` when rebuilding. `--maven` and `--repository` allow an explicit Maven executable and isolated dependency cache. Runtime `--directory` must point to the prepared fixture directory. CI uploads build reports, API diff diff --git a/compatibility/consumers.py b/compatibility/consumers.py index 4aee51e..3b1756e 100644 --- a/compatibility/consumers.py +++ b/compatibility/consumers.py @@ -164,7 +164,7 @@ def source_metadata(kind, source_jar): def prepare(args): out = args.directory.resolve() if out.exists() and any(out.iterdir()): - raise ValueError('Preparation requires an empty directory; run mvn clean verify or choose a new --directory: ' + str(out)) + raise ValueError('Preparation requires an empty directory; run ./mvnw clean verify or choose a new --directory: ' + str(out)) out.mkdir(parents=True, exist_ok=True) shutil.copytree(ROOT / 'compatibility/config', out / 'config', dirs_exist_ok=True) ns = {'p': 'http://maven.apache.org/POM/4.0.0'} @@ -199,7 +199,7 @@ def prepare(args): for kind in ('jsp', 'jakarta', 'esapi', 'osgi-r6', 'osgi-r8', 'legacy-core'): run(args.maven, '-B', '-ntp', '-f', ROOT / 'compatibility/dependencies' / (kind + '.xml'), '-Dmaven.repo.local=' + str(args.repository.resolve()), - 'org.apache.maven.plugins:maven-dependency-plugin:3.9.0:copy-dependencies', + 'org.apache.maven.plugins:maven-dependency-plugin:3.11.0:copy-dependencies', '-DincludeScope=runtime', '-DoutputDirectory=' + str(out / 'dependencies' / kind)) for kind, (artifact, explicit, automatic, package, main) in ARTIFACTS.items(): deps = sorted((out / 'dependencies' / kind).glob('*.jar')) @@ -282,7 +282,7 @@ def consume(args): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('action', choices=['prepare', 'run']) parser.add_argument('--directory', type=Path, default=ROOT / 'target/compatibility') - parser.add_argument('--maven', default='mvn') + parser.add_argument('--maven', default=str(ROOT / ('mvnw.cmd' if os.name == 'nt' else 'mvnw'))) parser.add_argument('--repository', type=Path, default=Path.home() / '.m2/repository') parser.add_argument('--runtime', type=int, choices=[8, 11, 17, 21, 25], default=17) parser.add_argument('--java-home', default=os.environ.get('JAVA_HOME')) diff --git a/compatibility/jsp-engine/README.md b/compatibility/jsp-engine/README.md index 8deca2b..267c51e 100644 --- a/compatibility/jsp-engine/README.md +++ b/compatibility/jsp-engine/README.md @@ -1,6 +1,6 @@ # Packaged taglibs through real JSP engines -Normal JDK 17 `mvn clean verify` runs this Docker-free fixture after each adapter +Normal JDK 17 `./mvnw clean verify` runs this Docker-free fixture after each adapter has been packaged. It deploys only the packaged core and adapter JARs into a loopback-only temporary web application. Jasper discovers their actual TLDs, compiles generated JSPs and serves HTTP responses. No install is necessary. @@ -46,7 +46,7 @@ scriptlet passing Java null directly to the tag's String setter. Every tag must also reject a nonempty body and an omitted required value at JSP translation time. A generic HTTP 500 does not suffice: the response must identify a Jasper exception and the expected constraint. Generated sources and engine -work directories remain under `target` for diagnosis; `mvn clean` removes them. +work directories remain under `target` for diagnosis; `./mvnw clean` removes them. This fixture proves binding, compilation, coercion and emitted bytes. It does not prove that an arbitrary use of those bytes is safe in a browser context. diff --git a/core/src/main/java9/module-info.java b/core/src/main/java9/module-info.java index fabb12a..bf2b8a2 100644 --- a/core/src/main/java9/module-info.java +++ b/core/src/main/java9/module-info.java @@ -1,3 +1,37 @@ +// Copyright (c) 2024 Jeremy Long +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above +// copyright notice, this list of conditions and the following +// disclaimer. +// +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// * Neither the name of the OWASP nor the names of its +// contributors may be used to endorse or promote products +// derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + module owasp.encoder { exports org.owasp.encoder; } diff --git a/src/site/markdown/index.md b/docs/usage.md similarity index 66% rename from src/site/markdown/index.md rename to docs/usage.md index 0d3adb0..1178a12 100644 --- a/src/site/markdown/index.md +++ b/docs/usage.md @@ -1,4 +1,4 @@ -## OWASP Java Encoder Project +# Basic Java and JSP usage The OWASP Java Encoder Project is a collection of high-performance low-overhead contextual encoders, that when utilized correctly, is an effective tool in @@ -8,17 +8,19 @@ Scripting (XSS). Please see the [OWASP XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html) for more information on preventing XSS. +See the [README](../README.md) for the current distribution status, Jakarta coordinates, and full context guidance. Version 1.4.1 is retained as signed release assets while Central publication is pending; use those exact artifacts until Central availability is confirmed. + ### Usage In addition to the usage guidance below, more examples can be found on the [OWASP Java Encoder project page](https://owasp.org/www-project-java-encoder/). -The JARs can be found in [Maven Central](https://central.sonatype.com/namespace/org.owasp.encoder). +Before using these coordinates, [download and verify the signed 1.4.1 release](../releases/1.4.1.md#verification) and install its artifacts in your local or organizational repository, as described in the [README](../README.md#start-using-the-owasp-java-encoders). These examples depend on that installation while Central publication is pending. ```xml org.owasp.encoder encoder - 1.4.0 + 1.4.1 ``` @@ -42,7 +44,7 @@ includes tags and a set of JSP EL functions: org.owasp.encoder encoder-jsp - 1.4.0 + 1.4.1 ``` diff --git a/esapi/pom.xml b/esapi/pom.xml index 9a7fc4e..cc4d2a4 100644 --- a/esapi/pom.xml +++ b/esapi/pom.xml @@ -55,6 +55,8 @@ + 0.964 + 1.000 2.7.0.0 org.owasp.encoder.esapi @@ -91,24 +93,6 @@ - - org.apache.maven.plugins - maven-enforcer-plugin - 3.6.3 - - - enforce-esapi-dependency-convergence - - enforce - - - - - - - - - org.apache.maven.plugins maven-compiler-plugin diff --git a/esapi/src/main/java9/module-info.java b/esapi/src/main/java9/module-info.java index f40d9c0..45cf4c6 100644 --- a/esapi/src/main/java9/module-info.java +++ b/esapi/src/main/java9/module-info.java @@ -1,3 +1,37 @@ +// Copyright (c) 2024 Jeremy Long +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above +// copyright notice, this list of conditions and the following +// disclaimer. +// +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// * Neither the name of the OWASP nor the names of its +// contributors may be used to endorse or promote products +// derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + module owasp.encoder.esapi { requires transitive esapi; requires owasp.encoder; diff --git a/jakarta-test/README.md b/jakarta-test/README.md index 4fb3317..6cdcb8a 100644 --- a/jakarta-test/README.md +++ b/jakarta-test/README.md @@ -4,7 +4,7 @@ This optional application is a test fixture, not a dependency of an encoder library. From the repository root, with JDK 17, Maven and Docker available: ```sh -mvn -B -ntp -Dmaven.repo.local=/tmp/encoder-browser-m2 clean verify -PtestJakarta +./mvnw -B -ntp -Dmaven.repo.local=/tmp/encoder-browser-m2 clean verify -PtestJakarta ``` Use an empty task-specific Maven directory for fresh validation. The reactor diff --git a/jakarta-test/pom.xml b/jakarta-test/pom.xml index a64daf7..7d3ef34 100644 --- a/jakarta-test/pom.xml +++ b/jakarta-test/pom.xml @@ -14,7 +14,13 @@ war jakarta-test Test for OWASP encoder jakarta JSP + + The BSD 3-Clause Licensehttps://opensource.org/license/bsd-3-clauserepo + + true + true + true 17 11.0.26 @@ -88,8 +94,60 @@ + + org.apache.maven.pluginsmaven-site-plugin3.22.0 + truetrue + jakarta-test + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.6.0 + + + com.puppycrawl.tools + checkstyle + + 12.3.1 + + + + ${maven.multiModuleProjectDirectory}/src/main/config/checkstyle.xml + ${maven.multiModuleProjectDirectory}/src/main/config/checkstyle-header.txt + + module-info.java + false + false + + ${project.basedir}/src/main/java + ${project.basedir}/src/main/java9 + + + + + main-source-policy + validate + check + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.6.3 + fixture-build-policyenforce + + [3.9.16,) + [17,) + + + + clean,verify,install,deploy + + + org.apache.maven.plugins maven-failsafe-plugin diff --git a/jakarta-test/src/main/java/org/owasp/encoder/testing/jakarta_test/JakartaTestApplication.java b/jakarta-test/src/main/java/org/owasp/encoder/testing/jakarta_test/JakartaTestApplication.java index 9c0c237..be582a7 100644 --- a/jakarta-test/src/main/java/org/owasp/encoder/testing/jakarta_test/JakartaTestApplication.java +++ b/jakarta-test/src/main/java/org/owasp/encoder/testing/jakarta_test/JakartaTestApplication.java @@ -1,3 +1,37 @@ +// Copyright (c) 2024 Jeremy Long +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above +// copyright notice, this list of conditions and the following +// disclaimer. +// +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// * Neither the name of the OWASP nor the names of its +// contributors may be used to endorse or promote products +// derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + package org.owasp.encoder.testing.jakarta_test; import org.springframework.boot.SpringApplication; diff --git a/jakarta-test/src/main/java/org/owasp/encoder/testing/jakarta_test/controller/HomeController.java b/jakarta-test/src/main/java/org/owasp/encoder/testing/jakarta_test/controller/HomeController.java index 8b36a25..953a11d 100644 --- a/jakarta-test/src/main/java/org/owasp/encoder/testing/jakarta_test/controller/HomeController.java +++ b/jakarta-test/src/main/java/org/owasp/encoder/testing/jakarta_test/controller/HomeController.java @@ -1,3 +1,37 @@ +// Copyright (c) 2024 Jeremy Long +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above +// copyright notice, this list of conditions and the following +// disclaimer. +// +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// * Neither the name of the OWASP nor the names of its +// contributors may be used to endorse or promote products +// derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + package org.owasp.encoder.testing.jakarta_test.controller; import org.springframework.stereotype.Controller; @@ -16,4 +50,4 @@ public class HomeController { public String index() { return "index"; } -} \ No newline at end of file +} diff --git a/jakarta-test/src/main/java/org/owasp/encoder/testing/jakarta_test/controller/ItemController.java b/jakarta-test/src/main/java/org/owasp/encoder/testing/jakarta_test/controller/ItemController.java index 7824cde..7fa7f4c 100644 --- a/jakarta-test/src/main/java/org/owasp/encoder/testing/jakarta_test/controller/ItemController.java +++ b/jakarta-test/src/main/java/org/owasp/encoder/testing/jakarta_test/controller/ItemController.java @@ -1,3 +1,37 @@ +// Copyright (c) 2024 Jeremy Long +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above +// copyright notice, this list of conditions and the following +// disclaimer. +// +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// * Neither the name of the OWASP nor the names of its +// contributors may be used to endorse or promote products +// derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + package org.owasp.encoder.testing.jakarta_test.controller; import java.util.List; diff --git a/jakarta-test/src/main/java/org/owasp/encoder/testing/jakarta_test/dto/Item.java b/jakarta-test/src/main/java/org/owasp/encoder/testing/jakarta_test/dto/Item.java index 0c1c72b..0ed2ecc 100644 --- a/jakarta-test/src/main/java/org/owasp/encoder/testing/jakarta_test/dto/Item.java +++ b/jakarta-test/src/main/java/org/owasp/encoder/testing/jakarta_test/dto/Item.java @@ -1,3 +1,37 @@ +// Copyright (c) 2024 Jeremy Long +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above +// copyright notice, this list of conditions and the following +// disclaimer. +// +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// * Neither the name of the OWASP nor the names of its +// contributors may be used to endorse or promote products +// derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + package org.owasp.encoder.testing.jakarta_test.dto; /** Immutable values exposed as bean properties to JSP EL. */ diff --git a/jakarta/pom.xml b/jakarta/pom.xml index b4b432c..3bdb60a 100644 --- a/jakarta/pom.xml +++ b/jakarta/pom.xml @@ -56,6 +56,8 @@ + 1.000 + 1.000 org.owasp.encoder.jakarta org.owasp.encoder.jakarta-jsp + 2026-09-26T00:00:00Z UTF-8 UTF-8 + + + + 0.990 + 0.985 * @@ -155,6 +149,58 @@ + + org.apache.maven.plugins + maven-clean-plugin + 3.5.0 + + + org.apache.maven.plugins + maven-resources-plugin + 3.5.0 + + + org.apache.maven.plugins + maven-install-plugin + 3.2.0 + + + org.apache.maven.plugins + maven-deploy-plugin + 3.2.0 + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.6.3 + + + org.apache.maven.plugins + maven-dependency-plugin + 3.11.0 + + + org.apache.maven.plugins + maven-help-plugin + 3.5.2 + + + org.apache.maven.plugins + maven-wrapper-plugin + 3.3.4 + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.6.0 + + + + org.apache.maven.plugins + maven-site-plugin + 3.22.0 + truetrue + org.apache.maven.plugins @@ -225,49 +271,10 @@ maven-surefire-plugin 3.6.0 - - org.apache.maven.plugins - maven-surefire-report-plugin - 3.6.0 - org.apache.maven.plugins maven-gpg-plugin - 1.6 - - - org.apache.maven.plugins - maven-site-plugin - - 3.4 - - - lt.velykis.maven.skins - reflow-velocity-tools - 1.1.1 - - - - org.apache.velocity - velocity - 1.7 - - - org.apache.maven.doxia - doxia-module-markdown - 1.6 - - - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 2.9 - - - org.apache.maven.plugins - maven-pmd-plugin - 3.6 + 3.2.8 org.apache.felix @@ -277,27 +284,71 @@ org.codehaus.mojo versions-maven-plugin - 2.3 - - - org.apache.maven.plugins - maven-jxr-plugin - 2.5 - - - org.codehaus.mojo - findbugs-maven-plugin - 3.0.4 + 2.22.0 org.sonatype.central central-publishing-maven-plugin - 0.9.0 + 0.11.0 + + org.apache.maven.plugins + maven-enforcer-plugin + + + build-policy + validate + enforce + + + [3.9.16,) + [17,) + + + + + clean,verify,install,deploy + + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + com.puppycrawl.tools + checkstyle + + 12.3.1 + + + + ${maven.multiModuleProjectDirectory}/src/main/config/checkstyle.xml + ${maven.multiModuleProjectDirectory}/src/main/config/checkstyle-header.txt + + module-info.java + false + false + + ${project.basedir}/src/main/java + ${project.basedir}/src/main/java9 + + + + + main-source-policy + validate + check + + + com.github.siom79.japicmp @@ -387,6 +438,8 @@ true + <_reproducible>true + <_noextraheaders>true <_noee>true <_noimportjava>true @@ -415,6 +468,23 @@ surefireArgLine + true + + + + coverage-report + verify + reportcheck + + + + BUNDLE + + LINECOVEREDRATIO${coverage.line.minimum} + BRANCHCOVEREDRATIO${coverage.branch.minimum} + + + @@ -423,7 +493,7 @@ org.apache.maven.plugins maven-surefire-plugin - ${surefireArgLine} + @{surefireArgLine} @{argLine} @@ -490,121 +560,8 @@ - - org.apache.maven.plugins - maven-site-plugin - - - org.sonatype.central - central-publishing-maven-plugin - true - - central - false - - - - - - org.apache.maven.plugins - maven-project-info-reports-plugin - - - - index - summary - license - scm - mailing-list - issue-tracking - dependencies - plugin-management - project-team - - - - - - org.codehaus.mojo - versions-maven-plugin - - - - dependency-updates-report - plugin-updates-report - - - - - - org.apache.maven.plugins - maven-jxr-plugin - - - org.apache.maven.plugins - maven-surefire-report-plugin - - - - report-only - failsafe-report-only - - - - - - org.jacoco - jacoco-maven-plugin - - - - - report - - - - - - org.apache.maven.plugins - maven-pmd-plugin - - 1.8 - true - utf-8 - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - default - - javadoc - - - 8 - true - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - src/main/config/checkstyle.xml - src/main/config/checkstyle-header.txt - - - - org.codehaus.mojo - findbugs-maven-plugin - - - sign-artifacts @@ -616,9 +573,49 @@ + + org.apache.maven.plugins + maven-enforcer-plugin + + + release-toolchain + enforce + + [17.0.20-1] + + Eclipse Adoptium + java.runtime.version17\.0\.20\.1\+1 + [3.9.16] + + + + + + org.sonatype.central + central-publishing-maven-plugin + true + + + com.fasterxml.jackson.corejackson-databind2.22.3 + com.fasterxml.jackson.corejackson-core2.22.3 + com.fasterxml.jackson.corejackson-annotations2.22 + org.apache.httpcomponents.client5httpclient55.6.4 + org.apache.httpcomponents.core5httpcore55.4.4 + org.apache.httpcomponents.core5httpcore5-h25.4.4 + + + + central + false + jakarta-test + + org.apache.maven.plugins maven-gpg-plugin + + true + sign-artifacts diff --git a/releases/batch-04-validation.md b/releases/batch-04-validation.md new file mode 100644 index 0000000..7867a94 --- /dev/null +++ b/releases/batch-04-validation.md @@ -0,0 +1,102 @@ +# Batch 04 validation + +Validated 2026-09-26. #123 merged separately in PR #184 after all 26 checks and +Sol then Astra reviews. This record covers the subsequent coordinated build +policy/release changes; it is not release approval. + +## Reference payload comparison + +Commit `8b548244233330872ba2c6d165f7624b5e6cddff`, exported twice with `git archive`; each copy used a +new empty local Maven repository and wrapper cache. Eclipse Temurin +17.0.20.1+1 (macOS aarch64 archive SHA-256 +`196d13ba5f10414bef7f6a05a9b3f00edacb18ebacef2b99485db9e2ee18f0e8`), +Maven 3.9.16, macOS 27.0 (26A428), UTC, C locale and explicit UTF-8/en-US JVM +properties. `scripts/check-reproducible.py` compared the installed payloads, +not an older published release. Both clean installs succeeded and all seventeen +files matched. Later documentation/consumer-helper updates do not change these +artifact inputs. Source timestamp: `2026-09-26T00:00:00Z` from the reviewed POM. +No cross-OS/architecture claim and no comparison of signing timestamps is made. + +| Artifact | SHA-256 (both builds) | +| --- | --- | +| `encoder-1.5.0-SNAPSHOT-javadoc.jar` | `2c8344ee19f9345c39915ca8c3ca654da60770f43787b8df25e2381b4d131d98` | +| `encoder-1.5.0-SNAPSHOT-sources.jar` | `32e0a28730aa55f2c546cf1122eef8a2971e99be67ac2872475be243649f299b` | +| `encoder-1.5.0-SNAPSHOT.jar` | `2f94c721dec1cd52d76617e51161e6174a498aa882cd409f59d8bc8bee86ee0a` | +| `encoder-1.5.0-SNAPSHOT.pom` | `8a663236fc5ad4d11c7c4f18c02c0a20bb2db3fae63a00bc7bc2e9d6fc858075` | +| `encoder-esapi-1.5.0-SNAPSHOT-javadoc.jar` | `6faacb28bd20720b9673462c434a5af465554f5aeefadda36231068f4b5e638c` | +| `encoder-esapi-1.5.0-SNAPSHOT-sources.jar` | `64e81e13cf000c11d82be10ab611cec53429bd0b0e5632e7480ea89176616c30` | +| `encoder-esapi-1.5.0-SNAPSHOT.jar` | `93434c28eeb31a28aa675164e8c22444b2336dfeb4fa94cc006b2dffd5830211` | +| `encoder-esapi-1.5.0-SNAPSHOT.pom` | `311fdafe83331ae937e6826d363115553c5a9d1e453d41c9ce270661741f890f` | +| `encoder-jakarta-jsp-1.5.0-SNAPSHOT-javadoc.jar` | `1defe3a269062ec12e9103138d3e731a1832767a39e8ee13fc125a55d52d6eef` | +| `encoder-jakarta-jsp-1.5.0-SNAPSHOT-sources.jar` | `1bfd112d8f2ed9e2b9dc2912043ad2caac1daa1c806861c6d6ecc6fe30457129` | +| `encoder-jakarta-jsp-1.5.0-SNAPSHOT.jar` | `9ceb6b4f4ad46a5d2233cc78f37f775acc1fb6115dbf039421bbe8359f318927` | +| `encoder-jakarta-jsp-1.5.0-SNAPSHOT.pom` | `379b3964ec5924175bfe5aa43448a05212348a5cb8cb0875f271cbbd8c65ac08` | +| `encoder-jsp-1.5.0-SNAPSHOT-javadoc.jar` | `e2a8025ae8895af5c71da8a273f2d290a5cd9abbc84dfde99ed4ab14e01c0796` | +| `encoder-jsp-1.5.0-SNAPSHOT-sources.jar` | `8ce08bbcd7c067038556cd525d42d09781875a60f89709f74c4be385302d70a5` | +| `encoder-jsp-1.5.0-SNAPSHOT.jar` | `f95dbbbc2bbe00acbefb2848e7c3c137396271f68358d1d0640c87399aa2dc80` | +| `encoder-jsp-1.5.0-SNAPSHOT.pom` | `6343c9cc2d5a5582b3c98444b139eecb792c416af8f96eb3d3efb88f146678a5` | +| `encoder-parent-1.5.0-SNAPSHOT.pom` | `8c15bf168cf491d2cebabd683c3550f6b8dbba8fdaa2647ecea7d1526be1209b` | + +## Build and policy evidence + +- Full clean verify on the reference JDK: 2,178 unit tests, eight existing + integration tests, both packaged JSP engines, API/signature checks and coverage. +- Seventeen binary/source metadata guards and 34-file adapter parity passed; + Java 17 isolated classpath/explicit/automatic-module and Felix R6/R8 consumers + passed, including negative old-core wiring. +- Normal plus optional profile Enforcer/Checkstyle validation passed. Maven 4 + prerelease plugins are intentionally not selected. Explicit pins cover clean, + resources, compiler, JAR, source, Javadoc, install, deploy, versions, helper, + analysis, API checks and the disabled implicit Site lifecycle. +- Coverage baseline/floors and scope are in BUILDING.md. A fresh reduced-test run + failed at 76.0% core lines/61.5% branches; a separate clean `jacoco.skip=true` + verify passed. Unit-test agents do not enter packaged consumers/JSP engines. +- Unix wrapper bootstrap and incorrect-SHA rejection passed locally; Windows + and Unix CI bootstrap/checksum jobs passed at the initial PR head. +- Source notices preserve historical owners. Four module descriptors and four + app source files lacked notices; their introduction commits identify Jeremy + Long, 2024. No encoder-loop implementation changes were made. +- No old OSS repositories/profile inheritance remains. The new plugin graph + parser was exercised against dependency-plugin 3.11.0 output. + +## Local signing rehearsal + +The same committed sources were exported to an isolated directory and changed +only there to the non-published fixture version `9.9.9-validation` and matching +SCM tag text. A disposable, one-day **local validation only** RSA key, separate +GnuPG home, dummy Central settings entry and new Maven repository were used. +`clean deploy -DperformRelease=true -DskipPublishing=true -DskipTests` succeeded; +no production keys/tokens were used, no upload occurred, and no Git tag was made. +The plugin's skip flag filters all artifacts rather than generating the documented +ZIP; source inspection and empty staging output confirmed this. The safe local +assembler now handles this path explicitly, after verifying all 17 signatures. +It assembled 5 POMs and 12 JARs, their 17 signatures and 68 checksums (102 entries), +with no WAR. Validation-only bundle SHA-256: +`3aaf5f8986afdb91eb791fcedcd2c3ee7f4c9d38855838450c51541c31d375c1`. +The first attempt exposed Central 0.11.0's requirement for a server settings entry +even when upload is disabled; the documented command now accounts for it. + +GPG 3.2.8/Publisher 0.11.0 and plugin-only Jackson/HTTP overrides were exercised. +OSV exact-version checks on the six overrides returned no advisories on the review +date; the upstream old Jackson/HTTP versions did have matches. This is narrower +than certifying the whole build graph or live Portal transport. #111 still owns +namespace access and validated-then-dropped real staging rehearsals. + +Negative bundle probes rejected both an unexpected fingerprint and a modified +signed JAR before creating an output ZIP. Full-fingerprint GPG and raw Maven +SHA-256 sidecar instructions were independently exercised by Sol against the +original Central 1.4.0 artifact. The current project key block is unchanged. +Four early-key authorization-record gaps remain explicitly documented in #110. + +All 28 PR checks passed at `8b548244233330872ba2c6d165f7624b5e6cddff`, including +both wrapper platforms, ten ESAPI versions, Docker/browser/WAR, Java 8 unit JVM, +original-JAR runtimes 8/11/17/21/25 and CodeQL. Final follow-up CI and model review +are required before merge; AI review is not independent maintainer approval. + +Sol's final review found optimization-sensitive `assert` checks in the new release +assembler/reproducibility checker. They were replaced with explicit failures, +including wrapper probes. Four added regression tests run Python with `-O`: +changed/missing comparison payloads, a real valid signature from the wrong key, +a stale signed POM and a snapshot bundle are rejected. All 14 policy/verification +tests pass. A missing-key Maven signing attempt fails noninteractively, and the +release profile rejects the development SNAPSHOT version as intended. diff --git a/scripts/build-dependency-snapshot.py b/scripts/build-dependency-snapshot.py index c0b6027..5faa96d 100644 --- a/scripts/build-dependency-snapshot.py +++ b/scripts/build-dependency-snapshot.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Convert Maven dependency:resolve-plugins 3.9.0 reports to a GitHub snapshot. +"""Convert Maven dependency:resolve-plugins 3.11.0 reports to a GitHub snapshot. Runtime/test graphs are submitted by the Maven submission action. This separate development-scope graph retains each build plugin's resolved dependency edges. diff --git a/scripts/check-reproducible.py b/scripts/check-reproducible.py new file mode 100644 index 0000000..168e3c9 --- /dev/null +++ b/scripts/check-reproducible.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +"""Compare twelve JARs and five installed POMs from the same committed sources.""" +import argparse +import hashlib +import io +import json +import os +from pathlib import Path +import subprocess +import tarfile +import xml.etree.ElementTree as ET + +ROOT = Path(__file__).resolve().parents[1] +NS = {'p': 'http://maven.apache.org/POM/4.0.0'} +MODULES = {'': 'encoder-parent', 'core': 'encoder', 'jsp': 'encoder-jsp', + 'jakarta': 'encoder-jakarta-jsp', 'esapi': 'encoder-esapi'} + + +def require_identical(first, second): + differences = sorted(name for name in set(first) | set(second) + if first.get(name) != second.get(name)) + if differences: + raise ValueError('Artifact bytes differ: ' + ', '.join(differences)) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--commit', required=True) + parser.add_argument('--directory', type=Path, required=True) + args = parser.parse_args() + commit = subprocess.check_output(['git', 'rev-parse', '--verify', args.commit + '^{commit}'], + cwd=ROOT, text=True).strip() + java = Path(os.environ['JAVA_HOME']) / 'bin/java' + props = subprocess.run([str(java), '-XshowSettings:properties', '-version'], + text=True, capture_output=True, check=True).stderr + if 'java.vendor = Eclipse Adoptium' not in props or 'java.runtime.version = 17.0.20.1+1\n' not in props: + raise ValueError('Unexpected reference JDK: ' + props) + directory = args.directory.resolve() + directory.mkdir(parents=True, exist_ok=False) + archive = subprocess.check_output(['git', 'archive', commit], cwd=ROOT) + results = [] + for name in ('first', 'second'): + work = directory / name + work.mkdir() + with tarfile.open(fileobj=io.BytesIO(archive)) as source: + source.extractall(work, filter='data') + repository = directory / (name + '-m2') + env = dict(os.environ, TZ='UTC', LC_ALL='C', LANG='C', + MAVEN_OPTS='-Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8', + MAVEN_USER_HOME=str(directory / (name + '-wrapper'))) + for key in ('MAVEN_ARGS', 'MVNW_REPOURL', 'JAVA_TOOL_OPTIONS', 'JDK_JAVA_OPTIONS'): + env.pop(key, None) + command = [str(work / ('mvnw.cmd' if os.name == 'nt' else 'mvnw')), + '-B', '-ntp', '-Dmaven.repo.local=' + str(repository), '-DskipTests', 'clean', 'install'] + with (directory / (name + '.log')).open('w') as log: + subprocess.run(command, cwd=work, env=env, stdout=log, stderr=subprocess.STDOUT, check=True) + version = ET.parse(work / 'pom.xml').findtext('p:version', namespaces=NS) + hashes = {} + for module, artifact in MODULES.items(): + base = repository / 'org/owasp/encoder' / artifact / version + suffixes = ['.pom'] if not module else ['.pom', '.jar', '-sources.jar', '-javadoc.jar'] + for suffix in suffixes: + filename = artifact + '-' + version + suffix + hashes[filename] = hashlib.sha256((base / filename).read_bytes()).hexdigest() + if len(hashes) != 17: + raise ValueError('Expected seventeen release payload files') + results.append(hashes) + differences = [name for name in results[0] if results[0][name] != results[1][name]] + report = {'commit': commit, 'java': props, 'maven': '3.9.16', + 'hashes': results, 'differences': differences} + (directory / 'comparison.json').write_text(json.dumps(report, indent=2) + '\n') + require_identical(results[0], results[1]) + print('Identical: twelve binary/source/Javadoc JARs and five POMs from', commit) + + +if __name__ == '__main__': + main() diff --git a/scripts/check-wrapper.py b/scripts/check-wrapper.py new file mode 100644 index 0000000..ad603ad --- /dev/null +++ b/scripts/check-wrapper.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +"""Exercise both platform bootstraps and the distribution checksum failure path.""" +import os +from pathlib import Path +import re +import shutil +import subprocess +import tempfile + +ROOT = Path(__file__).resolve().parents[1] +WRAPPER = 'mvnw.cmd' if os.name == 'nt' else 'mvnw' + + +def bootstrap(directory, bad_hash): + project = directory / 'project' + project.mkdir() + shutil.copy2(ROOT / WRAPPER, project / WRAPPER) + shutil.copytree(ROOT / '.mvn', project / '.mvn') + properties = project / '.mvn/wrapper/maven-wrapper.properties' + if bad_hash: + properties.write_text(re.sub(r'distributionSha256Sum=.*', + 'distributionSha256Sum=' + '0' * 64, + properties.read_text())) + env = dict(os.environ, MAVEN_USER_HOME=str(directory / 'maven-home')) + # The test must follow the reviewed URL and cannot reuse a wrapper override/cache. + for name in ('MVNW_REPOURL', 'MVNW_USERNAME', 'MVNW_PASSWORD'): + env.pop(name, None) + result = subprocess.run([str(project / WRAPPER), '-version'], cwd=project, + env=env, text=True, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, timeout=240) + if bad_hash: + if result.returncode == 0 or 'SHA-256' not in result.stdout: + raise ValueError('Checksum rejection did not work: ' + result.stdout) + else: + if result.returncode != 0 or 'Apache Maven 3.9.16' not in result.stdout: + raise ValueError('Wrapper bootstrap failed: ' + result.stdout) + print(('Rejected incorrect checksum' if bad_hash else 'Verified bootstrap'), WRAPPER) + + +if __name__ == '__main__': + for bad_hash in (False, True): + with tempfile.TemporaryDirectory(prefix='encoder-wrapper-') as root: + bootstrap(Path(root), bad_hash) diff --git a/scripts/package-release.py b/scripts/package-release.py new file mode 100644 index 0000000..7c9b288 --- /dev/null +++ b/scripts/package-release.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +"""Verify and assemble an existing signed release payload; never build/sign/upload.""" +import argparse +import hashlib +from pathlib import Path +import re +import subprocess +import xml.etree.ElementTree as ET +import zipfile + +NS = {'p': 'http://maven.apache.org/POM/4.0.0'} +MODULES = {'': 'encoder-parent', 'core': 'encoder', 'jsp': 'encoder-jsp', + 'jakarta': 'encoder-jakarta-jsp', 'esapi': 'encoder-esapi'} + + +def require(condition, message): + if not condition: + raise ValueError(message) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--source', type=Path, default=Path(__file__).resolve().parents[1]) + parser.add_argument('--output', type=Path, required=True) + parser.add_argument('--gnupg-home', type=Path, required=True) + parser.add_argument('--fingerprint', required=True) + args = parser.parse_args() + expected = args.fingerprint.upper() + require(re.fullmatch('[0-9A-F]{40}', expected), 'Use a full expected fingerprint') + root = args.source.resolve() + pom = ET.parse(root / 'pom.xml') + version = pom.findtext('p:version', namespaces=NS) + require(version and re.fullmatch(r'[0-9]+\.[0-9]+\.[0-9]+(?:-[a-zA-Z0-9.-]+)?', version), 'Invalid release version') + require(not version.endswith('-SNAPSHOT'), 'Refuse snapshot bundle') + require(pom.findtext('p:scm/p:tag', namespaces=NS) == 'v' + version, 'SCM tag mismatch') + entries = {} + payloads = 0 + for module, artifact in MODULES.items(): + module_pom = ET.parse(root / module / 'pom.xml') + require(module_pom.findtext('p:artifactId', namespaces=NS) == artifact, 'Artifact identity mismatch') + if module: + require(module_pom.findtext('p:parent/p:version', namespaces=NS) == version, 'Module version mismatch') + suffixes = ['.pom'] if not module else ['.pom', '.jar', '-sources.jar', '-javadoc.jar'] + for suffix in suffixes: + filename = artifact + '-' + version + suffix + path = root / module / 'target' / filename + data = path.read_bytes() + if suffix == '.pom': + require(data == (root / module / 'pom.xml').read_bytes(), 'Stale signed POM: ' + filename) + signature = Path(str(path) + '.asc') + result = subprocess.run(['gpg', '--homedir', str(args.gnupg_home.resolve()), + '--batch', '--status-fd', '1', '--verify', str(signature), str(path)], + text=True, capture_output=True, check=True) + valid = [line.split() for line in result.stdout.splitlines() + if line.startswith('[GNUPG:] VALIDSIG ')] + require(any(row[2] == expected or row[-1] == expected for row in valid), 'Unexpected signing fingerprint: ' + filename) + name = 'org/owasp/encoder/' + artifact + '/' + version + '/' + filename + entries[name] = data + entries[name + '.asc'] = signature.read_bytes() + for algorithm in ('md5', 'sha1', 'sha256', 'sha512'): + entries[name + '.' + algorithm] = hashlib.new(algorithm, data).hexdigest().encode('ascii') + payloads += 1 + require(payloads == 17 and len(entries) == 102, 'Incomplete release bundle') + # Exclusive creation protects retained immutable bundles from accidental replacement. + with zipfile.ZipFile(args.output, 'x', compression=zipfile.ZIP_DEFLATED) as archive: + for name, data in sorted(entries.items()): + archive.writestr(name, data) + # Re-read the exact written ZIP and verify every generated checksum. + with zipfile.ZipFile(args.output) as archive: + require(set(archive.namelist()) == set(entries), 'Bundle entry mismatch') + for name, data in entries.items(): + require(archive.read(name) == data, 'Bundle byte mismatch: ' + name) + print('Verified 17 signatures; assembled 5 POMs, 12 JARs, signatures and 68 checksums:', args.output) + print('Bundle SHA-256:', hashlib.sha256(args.output.read_bytes()).hexdigest()) + + +if __name__ == '__main__': + main() diff --git a/scripts/tests/test_release_tools.py b/scripts/tests/test_release_tools.py new file mode 100644 index 0000000..6c5d7b1 --- /dev/null +++ b/scripts/tests/test_release_tools.py @@ -0,0 +1,82 @@ +"""Security acceptance checks must remain effective with Python optimization.""" +from pathlib import Path +import shutil +import subprocess +import sys +import tempfile +import unittest + +ROOT = Path(__file__).resolve().parents[2] + + +class ReproducibilityChecks(unittest.TestCase): + def test_different_or_missing_payload_rejected_when_optimized(self): + for second in ({'encoder.jar': 'different'}, {}): + code = ('import runpy; checks=runpy.run_path(' + repr(str(ROOT / 'scripts/check-reproducible.py')) + + '); checks["require_identical"]({"encoder.jar":"expected"}, ' + repr(second) + ')') + result = subprocess.run([sys.executable, '-O', '-c', code], capture_output=True, text=True) + self.assertNotEqual(0, result.returncode) + self.assertIn('Artifact bytes differ: encoder.jar', result.stderr) + + +@unittest.skipUnless(shutil.which('gpg'), 'GnuPG required for real detached-signature fixture') +class ReleaseAcceptance(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.temp = tempfile.TemporaryDirectory(prefix='enc-') + cls.addClassCleanup(cls.temp.cleanup) + cls.root = Path(cls.temp.name) + cls.home = cls.root / 'g' + cls.home.mkdir(mode=0o700) + generated = subprocess.run(['gpg', '--homedir', str(cls.home), '--batch', '--pinentry-mode', 'loopback', '--passphrase', '', + '--quick-generate-key', 'Disposable TEST ONLY ', + 'rsa2048', 'sign', '1d'], capture_output=True, text=True) + if generated.returncode: + raise RuntimeError(generated.stderr) + keys = subprocess.check_output(['gpg', '--homedir', str(cls.home), '--with-colons', '--list-keys'], text=True) + cls.fingerprint = next(row.split(':')[9] for row in keys.splitlines() if row.startswith('fpr:')) + + @classmethod + def tearDownClass(cls): + if shutil.which('gpgconf'): + subprocess.run(['gpgconf', '--homedir', str(cls.home), '--kill', 'gpg-agent'], capture_output=True) + cls.temp.cleanup() + + def setUp(self): + self.project = self.root / self._testMethodName + (self.project / 'target').mkdir(parents=True) + pom = ('4.0.0' + 'encoder-parent9.9.9-validation' + 'v9.9.9-validation') + (self.project / 'pom.xml').write_text(pom) + self.signed_pom = self.project / 'target/encoder-parent-9.9.9-validation.pom' + self.signed_pom.write_text(pom) + subprocess.run(['gpg', '--homedir', str(self.home), '--batch', '--armor', '--detach-sign', + str(self.signed_pom)], check=True, capture_output=True) + + def reject(self, fingerprint, message): + output = self.project / 'must-not-exist.zip' + result = subprocess.run([sys.executable, '-O', str(ROOT / 'scripts/package-release.py'), + '--source', str(self.project), '--output', str(output), + '--gnupg-home', str(self.home), '--fingerprint', fingerprint], + text=True, capture_output=True) + self.assertNotEqual(0, result.returncode) + self.assertIn(message, result.stderr) + self.assertFalse(output.exists()) + + def test_valid_signature_from_unexpected_key_rejected(self): + self.reject('0' * 40, 'Unexpected signing fingerprint') + + def test_stale_signed_pom_rejected(self): + with (self.project / 'pom.xml').open('a') as pom: + pom.write('\n') + self.reject(self.fingerprint, 'Stale signed POM') + + def test_snapshot_rejected(self): + pom = self.project / 'pom.xml' + pom.write_text(pom.read_text().replace('9.9.9-validation', '9.9.9-SNAPSHOT')) + self.reject(self.fingerprint, 'Refuse snapshot bundle') + + +if __name__ == '__main__': + unittest.main() diff --git a/src/main/config/checkstyle-header.txt b/src/main/config/checkstyle-header.txt index 3b51065..283e2d9 100644 --- a/src/main/config/checkstyle-header.txt +++ b/src/main/config/checkstyle-header.txt @@ -1,4 +1,4 @@ -^// Copyright \(c\) 201[2-9] (Jeff Ichnowski|Jim Manico|Jeremy Long)\s*$ +^// Copyright \(c\) (?:19|20)\d{2}(?:-(?:19|20)\d{2})? (Jeff Ichnowski|Jim Manico|Jeremy Long|OWASP)\.?\s*$ ^// All rights reserved\.\s*$ ^// ^// Redistribution and use in source and binary forms, with or without\s*$ diff --git a/src/main/config/checkstyle.xml b/src/main/config/checkstyle.xml index 6656e95..65c2fe3 100644 --- a/src/main/config/checkstyle.xml +++ b/src/main/config/checkstyle.xml @@ -1,203 +1,25 @@ - + - - - - - - - - + + - - - - + - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - diff --git a/src/site/resources/images/owasp.jpg b/src/site/resources/images/owasp.jpg deleted file mode 100644 index addae89..0000000 Binary files a/src/site/resources/images/owasp.jpg and /dev/null differ diff --git a/src/site/site.xml b/src/site/site.xml deleted file mode 100644 index 2c1386d..0000000 --- a/src/site/site.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - - lt.velykis.maven.skins - reflow-maven-skin - 1.1.1 - - - - - OWASP Java Encoder Project - OWASP Java Encoder Project - OWASP Java Encoder Project - ./images/owasp.jpg - 107 - 300 - - - - default - true - github - - ${project.name} - https://owasp.org/www-project-java-encoder/ - - false - top - 6 - Modules|Maven Documentation - - Modules - Contribute - Maven Documentation - - - - - - - - - - - - - - - - - - - - - -