Skip to content

feature: pipeline governance hardening — secrets, build provenance, Flyway schema control, change control, audit logging - #294

Open
achalc wants to merge 3 commits into
DevOpsfrom
devin/1787578544-govern-pipeline-hardening
Open

feature: pipeline governance hardening — secrets, build provenance, Flyway schema control, change control, audit logging#294
achalc wants to merge 3 commits into
DevOpsfrom
devin/1787578544-govern-pipeline-hardening

Conversation

@achalc

@achalc achalc commented Aug 24, 2026

Copy link
Copy Markdown

Change summary

Governance / DevSecOps hardening of the pipeline and the runtime controls around it. No money-movement logic is changed — every change closes an ITGC control gap and is traceable to it.

Change record / ticket: Western Union governance review (demo #4)

Control Gap before Change
ITGC-SEC-06 — no committed credentials application.properties shipped root / Test@123; the same literal was in docker-compose.yml, kubernetes/secrets.yaml (base64) and helm/bankapp/values.yaml Credentials come from the environment only (SPRING_DATASOURCE_USERNAME / _PASSWORD, no defaults → fail-fast startup). Compose uses ${VAR:?...}, the Helm template uses required, kubernetes/secrets.yaml is replaced by a value-free secrets.[REDACTED SECRET].yaml. Contract in docs/configuration-contract.md; .env.[REDACTED SECRET] + .gitignore for local use. A blocking gitleaks stage runs in both Jenkins and GitHub Actions with a repo rule (.gitleaks.toml) that treats any MYSQL_ROOT_PASSWORD / spring.datasource.password assignment as a finding unless the value is resolved outside the repo ($..., {{ }}, <placeholder>, empty).
ITGC-CM-08 — build provenance Jenkinsfile and GitOps/Jenkinsfile checked out LondheShubham153/Springboot-BankApp — the pipeline was not building the audited repository Both pipelines check out https://github.com/COG-GTM/Springboot-BankApp.git via a single APP_REPO_URL / MANIFEST_REPO_URL env var, and the CD job pushes back to the same URL. The CD manifest sed also targeted bankapp-deployment.yaml, a file that does not exist (.yml), so the image tag was silently never updated; fixed and asserted with a grep -q.
ITGC-SDLC-09 — scan/test gate Trivy, OWASP and Sonar reported findings and the build continued; no tests ran in the pipeline Trivy blocks at HIGH,CRITICAL for vulnerabilities and secrets and at CRITICAL for infrastructure misconfiguration, OWASP runs with --failOnCVSS 7, Sonar with waitForQualityGate abortPipeline: true, plus ./mvnw -B clean verify. Jenkins and GitHub Actions run the same pinned aquasec/trivy:0.71.2 container so a finding in one reproduces in the other. Reports are archived as build evidence. Nothing is waived: the 8 pre-existing HIGH pod-security findings in kubernetes/ are reported and listed as follow-on work below instead of being silenced with a .trivyignore.
ITGC-DATA-10 — schema control spring.jpa.hibernate.ddl-auto=update let the running application mutate the production schema Flyway owns the schema: baseline V1__baseline_bankapp_schema.sql matching the current entities, ddl-auto=validate, and a flyway-maven-plugin configuration so a DBA can apply migrations without starting the app (./mvnw flyway:migrate).
ITGC-CM-07 — author != approver No CODEOWNERS, no PR template .github/CODEOWNERS (per-area owners for services, config, migrations, pipelines, k8s/Helm) and a PR template carrying the control attestations. This is only enforceable once branch protection on DevOps requires code-owner review, ≥1 approval and disallows bypass — that setting is outside the repo and still needs to be applied.
ITGC-LOG-11 — audit trail Financial events left no trace beyond the balance rows Structured single-line JSON audit events on a dedicated audit logger for deposit / withdrawal / transfer, success and failure, with actor, account ids, amount, currency, reason, timestamp and a correlation id (X-Correlation-Id, sanitised/generated by CorrelationIdFilter). No passwords, balances or full PII are logged; injected control characters are escaped so one event stays one record.
ITGC-SDLC-09 / CM-07 — PR CI Nothing ran on a PR .github/workflows/ci.yml runs on every PR and push to DevOps: gitleaks, then build + migrate-on-a-clean-MySQL + tests, then Trivy secret/misconfig (blocking) and dependency scanning.

Change classification

  • Standard (pre-approved, low risk)
  • Normal (requires review + approval before merge)
  • Emergency (retrospective approval; state the incident reference)

Control attestations

  • ITGC-CM-07 — I am not the approver of this change; an independent code owner will review it.
  • ITGC-SEC-06 — No credentials, tokens or keys are committed; configuration is read from the environment. The gitleaks job is green.
  • ITGC-DATA-10 — Any schema change is an append-only Flyway migration under src/main/resources/db/migration/; no existing migration was edited and spring.jpa.hibernate.ddl-auto remains validate.
  • ITGC-SDLC-09 — Tests and security scans pass in CI; scanner findings above threshold are fixed, not waived.
  • ITGC-LOG-11 — Financial or security-relevant behaviour changes emit audit events, and no secrets or full PII are written to logs.
  • ITGC-CM-08 — Build provenance unchanged: the pipeline still builds this repository.

Money movement impact

  • This change affects deposit, withdraw or transfer behaviour.

Balances, limits and authorisation are untouched; AccountService gains audit calls only.

Rollback plan

Revert the commit. The migration is backward compatible: V1 creates the existing tables IF NOT EXISTS and only adds a flyway_schema_history table, so an environment that already has the schema is baselined rather than altered; reverting simply stops Flyway from running. Deployments must keep the datasource environment variables in place — after this change the application will not start with a missing credential (by design).

Evidence

Build and tests (local, MySQL 8.0):

[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 -- com.[REDACTED SECRET].bankapp.BankappApplicationTests
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0 -- com.[REDACTED SECRET].bankapp.service.AccountServiceAuditTest
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0 -- com.[REDACTED SECRET].bankapp.audit.AuditLoggerTest
[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS

Migrations against a clean database (CREATE DATABASE bankapp_clean./mvnw flyway:migrate), after which the app started with ddl-auto=validate, i.e. Hibernate validated the Flyway-built schema:

[INFO] Creating Schema History table `bankapp_clean`.`flyway_schema_history` ...
[INFO] Migrating schema `bankapp_clean` to version "1 - baseline bankapp schema"
[INFO] Successfully applied 1 migration to schema `bankapp_clean`, now at version v1

version  description              success
1        baseline bankapp schema  1

Audit log output — real requests against the running app (deposit, withdrawal, rejected withdrawal, transfer, transfer to an unknown recipient), correlation ids supplied by the caller:

{"timestamp":"2026-08-24T13:38:55.182346675Z","event":"DEPOSIT","outcome":"SUCCESS","actor":"alice","accountId":"1","amount":"250.00","currency":"USD","correlationId":"wu-demo-0001"}
{"timestamp":"2026-08-24T13:38:55.197296379Z","event":"WITHDRAWAL","outcome":"SUCCESS","actor":"alice","accountId":"1","amount":"40.00","currency":"USD","correlationId":"wu-demo-0002"}
{"timestamp":"2026-08-24T13:38:55.204298531Z","event":"WITHDRAWAL","outcome":"FAILURE","actor":"alice","accountId":"1","amount":"10000.00","currency":"USD","reason":"INSUFFICIENT_FUNDS","correlationId":"wu-demo-0003"}
{"timestamp":"2026-08-24T13:38:55.274072014Z","event":"TRANSFER","outcome":"SUCCESS","actor":"alice","accountId":"1","counterpartyAccountId":"2","amount":"75.00","currency":"USD","correlationId":"wu-demo-0004"}
{"timestamp":"2026-08-24T13:38:55.283350993Z","event":"TRANSFER","outcome":"FAILURE","actor":"alice","accountId":"1","amount":"5.00","currency":"USD","reason":"RECIPIENT_NOT_FOUND","correlationId":"wu-demo-0005"}

Secret gate proves it blocks, not just passes — clean tree scans green; re-adding spring.datasource.password=Test@123 to a properties file fails the scan:

$ gitleaks detect --no-git --config .gitleaks.toml --exit-code 1     # clean tree
INF no leaks found

$ echo 'spring.datasource.password=Test@123' > leaktest.properties && gitleaks detect ...
File: /repo/leaktest.properties
WRN leaks found: 1        (exit 1)

Follow-on gaps left in this PR (money-movement controls, deliberately out of scope)

Control Gap Location
APP-TXN-01 — amount validation No positive/bounds check: a negative amount on deposit inflates the balance and a negative transfer drains the recipient. BigDecimal amounts are taken straight from the request. src/main/java/com/[REDACTED SECRET]/bankapp/service/AccountService.java:60 (deposit), :79 (withdraw), :125 (transfer); unvalidated request binding at src/main/java/com/[REDACTED SECRET]/bankapp/controller/BankController.java:51, :59, :83
APP-TXN-02 — atomic transfer The transfer performs four separate save calls with no @Transactional boundary; a failure between the debit and the credit destroys money. src/main/java/com/[REDACTED SECRET]/bankapp/service/AccountService.java:125-163 (debit at :138-139, credit at :142-143)
APP-TXN-03 — limits / maker-checker No per-transaction or daily limit and no second-person approval on any movement; a single authenticated session can move an unbounded amount. src/main/java/com/[REDACTED SECRET]/bankapp/service/AccountService.java:125
APP-ACC-04 — access control / SoD Single implicit role, everything behind anyRequest().authenticated(); no separation between customer and operator actions. src/main/java/com/[REDACTED SECRET]/bankapp/config/SecurityConfig.java:31-33
APP-SEC-05 — CSRF CSRF protection is disabled, so the state-changing POST /deposit, /withdraw, /transfer endpoints are forgeable. src/main/java/com/[REDACTED SECRET]/bankapp/config/SecurityConfig.java:30

| INFRA-K8S-06 — pod security context | 8 HIGH Trivy misconfiguration findings on the existing manifests: containers run with the default (root-capable) security context, no runAsNonRoot, no resource limits. Reported by CI, not yet fixed. | kubernetes/mysql-deployment.yml:17-41, kubernetes/bankapp-deployment.yml |

Also outstanding and outside a code change: the credentials in this repository's git history are disclosed and must be rotated (docs/configuration-contract.md#rotation), and branch protection on DevOps must be set to require code-owner review before CODEOWNERS enforces ITGC-CM-07.

Devin-Org: engineering

Link to Devin session: https://app.devin.ai/sessions/f4b7908a8b434230afc87c28e95e3f68
Requested by: @achalc


Devin Review

Status Commit
⚪ Not started

Run Devin Review

💡 Connect your GitHub account to enable automatic code reviews.

Devin Review (Staging)

…nge control, audit logging)

- ITGC-SEC-06: remove committed DB credentials from application.properties,
  docker-compose, Kubernetes and Helm; add a blocking gitleaks gate in Jenkins and GitHub Actions
- ITGC-CM-08: build and GitOps pipelines now check out the audited COG-GTM repository
- ITGC-SDLC-09: Trivy/OWASP/Sonar findings above threshold and failing tests break the build
- ITGC-DATA-10: replace hibernate ddl-auto=update with a Flyway baseline migration
- ITGC-CM-07: add CODEOWNERS and a PR template so author != approver is enforceable
- ITGC-LOG-11: structured audit logging of deposits, withdrawals and transfers with correlation ids
@devin-ai-integration

Copy link
Copy Markdown

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

…on gate

The trivy-action binary install failed on the runner; using the same pinned
aquasec/trivy image as Jenkins makes CI and Jenkins reproduce each other.
Secrets and vulnerabilities block at HIGH,CRITICAL; infrastructure
misconfiguration blocks at CRITICAL, with the pre-existing HIGH Kubernetes
pod-security findings reported rather than waived.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant