Skip to content

BCH-1341: Detect cross-SSP leverage drift, notify downstream, support re-attestation (Phase 5)#451

Merged
saltpy-cs merged 2 commits into
mainfrom
feat/BCH-1341
Jul 10, 2026
Merged

BCH-1341: Detect cross-SSP leverage drift, notify downstream, support re-attestation (Phase 5)#451
saltpy-cs merged 2 commits into
mainfrom
feat/BCH-1341

Conversation

@saltpy-cs

@saltpy-cs saltpy-cs commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Extends SyncExportOffering to flip stale SSPLeverageLinks to drifted on a content-changing version bump (now including a backing component's ImplementationStatus downgrade), an offering deprecation/revocation (new PATCH .../export-offerings/:id/status endpoint), or its LeveragedAuthorization being deleted ("lapsed").
  • Each drift creates/reopens a downstream-only inherited-revoked risk (linked via risk_responsibility_links and best-effort risk_control_links) and enqueues a leverage_drifted/leverage_revoked notification to the risk's owner(s).
  • New re-attestation endpoint (POST .../leveraged-controls/:linkId/attest) is the sole path that clears drift: bumps OfferingVersion, refreshes Satisfaction, and remediates the risk.
  • No upstream risk or evidence is ever exposed downstream — verified both by DB state and the actual downstream-facing JSON response shape.

Test plan

  • go build ./..., go vet ./... (incl. -tags integration), golangci-lint run ./... all clean
  • go test ./... (unit) passes
  • go test -tags integration ./internal/api/handler/oscal/... passes (real Postgres via testcontainers), including the ticket's own flow: publish v1 → subscribe → downgrade a provided status → re-sync → link drifted + inherited-revoked risk + notification enqueued; re-attest → cleared
  • Deprecate/revoke and leveraged-auth-lapse triggers covered, plus idempotency and cross-trigger dedupe
  • make swag docs regenerated

Summary by CodeRabbit

  • New Features

    • Export offerings can now be deprecated or revoked after publication.
    • Leverage links automatically enter a drifted state when offering content, status, or authorizations change.
    • Added re-attestation to restore drifted leverage links and remediate related risks.
    • Added email notifications for leverage drift and revocation events.
  • Bug Fixes

    • Prevented duplicate risk records and notifications during repeated drift triggers.
  • Documentation

    • Updated API documentation for offering status updates and related request fields.

… re-attestation (Phase 5)

Extends SyncExportOffering to flip stale leverage links to drifted (on a
content-changing version bump, offering deprecation/revocation, or its
leveraged authorization being deleted), raising a downstream-only
inherited-revoked risk and a leverage_drifted/leverage_revoked notification
without ever exposing the upstream's own risk register or evidence. A new
re-attestation endpoint is the sole path that clears drift and remediates
the risk.
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@saltpy-cs, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 11 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4e6fbb2a-e145-4253-979d-39a440a4c7c8

📥 Commits

Reviewing files that changed from the base of the PR and between 6440b18 and df39401.

📒 Files selected for processing (12)
  • docs/docs.go
  • docs/swagger.json
  • docs/swagger.yaml
  • internal/api/handler/oscal/dashboard_suggestions_integration_test.go
  • internal/api/handler/oscal/ssp_export_offerings.go
  • internal/api/handler/oscal/ssp_leverage.go
  • internal/api/handler/oscal/ssp_leverage_drift.go
  • internal/api/handler/oscal/ssp_leverage_drift_enqueue_test.go
  • internal/api/handler/oscal/ssp_leverage_reattest_test.go
  • internal/api/handler/oscal/system_security_plans.go
  • internal/service/worker/leverage_notifications.go
  • internal/service/worker/service.go
📝 Walkthrough

Walkthrough

Adds SSP export-offering status transitions, implementation-status-aware versioning, leverage drift risk handling, re-attestation, and per-owner email notifications. OpenAPI documentation, route wiring, worker registration, and unit/integration coverage are included.

Changes

SSP leverage drift lifecycle

Layer / File(s) Summary
API contracts and route wiring
docs/*, internal/authz/manifest.yaml, internal/service/relational/risks/models.go, internal/api/handler/oscal/api.go, internal/api/handler/oscal/ssp_export_offerings.go, internal/api/handler/oscal/system_security_plans.go
Adds the offering-status PATCH contract, the inherited-revoked risk source type, handler dependencies, and re-attestation route registration.
Hashing and drift risk engine
internal/api/handler/oscal/ssp_export_offerings.go, internal/api/handler/oscal/ssp_leverage_drift.go, internal/api/handler/oscal/*_test.go
Includes live implementation status in offering hashes, detects drift on version or offering-status changes, creates or reopens risks, links them to downstream entities, and validates idempotency.
Status transition and re-attestation
internal/api/handler/oscal/ssp_export_offerings.go, internal/api/handler/oscal/ssp_leverage.go, internal/api/handler/oscal/system_security_plans.go, internal/api/handler/oscal/*test.go
Adds deprecated/revoked status updates, drifts links when leveraged authorizations are deleted, and restores drifted links through re-attestation while remediating associated risks.
Drift notification pipeline
internal/service/worker/*, internal/api/handler/oscal/*enqueue_test.go
Adds per-owner River jobs and email rendering for leverage drifted and revoked events, with enqueue coverage across publish, status update, and authorization deletion flows.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Publish
  participant SyncExportOffering
  participant DriftEngine
  participant WorkerService
  participant River
  Client->>Publish: publish export offering
  Publish->>SyncExportOffering: synchronize offering
  SyncExportOffering->>DriftEngine: evaluate leverage drift
  DriftEngine-->>Publish: return drifted links
  Publish->>WorkerService: enqueue drift notifications
  WorkerService->>River: insert owner jobs
  River-->>Client: process notification asynchronously
Loading
sequenceDiagram
  participant Client
  participant ReAttest
  participant SSPLeverageLink
  participant Risk
  Client->>ReAttest: attest drifted link
  ReAttest->>SSPLeverageLink: recompute and activate link
  ReAttest->>Risk: remediate drift risk
  ReAttest-->>Client: return updated link
Loading

Possibly related PRs

Suggested reviewers: gusfcarvalho

Poem

A rabbit hops through drift and hash,
Finds risks in links that slipped their grasp.
It marks, reopens, sends mail bright,
Then re-attests to make things right.
“Patch complete!” the bunny sings.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.42% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change set: cross-SSP leverage drift detection, downstream notification, and re-attestation support.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@saltpy-cs

Copy link
Copy Markdown
Contributor Author

@CodeRabbit

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

@saltpy-cs I see you mentioned me, but I don't have a specific request to act on yet. Could you let me know what you'd like me to help with? For example, I can:

  • Review the current changes in this PR ()
  • Regenerate the PR summary ()
  • Answer questions about the SyncExportOffering drift logic, the new PATCH .../export-offerings/:id/status or POST .../leveraged-controls/:linkId/attest endpoints, or anything else in the test plan
  • Help with CodeRabbit configuration questions

Just let me know how I can help!

(=^・ェ・^=) ?

✅ Action performed

Summary regeneration triggered.

@saltpy-cs

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/docs.go`:
- Around line 38947-38954: Update the Go request struct corresponding to
oscal.updateOfferingStatusRequest, locating its status field, to mark it
required with binding:"required" and constrain it with the swaggo enum
annotation enums=deprecated,revoked. Then rerun swag init to regenerate the docs
artifacts, including the schema metadata in docs.go and the corresponding
swagger JSON/YAML files; do not edit generated files directly.

In `@docs/swagger.json`:
- Around line 38941-38948: Document the allowed values on the Status field of
the relevant Go request struct in ssp_export_offerings.go using the swaggo
enum/description annotation, matching validate()’s "deprecated" and "revoked"
constraints. Then rerun swag init to regenerate docs/swagger.json; do not edit
the generated schema directly.

In `@internal/api/handler/oscal/ssp_export_offerings.go`:
- Around line 739-743: Extract the duplicated drift-notification enqueue logic
into a shared helper near enqueueLeverageDriftNotifications, such as
enqueueLeverageDriftNotificationsAsync, accepting the request context, logger,
enqueuer, and drifted links. Have it return for empty links, detach cancellation
with context.WithoutCancel, apply the shared 10-second timeout, defer
cancellation, and invoke enqueueLeverageDriftNotifications. Replace the repeated
blocks in Publish, UpdateOfferingStatus, and
DeleteSystemImplementationLeveragedAuthorization with calls to this helper.

In `@internal/api/handler/oscal/ssp_leverage.go`:
- Around line 797-849: Prevent the TOCTOU race in the re-attestation transaction
by adding a drifted-status predicate to the SSPLeverageLink update in the
transaction callback. Update the Where clause to require both the link ID and
status relational.SSPLeverageStatusDrifted, then check RowsAffected and return
an appropriate conflict/not-found error when no row was updated, ensuring
concurrent requests or newly drifted links cannot be silently overwritten.

In `@internal/service/worker/leverage_notifications.go`:
- Around line 63-68: Replace the substring check in notificationKindForReason
with an explicit mapping of known reason strings to notification kinds, using a
switch or map; preserve the existing default behavior for unrecognized reasons
by returning leverageDriftedNotificationKind.

In `@internal/service/worker/service.go`:
- Line 1117: Remove the unused downstreamSSPID parameter from
Service.EnqueueLeverageDriftNotification and update the SSPJobEnqueuer interface
plus all callers and implementations to match, unless it is intentionally
required for future use; rely on the risk's SSPID field when constructing
LeverageDriftNotificationArgs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 810e209c-08ee-40c9-ac95-85b122cc6837

📥 Commits

Reviewing files that changed from the base of the PR and between 57d0226 and 6440b18.

📒 Files selected for processing (24)
  • docs/docs.go
  • docs/swagger.json
  • docs/swagger.yaml
  • internal/api/handler/oscal/api.go
  • internal/api/handler/oscal/dashboard_suggestions_integration_test.go
  • internal/api/handler/oscal/ssp_export_offering_status_test.go
  • internal/api/handler/oscal/ssp_export_offerings.go
  • internal/api/handler/oscal/ssp_export_offerings_unit_test.go
  • internal/api/handler/oscal/ssp_leverage.go
  • internal/api/handler/oscal/ssp_leverage_drift.go
  • internal/api/handler/oscal/ssp_leverage_drift_enqueue_test.go
  • internal/api/handler/oscal/ssp_leverage_drift_integration_test.go
  • internal/api/handler/oscal/ssp_leverage_drift_test.go
  • internal/api/handler/oscal/ssp_leverage_reattest_test.go
  • internal/api/handler/oscal/ssp_leverage_unit_test.go
  • internal/api/handler/oscal/system_security_plans.go
  • internal/api/handler/oscal/system_security_plans_leveraged_auth_drift_test.go
  • internal/authz/manifest.yaml
  • internal/service/relational/risks/models.go
  • internal/service/worker/jobs.go
  • internal/service/worker/leverage_notifications.go
  • internal/service/worker/leverage_notifications_test.go
  • internal/service/worker/risk_notifications.go
  • internal/service/worker/service.go

Comment thread docs/docs.go
Comment thread docs/swagger.json
Comment thread internal/api/handler/oscal/ssp_export_offerings.go Outdated
Comment thread internal/api/handler/oscal/ssp_leverage.go
Comment thread internal/service/worker/leverage_notifications.go
Comment thread internal/service/worker/service.go Outdated
…ueue logic, a re-attestation race, and a fragile reason-string match

Documents the two valid updateOfferingStatusRequest.Status values in swagger,
extracts the repeated drift-notification enqueue block into one helper,
closes a TOCTOU window where a stale re-attestation could silently overwrite
a link that had already been cleared or re-drifted, replaces a substring
match with an explicit reason-to-kind mapping, and drops an unused parameter
from the notification enqueuer interface.
@saltpy-cs saltpy-cs merged commit 0c6e943 into main Jul 10, 2026
5 checks passed
@saltpy-cs saltpy-cs deleted the feat/BCH-1341 branch July 10, 2026 14:52
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