Release Java SDK v17.2.1#975
Conversation
e5e9bc7 to
1802c4c
Compare
1802c4c to
b1f61c1
Compare
## Bug Fixes - KSM-854: fix KeeperFileData crash when lastModified is absent - KSM-823: always include custom:[] in record create payload - KSM-855: fix resource leaks in LocalConfigStorage and SecretsManager ## Features - KSM-902: add IL5 region mapping (il5.keepersecurity.us) ## CI/CD - Replace Syft with CycloneDX Gradle plugin (org.cyclonedx.bom v3.2.4) for accurate Maven purl generation (correct groupIds like org.jetbrains.kotlin vs kotlin-reflect) - Scope SBOM to runtimeClasspath only — excludes test deps and Kotlin compiler internals - Set SBOM root componentName to keeper-secrets-manager-java - Switch Manifest SBOM upload to manifest-cyber/manifest-github-action@v1.6.0 (pre-generated BOM file upload, no Syft invocation) - Add build-only mode to publish workflow (publish boolean input, default true) - Gate Maven Central publish via confirm-publish dependency cascade — preserves environment: prod release manager approval without firing on build-only runs - SHA-pin all actions (gradle/actions, ksm-action, manifest-cyber action, checkout, setup-java, upload-artifact) and upgrade to Node.js 24 compatible versions - Fix template injection findings — move needs outputs to env vars in verify step - Add path filter to test.java.yml (sdk/java/core/**) - Remove EOL Java 16/18 from test matrix; add Java 21 LTS
636ee8c to
1edd61a
Compare
Three complementary layers so IL5 customers can supply key ID 20 without it being embedded in the shipped SDK: - Layer 1 (config field): if `serverPublicKey` is present in storage (e.g. populated from a base64 config JSON), `generateTransmissionKey` uses those bytes instead of the embedded table. - Layer 2 (extended OTT): `initializeStorage` now parses a 4-segment token `REGION:clientKey:keyId:serverPublicKey`, saving both `serverPublicKeyId` and `serverPublicKey` to storage on bind. - Layer 3 (constructor param): `SecretsManagerOptions` accepts an optional `serverPublicKey` string that is immediately persisted to storage, so the key is available for all subsequent requests. Adds `KEY_SERVER_PUBLIC_KEY` constant. Existing behaviour is unchanged when the field is absent; the embedded key table is still used for all commercial environments.
KSM-902: Java IL5 dynamic server public key support
There was a problem hiding this comment.
An organization admin can view or raise the cap at claude.ai/admin-settings/claude-code. The cap resets at the start of the next billing period.
Once the cap resets or is raised, push a new commit or reopen this pull request to trigger a review.
- Add serverPublicKeyId parameter to SecretsManagerOptions (Layer 3 parity with serverPublicKey); both params persist to storage on init - Validate 4-segment OTT: require exactly 4 parts, keyId must be a positive integer, serverPublicKey must be >= 80 chars - Gate key-rotation overwrite in postQuery: when a custom server public key is configured, a rotation hint throws a clear error instead of silently replacing the custom key ID - Rename KEY_SERVER_PUBIC_KEY_ID (typo) to KEY_SERVER_PUBLIC_KEY_ID across SecretsManager.kt, LocalConfigStorage.kt, and tests; constant introduced in this release branch so no published API is broken - Replace fake testIL5ConfigFieldOverridesEmbeddedTable with a test that actually calls generateTransmissionKey via getSecrets and verifies key ID 999 (not in embedded table) reaches the post function
Workflow fixes: - Remove max-parallel: 1 from test.java.yml — no reason to serialize the JVM matrix - Add validate-version gate: fails fast if the version is already on Maven Central - Add standalone test job that runs in parallel with SBOM generation; tests no longer run inside publish-to-maven-central (removed redundant ./gradlew clean build test) - Fix base64 -w 0 in Bearer token construction — Linux base64 line-wraps at 76 chars without this flag, silently producing a malformed auth header - Anchor version grep to line start (^version) so a future multi-version build.gradle.kts cannot match the wrong assignment RecordData.kt: - Add @jvmoverloads to KeeperFileData constructor for Java caller ergonomics - Add inline comments explaining @EncodeDefault (KSM-823), FlexibleLongSerializer (KSM-673), and lastModified default of 0 (KSM-854)
There was a problem hiding this comment.
An organization admin can view or raise the cap at claude.ai/admin-settings/claude-code. The cap resets at the start of the next billing period.
Once the cap resets or is raised, push a new commit or reopen this pull request to trigger a review.
| needs: validate-version | ||
| runs-on: ubuntu-latest | ||
| defaults: | ||
| run: | ||
| working-directory: ${{ inputs.working-directory }} | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| persist-credentials: false | ||
| - name: Set up Java ${{ inputs.java-version }} | ||
| uses: actions/setup-java@v4 | ||
| uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 | ||
| with: | ||
| java-version: ${{ inputs.java-version }} | ||
| distribution: 'temurin' | ||
|
|
||
| - name: Setup Gradle | ||
| uses: gradle/actions/setup-gradle@v4 | ||
|
|
||
| - name: Build project and copy dependencies | ||
| run: | | ||
| echo "Building project to resolve dependencies..." | ||
| ./gradlew build -x test --no-daemon --refresh-dependencies | ||
| uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0 | ||
| with: | ||
| gradle-version: '8.14' | ||
| - name: Build and Test | ||
| run: ./gradlew clean build --no-daemon | ||
|
|
||
| echo "Copying runtime dependencies for SBOM scanning..." | ||
| ./gradlew copyDependencies | ||
| generate-and-upload-sbom: |
| needs: [get-version, validate-version] | ||
| runs-on: ubuntu-latest | ||
| defaults: | ||
| run: | ||
| working-directory: ${{ inputs.working-directory }} | ||
|
|
||
| echo "Dependencies collected:" | ||
| ls -la build/sbom-deps/ || echo "Warning: sbom-deps directory not created" | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| echo "Excluding fatJar from SBOM (test artifact only):" | ||
| find build/libs -name "*.jar" -type f ! -name "*-fat.jar" || true | ||
| - name: Set up Java ${{ inputs.java-version }} | ||
| uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 | ||
| with: | ||
| java-version: ${{ inputs.java-version }} | ||
| distribution: 'temurin' | ||
|
|
||
| - name: Install SBOM tools | ||
| run: | | ||
| echo "Installing Syft v1.18.1..." | ||
| curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin v1.18.1 | ||
| - name: Setup Gradle | ||
| uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0 | ||
|
|
||
| echo "Installing Manifest CLI v0.18.3..." | ||
| curl -sSfL https://raw.githubusercontent.com/manifest-cyber/cli/main/install.sh | sh -s -- -b . v0.18.3 | ||
| chmod +x ./manifest | ||
| - name: Build project | ||
| run: ./gradlew build -x test --no-daemon | ||
|
|
||
| echo "Verifying installations..." | ||
| syft version | ||
| ./manifest --version | ||
| - name: Generate SBOM with CycloneDX | ||
| run: ./gradlew cyclonedxBom --no-daemon | ||
|
|
||
| - name: Generate and publish SBOM | ||
| env: | ||
| MANIFEST_TOKEN: ${{ secrets.MANIFEST_TOKEN }} | ||
| run: | | ||
| echo "Creating Syft configuration for Java/Kotlin scanning..." | ||
| cat > syft-config.yaml << 'EOF' | ||
| package: | ||
| search: | ||
| scope: all-layers | ||
| unindexed-archives: true | ||
| indexed-archives: true | ||
| cataloger: | ||
| enabled: true | ||
| java: | ||
| enabled: true | ||
| search-unindexed-archives: true | ||
| search-indexed-archives: true | ||
| resolve-dependencies: true | ||
| use-network: false | ||
| max-parent-recursive-depth: 10 | ||
| # Disable non-Java catalogers | ||
| ruby: | ||
| enabled: false | ||
| python: | ||
| enabled: false | ||
| nodejs: | ||
| enabled: false | ||
| dotnet: | ||
| enabled: false | ||
| golang: | ||
| enabled: false | ||
| EOF | ||
|
|
||
| echo "Generating SBOM with Manifest CLI..." | ||
| ./manifest sbom build/sbom-deps \ | ||
| --generator=syft \ | ||
| --generator-config=syft-config.yaml \ | ||
| --name=${{ inputs.project-name }} \ | ||
| --version=${{ needs.get-version.outputs.version }} \ | ||
| --output=spdx-json \ | ||
| --file=sbom.json \ | ||
| --api-key=${MANIFEST_TOKEN} \ | ||
| --publish=true \ | ||
| --asset-label=application,sbom-generated,java,sdk,maven,security | ||
| - name: Publish SBOM to Manifest | ||
| uses: manifest-cyber/manifest-github-action@a90e8d22cb1a607317ec76cc9c53f61769c06213 # v1.6.0 | ||
| with: | ||
| apiKey: ${{ secrets.MANIFEST_TOKEN }} | ||
| bomFilePath: ${{ inputs.working-directory }}/build/reports/cyclonedx/bom.json | ||
| sbomName: ${{ inputs.project-name }} | ||
| sbomVersion: ${{ needs.get-version.outputs.version }} | ||
| asset-labels: application,sbom-generated,java,sdk,maven,security | ||
|
|
||
| - name: Archive SBOM | ||
| uses: actions/upload-artifact@v4 | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | ||
| with: | ||
| name: sbom-${{ inputs.project-name }}-${{ needs.get-version.outputs.version }} | ||
| path: ${{ inputs.working-directory }}/sbom.json | ||
| retention-days: 90 | ||
| name: sbom-cyclonedx-${{ inputs.project-name }}-${{ needs.get-version.outputs.version }} | ||
| path: ${{ inputs.working-directory }}/build/reports/cyclonedx/bom.json | ||
| retention-days: 10 | ||
|
|
||
| confirm-publish: |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Add if: inputs.publish == true to publish-to-maven-central so GitHub does not create a pending deployment entry for environment: prod when the job will be skipped. Without this, build-only runs show a spurious waiting-to-publish indicator in the PR and Deployments UI.
Summary
Release branch for Java SDK v17.2.1 — three changes: IL5 region support, serialization correctness for record create, and file attachment crash fix.
Changes
New Features
\"IL5\" -> \"il5.keepersecurity.us\"to theinitializeStorage()region map inSecretsManager.kt. Tokens prefixedIL5:now route to the DoD Impact Level 5 environment.Bug Fixes
KeeperRecordData.customdefaulted tonull, causingkotlinx-serializationto omit\"custom\"from the V3 API payload when no custom fields were set. Changed default tomutableListOf()and added@EncodeDefaultto force inclusion. Regression test added.lastModifiedabsent (KSM-854): files uploaded by non-SDK clients (iOS, Android, Web Vault) omitlastModified;kotlinx-serializationtreated it as required, throwingMissingFieldExceptionand silently dropping the attachment. Added= 0default, consistent with .NET SDK behavior (KSM-674).Deferred to v17.2.2
LocalConfigStorageandSecretsManager(KSM-855): deferred to v17.2.2 for faster v17.2.1 QA cycle. Full implementation available in feature branchfeature/java-ksm-855-resource-leaks.Breaking Changes
None.
Related Issues