Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
e46c83b
Harden callback SSRF controls, redact URL secrets, and align tests/co…
patelila Feb 27, 2026
66a4bef
Bumping chart version/ fixing aliases
hmcts-jenkins-a-to-c[bot] Feb 27, 2026
f3d91ac
feat(callback-security): enforce ingestion+runtime callback URL valid…
patelila Feb 27, 2026
9d123d5
Merge remote-tracking branch 'origin/CCD-7110_Server-Side_Request_For…
patelila Feb 27, 2026
f0d416c
chore(security): complete SSRF hardening pass, sonar remediation, and…
patelila Mar 2, 2026
3db38e1
chore(sonar): fix conditional callback logging and suppress intention…
patelila Mar 2, 2026
ca7026c
checkstyle error
patelila Mar 2, 2026
3ec0b21
checkstyle error
patelila Mar 2, 2026
4799c2e
fix for DefaultCaseDefinitionRepositoryCallbackValidationTest > shoul…
patelila Mar 2, 2026
6e32fca
fix for
patelila Mar 2, 2026
ac22024
code coverage for gate error
patelila Mar 2, 2026
3395df3
Fix for Using hardcoded IP addresses is security-sensitivejava:S1313
patelila Mar 2, 2026
2c783f8
• feat(security): harden callback SSRF controls (reject redirects, st…
patelila Mar 3, 2026
a3509a6
test: reduce WireMock port-bind flakes by tearing down context after …
patelila Mar 3, 2026
d264a5a
build(test): split into parallel testUnit + serialized testIt to impr…
patelila Mar 3, 2026
6e94f75
test(config): replace fixed wiremock fallback ports with random range…
patelila Mar 3, 2026
1f97f85
build(coverage): align jacocoTestReport with testUnit/testIt executio…
patelila Mar 3, 2026
bc1d5e4
helm(preview): allow host.docker.internal in callback URL allowlists…
patelila Mar 3, 2026
2d1a5e4
helm(preview): allow host. Add pre-smoke diagnostics for TEST_URL/en…
patelila Mar 3, 2026
e7a9f3d
Archive stage-specific smoke/functional cucumber JSON and XML artifa…
patelila Mar 3, 2026
9f18e85
Reduce Jenkins artifact footprint by archiving stage-specific cucumb…
patelila Mar 3, 2026
2ac70c5
Add callback config drift guards and smoke preflight diagnostics for …
patelila Mar 3, 2026
8e6f1d9
output values for - CCD_CALLBACK_ALLOWED_HOSTS
patelila Mar 3, 2026
422b01e
revert
patelila Mar 3, 2026
ec9ef0e
test(callback-validation): add explicit no-throw assertion for null c…
patelila Mar 4, 2026
9903ea7
Merge branch 'master' into CCD-7110_Server-Side_Request_Forgery_Crede…
patelila Mar 4, 2026
93cb5c1
ci(jenkins): set callback allowlist envs for BEFTA stub host and log …
patelila Mar 4, 2026
bb6ce70
Merge remote-tracking branch 'origin/CCD-7110_Server-Side_Request_For…
patelila Mar 4, 2026
7642946
Fix preview callback allowlists to include BEFTA stub host explicitly
patelila Mar 4, 2026
4f37174
fix(ci): harden callback host consistency checks and restore valid pr…
patelila Mar 4, 2026
a88da40
Align nightly callback allowlist env with CNP and make preFunctionalD…
patelila Mar 5, 2026
9cf14a3
Merge branch 'master' into CCD-7110_Server-Side_Request_Forgery_Crede…
patelila Mar 5, 2026
df8959f
Merge branch 'master' into CCD-7110_Server-Side_Request_Forgery_Crede…
patelila Mar 5, 2026
abb49d4
Add aac-manage-case-assignment host to callback allowlists and update…
patelila Mar 5, 2026
2bb60f3
Merge remote-tracking branch 'origin/CCD-7110_Server-Side_Request_For…
patelila Mar 5, 2026
59cf897
Add aac-manage-case-assignment host to callback allowlists and update…
patelila Mar 5, 2026
3ec77ad
fix yVersions below java-logging: 8.0.0 are deprecated, please upgrad…
patelila Mar 9, 2026
e9b988f
CCD-7110 Make callback allowlist checks env-driven and harden callbac…
patelila Mar 9, 2026
a385f91
Merge branch 'master' into CCD-7110_Server-Side_Request_Forgery_Crede…
patelila Mar 9, 2026
b0c7e26
CCD-7110 Harden callback SSRF controls, env-driven allowlist checks, …
patelila Mar 9, 2026
d50e715
CCD-7110 Fix null request handling in CallbackService and harden call…
patelila Mar 9, 2026
b497fae
Merge branch 'master' into CCD-7110_Server-Side_Request_Forgery_Crede…
patelila Mar 9, 2026
acfbf18
Merge branch 'master' into CCD-7110_Server-Side_Request_Forgery_Crede…
patelila Mar 10, 2026
ffd7515
Merge branch 'master' into CCD-7110_Server-Side_Request_Forgery_Crede…
patelila Mar 12, 2026
b3834e0
Merge branch 'master' into CCD-7110_Server-Side_Request_Forgery_Crede…
patelila Mar 13, 2026
007ff9e
Merge branch 'master' into CCD-7110_Server-Side_Request_Forgery_Crede…
patelila Mar 16, 2026
da76cfd
Merge branch 'master' into CCD-7110_Server-Side_Request_Forgery_Crede…
patelila Mar 17, 2026
78d2154
Merge branch 'master' into CCD-7110_Server-Side_Request_Forgery_Crede…
patelila Mar 17, 2026
2966699
Move repo skills under docs and drop unused skill metadata
patelila Mar 20, 2026
96279c0
Migrate deprecated ACR references to hmctsprod
patelila Mar 20, 2026
641f577
Merge branch 'master' into CCD-7110_Server-Side_Request_Forgery_Crede…
patelila Mar 25, 2026
5d9600e
Switch callback allowlists to regex-capable matching and share prefli…
patelila Mar 25, 2026
5b7a761
Merge branch 'master' into CCD-7110_Server-Side_Request_Forgery_Crede…
patelila Mar 25, 2026
6c6915b
Switch callback allowlists to regex-capable matching and share prefli…
patelila Mar 25, 2026
6dea5cc
Refine callback allowlist matching and preflight consistency checks
patelila Mar 25, 2026
9f9bb35
Merge branch 'master' into CCD-7110_Server-Side_Request_Forgery_Crede…
patelila Mar 25, 2026
acfe1ce
Fix callback allowlist regex parsing and clean up callback hardening …
patelila Mar 26, 2026
aeb0921
Harden callback allowlist parsing and align contract test config
patelila Mar 26, 2026
17e8365
Merge branch 'master' into CCD-7110_Server-Side_Request_Forgery_Crede…
patelila Mar 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Agents

This file is the index for repo-local workflow skills in this repository. Skill files live under `docs/skills/`.

## CCD Callback SSRF Hardening

Use the `ccd-callback-ssrf-hardening` agent for any callback security change in this repository, especially around event callbacks, webhook URL ingestion, or auth header handling.

### Trigger Phrases

- "Use ccd-callback-ssrf-hardening"
- "Run callback SSRF hardening"
- "Audit callback token leakage"

### Recommended Prompt Template

```text
Use ccd-callback-ssrf-hardening on hmcts/ccd-data-store-api.
Scope:
- src/main/java/uk/gov/hmcts/ccd/domain/service/callbacks/CallbackService.java
- src/main/java/uk/gov/hmcts/ccd/data/SecurityUtils.java
- callback URL ingestion/parsing paths
Tasks:
1. Detect SSRF and credential leakage patterns.
2. Enforce callback URL validation (allowlist, HTTPS, private/internal target blocking).
3. Remove sensitive header forwarding (Authorization, ServiceAuthorization, user-id, user-roles).
4. Add/update regression tests.
5. Summarize risk reduction and residual risk.
```

### Quick Scanner

```bash
bash docs/skills/ccd-callback-ssrf-hardening/scripts/scan_callback_risks.sh
```

## CCD SonarQube Remediation

Use the `ccd-sonarqube-remediation` agent for SonarQube-driven cleanup and quality fixes in this repository, especially maintainability/code smell issues that require safe refactors and test wiring updates.

### Trigger Phrases

- "Use ccd-sonarqube-remediation"
- "Fix Sonar issues"
- "Address code smells from SonarQube"

### Recommended Prompt Template

```text
Use ccd-sonarqube-remediation on hmcts/ccd-data-store-api.
Scope:
- files flagged by current SonarQube findings
Tasks:
1. Reproduce and identify root cause for each finding.
2. Patch with minimal behavior change and clear naming/structure.
3. Update affected tests and fixtures if constructor/bean wiring changes.
4. Add/update tests so coverage for new/changed code is at least 80%.
5. Run targeted Gradle compile/tests plus `checkstyleMain` and `checkstyleTest` for touched areas.
6. Verify SonarQube quality gate status and that blocker/critical issues introduced by the change are zero.
7. Summarize risks, behavior impact, and follow-up actions.
```

### Testing Workflow Note

- WireMock-backed integration tests now use class-level Spring context teardown via `@DirtiesContext(AFTER_CLASS)` in `WireMockBaseTest` to reduce intermittent `WireMockServer` port-bind failures.
- This improves stability but can increase test runtime because affected test classes do not reuse the same Spring context across classes.
- Build verification is split into `testUnit` (parallel unit tests) and `testIt` (serialized `*IT`/`*ITest` tests for stability).
- The default `test` task is disabled to avoid duplicate execution; `check`/`build` run `testUnit` and `testIt`.
- Smoke test execution now includes `preSmokeDiagnostics`, which logs `TEST_URL`, key auth/callback env values, and probes `${TEST_URL}/actuator/health` before BEFTA starts.
- Jenkins archives stage-specific BEFTA outputs as `target/cucumber-smoke.json` and `target/cucumber-functional.json` (plus corresponding JUnit XML), so smoke/functional failures can be triaged independently.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ ARG JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom"
ARG APP_INSIGHTS_AGENT_VERSION=3.7.7
ARG PLATFORM=""

FROM hmctspublic.azurecr.io/base/java${PLATFORM}:21-distroless
FROM hmctsprod.azurecr.io/base/java${PLATFORM}:21-distroless
USER hmcts
LABEL maintainer="https://github.com/hmcts/ccd-data-store-api"

Expand Down
22 changes: 20 additions & 2 deletions Jenkinsfile_CNP
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,20 @@ env.MAX_NUM_PARALLEL_THREADS=6
env.BEFTA_S2S_CLIENT_ID = "ccd_gw"
env.ROLE_ASSIGNMENT_API_GATEWAY_S2S_CLIENT_ID = "ccd_data"
env.BEFTA_TEST_STUB_SERVICE_BASE_URL = "http://ccd-test-stubs-service-aat.service.core-compute-aat.internal"
env.BEFTA_TEST_STUB_SERVICE_HOST = "ccd-test-stubs-service-aat.service.core-compute-aat.internal"
env.AAC_MANAGE_CASE_ASSIGNMENT_HOST = "aac-manage-case-assignment-aat.service.core-compute-aat.internal"
env.CCD_CALLBACK_ALLOWED_HOSTS = "localhost,127.0.0.1,${env.BEFTA_TEST_STUB_SERVICE_HOST},${env.AAC_MANAGE_CASE_ASSIGNMENT_HOST}"
env.CCD_CALLBACK_ALLOWED_HTTP_HOSTS = "localhost,127.0.0.1,${env.BEFTA_TEST_STUB_SERVICE_HOST},${env.AAC_MANAGE_CASE_ASSIGNMENT_HOST}"
env.CCD_CALLBACK_ALLOW_PRIVATE_HOSTS = "localhost,127.0.0.1,${env.BEFTA_TEST_STUB_SERVICE_HOST},${env.AAC_MANAGE_CASE_ASSIGNMENT_HOST}"
env.BEFTA_S2S_CLIENT_ID_OF_CCD_DATA = "ccd_data"
env.BEFTA_S2S_CLIENT_ID_OF_XUI_WEBAPP = "xui_webapp"
// BEFTA retry env variables
env.BEFTA_RETRY_MAX_ATTEMPTS = "3"
env.BEFTA_RETRY_STATUS_CODES = "500,502,503,504"
env.BEFTA_RETRY_MAX_DELAY = "1000"
env.BEFTA_RETRY_NON_RETRYABLE_HTTP_METHODS = "POST,PUT"
// Prevent Docker hub rate limit errors by ensuring that testcontainers uses images from hmctspublic ACR
env.TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX = "hmctspublic.azurecr.io/imported/"
// Prevent Docker hub rate limit errors by ensuring that testcontainers uses images from hmctsprod ACR
env.TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX = "hmctsprod.azurecr.io/imported/"

withPipeline(type, product, component) {
onMaster {
Expand All @@ -139,6 +144,7 @@ withPipeline(type, product, component) {
env.DEFINITION_STORE_URL_BASE = "https://ccd-definition-store-ccd-data-store-api-${env.BRANCH_NAME}.preview.platform.hmcts.net".toLowerCase()
env.DEFINITION_STORE_HOST = env.DEFINITION_STORE_URL_BASE
}
echo "Effective TEST_URL=${env.TEST_URL}"

echo "ES FTA Enabled = ${env.ELASTIC_SEARCH_FTA_ENABLED} on branch ${env.BRANCH_NAME}"

Expand Down Expand Up @@ -184,6 +190,9 @@ withPipeline(type, product, component) {
}

afterAlways('smoketest:preview') {
steps.sh("if [ -f target/cucumber.json ]; then cp target/cucumber.json target/cucumber-smoke.json; fi")
steps.archiveArtifacts allowEmptyArchive: true, artifacts: 'target/cucumber-smoke.json'
steps.archiveArtifacts allowEmptyArchive: true, artifacts: 'build/test-results/smoke/cucumber.xml'
steps.archiveArtifacts allowEmptyArchive: true, artifacts: '**/BEFTA Report for Smoke Tests/**/*'
publishHTML target: [
allowMissing : true,
Expand All @@ -196,6 +205,9 @@ withPipeline(type, product, component) {
}

afterAlways('smoketest:aat') {
steps.sh("if [ -f target/cucumber.json ]; then cp target/cucumber.json target/cucumber-smoke.json; fi")
steps.archiveArtifacts allowEmptyArchive: true, artifacts: 'target/cucumber-smoke.json'
steps.archiveArtifacts allowEmptyArchive: true, artifacts: 'build/test-results/smoke/cucumber.xml'
steps.archiveArtifacts allowEmptyArchive: true, artifacts: '**/BEFTA Report for Smoke Tests/**/*'
publishHTML target: [
allowMissing : true,
Expand All @@ -208,6 +220,9 @@ withPipeline(type, product, component) {
}

afterAlways('functionalTest:preview') {
steps.sh("if [ -f target/cucumber.json ]; then cp target/cucumber.json target/cucumber-functional.json; fi")
steps.archiveArtifacts allowEmptyArchive: true, artifacts: 'target/cucumber-functional.json'
steps.archiveArtifacts allowEmptyArchive: true, artifacts: 'build/test-results/functional/cucumber.xml'
steps.archiveArtifacts allowEmptyArchive: true, artifacts: '**/BEFTA Report for Functional Tests/**/*'
publishHTML target: [
allowMissing : true,
Expand All @@ -220,6 +235,9 @@ withPipeline(type, product, component) {
}

afterAlways('functionalTest:aat') {
steps.sh("if [ -f target/cucumber.json ]; then cp target/cucumber.json target/cucumber-functional.json; fi")
steps.archiveArtifacts allowEmptyArchive: true, artifacts: 'target/cucumber-functional.json'
steps.archiveArtifacts allowEmptyArchive: true, artifacts: 'build/test-results/functional/cucumber.xml'
steps.archiveArtifacts allowEmptyArchive: true, artifacts: '**/BEFTA Report for Functional Tests/**/*'
publishHTML target: [
allowMissing : true,
Expand Down
9 changes: 7 additions & 2 deletions Jenkinsfile_nightly
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ def vaultOverrides = [
// vars needed for functional tests
// Assume a feature build branched off 'develop', with dependencies develop-to-develop.
env.TEST_URL = "http://ccd-data-store-api-aat.service.core-compute-aat.internal"
// Prevent Docker hub rate limit errors by ensuring that testcontainers uses images from hmctspublic ACR
env.TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX = "hmctspublic.azurecr.io/imported/"
// Prevent Docker hub rate limit errors by ensuring that testcontainers uses images from hmctsprod ACR
env.TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX = "hmctsprod.azurecr.io/imported/"

// Other env variables needed for BEFTA.
env.BEFTA_S2S_CLIENT_ID = "ccd_gw"
Expand All @@ -96,6 +96,11 @@ env.BEFTA_RESPONSE_HEADER_CHECK_POLICY="JUST_WARN" // Temporary workaround for p
env.ELASTIC_SEARCH_FTA_ENABLED = "true"
env.DEFAULT_COLLECTION_ASSERTION_MODE="UNORDERED"
env.BEFTA_TEST_STUB_SERVICE_BASE_URL = "http://ccd-test-stubs-service-aat.service.core-compute-aat.internal"
env.BEFTA_TEST_STUB_SERVICE_HOST = "ccd-test-stubs-service-aat.service.core-compute-aat.internal"
env.AAC_MANAGE_CASE_ASSIGNMENT_HOST = "aac-manage-case-assignment-aat.service.core-compute-aat.internal"
env.CCD_CALLBACK_ALLOWED_HOSTS = "localhost,127.0.0.1,${env.BEFTA_TEST_STUB_SERVICE_HOST},${env.AAC_MANAGE_CASE_ASSIGNMENT_HOST}"
env.CCD_CALLBACK_ALLOWED_HTTP_HOSTS = "localhost,127.0.0.1,${env.BEFTA_TEST_STUB_SERVICE_HOST},${env.AAC_MANAGE_CASE_ASSIGNMENT_HOST}"
env.CCD_CALLBACK_ALLOW_PRIVATE_HOSTS = "localhost,127.0.0.1,${env.BEFTA_TEST_STUB_SERVICE_HOST},${env.AAC_MANAGE_CASE_ASSIGNMENT_HOST}"
env.DEFINITION_STORE_HOST = "http://ccd-definition-store-api-aat.service.core-compute-aat.internal"
// BEFTA retry env variables
env.BEFTA_RETRY_MAX_ATTEMPTS = "3"
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

Store/search cases and provide workbaskets.

Repo-local workflow docs are indexed in `AGENTS.md`.

### Prerequisites

- [Open JDK 21](https://openjdk.java.net/)
Expand Down Expand Up @@ -47,6 +49,12 @@ The following environment variables are required:
| DRAFT_STORE_URL | - | Base URL for Draft Store API service. `http://localhost:8800` for the dockerised local instance. |
| DRAFT_ENCRYPTION_KEY | - | Draft encryption key. The encryption key used by draft store to encrypt documents with. |
| DRAFT_TTL_DAYS | - | Number of days after which the saved draft will be deleted if unmodified. |
| CCD_CALLBACK_ALLOWED_HOSTS | localhost,127.0.0.1 | Comma-separated callback destination match patterns. Exact hosts, legacy `*.domain.tld`, `*`, and regex patterns are supported. Invalid regex-like entries fail validation explicitly. Use environment-specific callback hosts or patterns (for example local Docker: `host.docker.internal`; preview/demo PR domains: `.*\\.demo\\.platform\\.hmcts\\.net,.*\\.preview\\.platform\\.hmcts\\.net`; AAT/pipeline: internal service DNS). |
| CCD_CALLBACK_ALLOWED_HTTP_HOSTS | localhost,127.0.0.1 | Comma-separated host match patterns allowed to use `http` for callbacks. Exact hosts, legacy `*.domain.tld`, `*`, and regex patterns are supported; invalid regex-like entries fail validation explicitly; all other callback hosts must use `https`. |
| CCD_CALLBACK_ALLOW_PRIVATE_HOSTS | localhost,127.0.0.1 | Comma-separated host match patterns allowed to resolve to private/local addresses for callbacks. Exact hosts, legacy `*.domain.tld`, `*`, and regex patterns are supported; invalid regex-like entries fail validation explicitly. |

For callback hardening rollout guidance, allowlist pattern syntax, and environment examples (including preview/AAT allowlist hosts),
see [`docs/api/security.md`](docs/api/security.md).

### Building

Expand Down
4 changes: 2 additions & 2 deletions acb.tpl.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: 1.0-preview-1
steps:
- id: pull-base-image-amd64
cmd: docker pull --platform linux/amd64 hmctspublic.azurecr.io/base/java:21-distroless && docker tag hmctspublic.azurecr.io/base/java:21-distroless hmctspublic.azurecr.io/base/java/linux/amd64:21-distroless
cmd: docker pull --platform linux/amd64 hmctsprod.azurecr.io/base/java:21-distroless && docker tag hmctsprod.azurecr.io/base/java:21-distroless hmctsprod.azurecr.io/base/java/linux/amd64:21-distroless
when: ["-"]
retries: 3
retryDelay: 5
Expand All @@ -18,7 +18,7 @@ steps:
retryDelay: 5

- id: pull-base-image-arm64
cmd: docker pull --platform linux/arm64 hmctspublic.azurecr.io/base/java:21-distroless && docker tag hmctspublic.azurecr.io/base/java:21-distroless hmctspublic.azurecr.io/base/java/linux/arm64:21-distroless
cmd: docker pull --platform linux/arm64 hmctsprod.azurecr.io/base/java:21-distroless && docker tag hmctsprod.azurecr.io/base/java:21-distroless hmctsprod.azurecr.io/base/java/linux/arm64:21-distroless
when:
- pull-base-image-amd64
retries: 3
Expand Down
Loading