Conversation
Adds the depth and audit trail Chris asked for so auditors can trust the automated checks. Five phases shipped together: Phase 1 — Quick wins - Evidence JSON viewer on each finding (sensitive keys redacted via evidence-sanitizer.ts — suffix-match strategy preserves booleans/numbers) - Last-scan metadata strip (47 checks, 41 passed, 6 failed, 3.2s) - Resource labels on every finding row (IAM User: john, S3 Bucket: …) - Rename "Security Findings" header + tab to "Scan Results" Phase 2 — AI check descriptions - Tier 3 panel (what / pass / fail / why) generated lazily on first expand - Claude Haiku 4.5 via @ai-sdk/anthropic, cached per (orgId, checkId) with source-hash invalidation (regenerates when adapter strings change) - Server-side strip of compliance control numbers + URLs as a backstop - GCP/Azure: passthrough from provider catalog (no AI call) Phase 3 — Per-check sub-grouping - Findings within a service grouped by normalized checkKey - "X of Y failing" headers; "Show all results" reveals passing rows - 100-row display cap per check with "Show more" affordance Phase 4 — Comments on auditor findings - CommentsPermissionGuard resolves entityType-specific permission so auditors (finding:update, no task:update) can comment on findings - Comments thread on /overview/findings detail sheet Phase 5 — Resolution + exceptions history - FindingException / FindingResolution / FindingRegression tables - Reconciliation classifies each resolution: platform_fix (RemediationAction matched), external_fix, resource_deleted, or exception_marked - scannedServices column on IntegrationCheckRun prevents false "resolved" events on partial scans - "Mark as exception" modal: required reason (20+ chars), optional reviewer, optional auto-review date - New History tab: summary + resolutions + active exceptions + regressions Includes 96 new tests (59 api + 37 frontend) and 3 Prisma migrations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Real bugs flagged by cubic on PR #2838: - comments-permission.guard.ts: API key / service-token requests were granted without any scope check (authorization bypass). Now mirrors the standard PermissionGuard's API-key + service-token enforcement, but with the dynamic `${entityType}:${action}` scope so finding-permission scopes work end-to-end. - evidence-sanitizer.ts: snake_case credential keys (`access_key_id`, `secret_access_key`) and arrays under sensitive keys (e.g. `tokens: [...]`) bypassed redaction. Now normalizes case-separators before matching and redacts string/object elements of arrays under sensitive keys. Added plural variants (tokens / secrets / cookies / accessKeys / signingKeys / sessionTokens / publicKeys / privateKeys / apiKeys / passwords). - exception.service.ts: concurrent markAsException could create duplicate active exceptions. New `@@unique([orgId, connectionId, checkId, resourceId])` constraint + Prisma upsert guarantees a single row per finding atomically. Re-marking after revoke clears `revokedAt` on the same row; full history still lives in AuditLog. - reconciliation.service.ts: idempotency probe only checked FindingResolution — a run that produced only regressions could be re-reconciled and write duplicate regression rows. Now checks both tables. - ai-description.prompt.ts: forbidden-content backstop missed bare control citations like "CIS 1.8" / "PCI 8.2.3" / "NIST AC-2" / "HIPAA 164.312". Added a broader regex catching `(FRAMEWORK) [LETTERS]?[- ]?N(.N)*`. Smaller cleanups also flagged: - MarkExceptionModal / cloud-security.controller: date-only exception expiry was parsed as UTC midnight, expiring exceptions ~8h before the user-selected local date. Frontend now computes `min` from local date, backend now expands bare `YYYY-MM-DD` to end-of-day UTC. - EvidenceJsonViewer copy button: added focus-visible styles so keyboard users can see the focused control. - exception.service.spec.ts: revocation test now asserts revokedAt is a Date in the expected window (caught the missing assertion). Skipped from cubic's list: cloud-security-query.legacy.ts filter — that code is pre-existing and was not introduced by this PR. Test counts after fixes: 105 (was 96). 4 Prisma migrations now. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…cleanup Two related follow-ups to the cubic review: 1. Legacy result filter (cubic finding #6 — earlier deferred as pre-existing, now fixed since the cwd is right here): `getLegacyFindings` used `completedAt <= lastRunAt && completedAt >= lastRunAt - 10min` to keep only the most-recent scan's IntegrationResult rows. This silently hid the newest legacy failures whenever a scan wrote results but did NOT advance `Integration.lastRunAt` (a known consistency gap on the legacy path). Extracted the filter as a pure `filterToLatestScanResults` helper that takes the LATER of `lastRunAt` and the per-integration max result `completedAt` as the reference point. Robust against either signal being stale. Added 8 unit tests (per-integration scoping, fallback when lastRunAt is null, window exclusion, mixed-age data, future-date edge). 2. Prisma migration cleanup: The previous `finding_exception_unique` migration was created manually (via raw SQL file) because `prisma migrate dev` requires a TTY and couldn't run from this environment. `prisma migrate diff` flagged two small drift issues afterwards: - the manual `CREATE UNIQUE INDEX` left an orphan `FindingException_checkId_resourceId_idx` (now subsumed by the four-column unique key — should be dropped) - the index name didn't match what Prisma's @@unique directive expects (truncated differently at the 63-char Postgres limit) New migration `finding_exception_index_cleanup`: - drops the orphan idx - renames the unique index to Prisma's expected name (`<Model>_<col1>_<col2>..._key`) `prisma migrate status` reports the schema is up to date and `prisma migrate diff --from-config-datasource ... --to-schema ...` returns an empty migration — drift resolved. Test counts: 113 (was 105 — added 8 legacy-filter tests). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…erated one You were right — the two manually-written migrations from prior commits (`finding_exception_unique` + `finding_exception_index_cleanup`) should be collapsed into a single migration generated the normal way. What I did: - Rolled back the worktree DB to the pre-manual state (dropped the manual unique index, restored the original `_checkId_resourceId_idx`, removed the two migration rows from `_prisma_migrations`) - Deleted both manual migration directories - Ran `prisma migrate dev` properly (used `expect(1)` to handle Prisma's interactive `Are you sure?` prompt — the CLI refuses to proceed non-interactively when adding a unique constraint that could fail on pre-existing duplicates) - Prisma generated `20260513231103_finding_exception_unique` with the exact SQL it would normally produce: DROP the now-redundant idx and CREATE UNIQUE INDEX with the conventional truncated name End state: one clean migration instead of two manuals. `prisma migrate status` → up to date. `prisma migrate diff --from-config-datasource ... --to-schema ...` returns `This is an empty migration.` → zero drift. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…y validation
Two more cubic findings, both real:
1. evidence-sanitizer.ts (P1): `redactArray` handled string and object
elements but not nested arrays — they fell through `return item`
unchanged. Payloads like `{ secrets: [["s1","s2"], ["s3"]] }` would
leak the inner strings. Now recursively redacts.
2. exception-expiry parsing (P2): bare `YYYY-MM-DD` strings like
"2026-02-30" passed straight into `new Date(Date.UTC(2026, 1, 30))`,
which silently rolled forward to March 2 — an exception meant to
expire Feb 30 would actually expire days later than the user expected.
Extracted to `exception-expiry.utils.ts` with strict component
validation that throws BadRequestException for non-existent calendar
dates (Feb 30, month 13, day 0, Feb 29 in non-leap years, etc.).
Test counts: 122 (was 113 — added 1 nested-array sanitizer test + 8
expiry tests).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The strip showed `Ran Xd ago • N checks evaluated • N passed • N failed` above the three stat cards. The cards already convey the counts, and the connection-detail line above the tab already shows `Last scan: …`, so the strip was double-rendering most of its information. It could also show numbers that didn't match the cards when an org has multiple connections of the same provider — the strip's `latestRun` came from this connection's IntegrationCheckRun, while the cards count findings from `providerSlug === X OR connectionId === Y` (a pre-existing OR filter in CloudTestsSection that aggregates across provider siblings). Confusing for users testing multi-connection orgs. Removes: - `<LastScanStrip />` render call - LastScanStrip component and its formatRelativeTime / formatDuration helpers (only used by the strip) - `latestRun` prop on CloudTestsSection + the `latestRun` pass-through in ProviderTabs Keeps the backend `latestRun` field on the providers API response and the `ProviderLatestRun` type — both are still emitted (cheap, harmless) in case a future view wants this data without re-plumbing the API. The only piece of unique info lost is the scan duration (~3.2s style), which wasn't surfaced anywhere else. If we want it back, a tighter "Duration X" pill near the Scan button is a follow-up — separate from the redundancy fix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…compai/comp into tofik/cloud-tests-auditor-visibility
… view The shape mismatch between the primary Fix pill (accent-filled, with icon) and the Mark-as-exception button (rectangular, outlined) made every failing row look visually noisy. Worse, both competing for the same right-side action area pushed the severity badge around. Marking a finding as an exception is meant to be a DELIBERATE action — the user must provide a written reason that an auditor will read. So forcing them to expand the row first (read the title, the description, the evidence, the remediation) before they can mark it accepted is actually better auditor-experience UX, not worse. - Removes the row-level "Mark as exception" button - Adds it to the bottom of the expanded panel, right-aligned, separated by a divider so it reads as a discrete final action - All the modal wiring stays exactly the same — same canMarkException gate (`integration:update` permission), same onMarkException callback, same MarkExceptionModal opens Row right side is now just [Fix] [severity-badge] — clean and consistent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…emediations
For findings like "No CloudTrail trails configured" or "No GuardDuty
detector", the AI fix-plan dialog rendered CURRENT and PROPOSED as
empty {} boxes — the user couldn't see what the fix was about to create.
Root cause: the SYSTEM_PROMPT for fix plans tells the model to populate
currentState/proposedState ONLY with fields from the scan evidence, and
to "show absence as false or null". For mutate-existing-resource
remediations that worked fine. But for create-from-scratch remediations
the evidence has nothing to describe (the resource doesn't exist), so
the model returned `{}` for both — collapsing to an empty diff.
Adds a new "CREATE-FROM-SCRATCH REMEDIATIONS" section to the prompt
that:
- Requires currentState to include `"exists": false` (plus other known
absence facts) so the user sees that nothing is there today.
- Requires proposedState to describe the resource that WILL be created
using the same values the model is putting into its fixSteps
(CreateTrail name, multi-region flag, log validation, bucket name,
etc.) — concrete, not abstract.
- Explicitly forbids returning empty objects for both. At minimum the
model must emit `{ exists: false }` / `{ exists: true }` so the
user sees a readable false→true diff.
No schema change — `currentState`/`proposedState` are already
`z.record(z.string(), z.unknown())`. No UI change either — the existing
StateBlock renders whatever JSON the AI returns. The fix is purely on
the prompt side.
Other findings likely affected by the same pattern (now improved):
"No multi-region CloudTrail", "No AWS Config recorder", "No GuardDuty
detector", "No CloudWatch log group for X", "No S3 bucket configured
as the log target", etc.
Reported by sales via Tofik — empty CURRENT/PROPOSED boxes on the
"No CloudTrail trails configured" Auto-Remediate dialog.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…n fix-plan dialog
The previous prompt change asked the AI to emit `{ exists: false }` /
`{ exists: true, ... }` for create-from-scratch findings, but the model
doesn't reliably follow it — Tofik still sees `{} → {}` in the
Auto-Remediate Finding dialog for "No CloudTrail trails configured"
even with the new prompt in place.
Server-side backstop: after `generateFixPlan` and `refineFixPlan`
return, if BOTH currentState and proposedState are empty, fill them in
deterministically. `currentState` becomes `{ exists: false }`.
`proposedState` becomes `{ exists: true }` or, when the plan's fixSteps
include any `Create*Command` ops, `{ exists: true, willCreate:
["service:Resource", ...] }` derived from those steps. Never depends on
the model.
Only kicks in when BOTH sides are empty — verify-only plans that
legitimately have one side blank are untouched. Mutate-existing plans
(IAM, S3 versioning, etc.) already have rich state, so they're
untouched too. Risk of regression on the working cases: zero.
Includes ai-remediation.service.spec.ts (4 tests):
- CloudTrail-style with Create* steps → currentState { exists: false },
proposedState { exists: true, willCreate: ["cloudtrail:Trail",
"s3:Bucket"] }
- Plan with no Create* steps → proposedState falls back to `{ exists:
true }` without a willCreate list
- Plan with non-empty currentState → untouched
- Plan with one side empty → untouched (legitimate verify-only)
The prompt change from 11d717f stays in — it's still useful when the
model does follow it (richer output than the backstop). This fix
guarantees a readable diff regardless of model behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…remediation rendering
GCP/Azure passthrough was using the SCC `description` for both
"description" and "fail criteria", causing the same paragraph to
appear under multiple labels in the expanded finding view. Each of
the four fields (description, passCriteria, failCriteria,
whyItMatters) now derives a distinct sentence from the machine-
readable category code.
Remediation was rendered as a single paragraph that included the
reference URL and compliance metadata inline. A new
`parseRemediation` helper splits the API-concatenated string back
into `{ steps, referenceUrl, compliance[] }`, and a new
`RemediationSection` component renders steps as readable text, the
reference as a link, and frameworks as chips.
Also tightened the visual hierarchy of the expanded view so the
"about this check" card, the per-account failure, the evidence
viewer, and the remediation card each have a distinct treatment
instead of looking like five identical white boxes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…chEmptyState
P1 — RemediationSection now validates the reference URL scheme before
assigning it to an href. `parseRemediation` extracts the URL from
provider-supplied text (today: SCC's externalUri); a new `safeHttpUrl`
helper only honors http(s) URLs and rejects javascript:, data:, vbscript:,
file:, and protocol-relative URLs. The link is hidden when validation
fails so we never render an unsafe href.
P2 — enrichEmptyState was firing for every {}/{} plan, including
update-style remediations (e.g. UpdateAccountPasswordPolicyCommand),
fabricating `currentState: { exists: false } → proposedState: { exists: true }`
which told the UI "we'll create this resource" when the truth was
"we'll update the existing one". The backstop now only enriches when
at least one `Create*` command is present in fixSteps. Update-style
plans are left untouched.
Tests:
- 10 new safeHttpUrl tests (parser test file)
- Updated the existing backstop test that asserted the wrong behavior
- Added `purpose` to fix-step test data to satisfy the strict schema
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Eight real issues from cubic's review on 79bfb0f. Two findings about Jest hoisting (exception/reconciliation specs) skipped — those are babel-jest's rule but the project uses ts-jest, where the pattern works and all 145 tests currently pass. P3 — "All passed" badge unreachable CloudTestsSection.tsx used `group.findings.length > 0` for hasFailures, but `findings` is the merged failed+passed set, so groups with only passing checks were never recognized. Use `group.failed > 0` instead. P2 — Dangling FK on FindingRegression.previousResolutionId Schema documented it as an FK but didn't declare a relation. Added `@relation(..., onDelete: SetNull)` plus a back-relation on FindingResolution.regressions, with a Prisma-generated migration that adds the constraint. P2 — DTO accepted whitespace-only exception reasons @minlength(20) counts spaces. Added @matches(/(?:\\S.*?){20}/s) so twenty literal spaces no longer satisfy the contract. P2 — Placeholder leak in fix-plan prompt CREATE-FROM-SCRATCH example used `<accountId>` while the prompt forbids placeholders. Replaced with a concrete account ID + region pair so the model has a clean reference. P2 — exception-expiry fallback was too permissive `new Date(input)` accepted locale-specific strings like "January 1, 2026" and "2026/08/13", bypassing the documented ISO 8601 contract. Added a strict ISO 8601 regex check ahead of the Date constructor; spec updated to assert the new strict behavior. P2 — History tab silently truncated rows Service capped resolutions/exceptions/regressions at 200/100 with no totals. Now returns true counts from separate `count()` queries plus a `truncated` flag per category; HistoryTab renders "shown of total" when truncation is active so auditors don't see misleading numbers. P2 — CommentsPermissionGuard missing try/catch around checkPermission Standard PermissionGuard wraps the call so network/auth-service failures convert to ForbiddenException('Unable to verify permissions') rather than leaking 500s. Mirrored that here. P2 — platform_fix attribution scope too broad Reconciliation looked up RemediationActions by connection + resource alone, so a fix for check A on a resource was mis-attributed to check B on the same resource. Plumbed `priorCheckResultId` through indexResults → determineResolutionMethod and now filter by the specific `checkResultId` instead. Spec updated to populate `id` on test results. Test counts: 145 API tests + 20 parser tests pass; the single failing suite (remediation.controller.spec.ts) is the pre-existing Postgres TLS environmental issue, unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ibility feat(cloud-tests): auditor visibility improvements (phases 1-5)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
Contributor
There was a problem hiding this comment.
5 issues found across 64 files
Confidence score: 3/5
- There is concrete regression risk: in
apps/app/src/app/(app)/[orgId]/cloud-tests/components/CloudTestsSection.tsx, theFix Alltargeting can now include passed findings, which is user-facing and could trigger unintended batch actions. - The highest-severity issue is in
apps/api/src/cloud-security/reconciliation.service.spec.ts: the hoistedjest.mock()referencingdbMockmay break test execution unless the mock variable naming/placement is adjusted. apps/api/src/cloud-security/exception-expiry.utils.tshas environment-dependent datetime parsing without explicit timezone offsets, which can produce inconsistent expiry behavior across servers.- Pay close attention to
apps/app/src/app/(app)/[orgId]/cloud-tests/components/CloudTestsSection.tsx,apps/api/src/cloud-security/reconciliation.service.spec.ts, andapps/api/src/cloud-security/exception-expiry.utils.ts- unintended fix targeting, mock hoisting safety, and timezone-sensitive expiry logic are the key risks.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/api/src/cloud-security/exception-expiry.utils.ts">
<violation number="1" location="apps/api/src/cloud-security/exception-expiry.utils.ts:50">
P2: Require an explicit timezone offset for timestamp inputs. Allowing timezone-less datetimes causes environment-dependent expiry values when `new Date(input)` parses in server local time.</violation>
</file>
<file name="apps/api/src/cloud-security/reconciliation.service.ts">
<violation number="1" location="apps/api/src/cloud-security/reconciliation.service.ts:127">
P2: Redundant condition: `!prior.passed === false` is equivalent to `prior.passed` (due to `!` binding tighter than `===`), making it a duplicate of the next line `if (prior.passed) continue`. Remove this line — it adds confusion and likely masks a logic error the author originally intended (e.g., filtering on a different field).</violation>
</file>
<file name="apps/app/src/app/(app)/[orgId]/cloud-tests/components/CloudTestsSection.tsx">
<violation number="1" location="apps/app/src/app/(app)/[orgId]/cloud-tests/components/CloudTestsSection.tsx:359">
P1: `Fix All` now includes passed findings because service groups store passed+failed results, but batch target selection still filters only by remediation key.</violation>
</file>
<file name="apps/api/src/cloud-security/ai-description.prompt.ts">
<violation number="1" location="apps/api/src/cloud-security/ai-description.prompt.ts:109">
P2: Make the ISO control-number regex case-insensitive so lowercase variants are also blocked.</violation>
</file>
<file name="apps/api/src/cloud-security/reconciliation.service.spec.ts">
<violation number="1" location="apps/api/src/cloud-security/reconciliation.service.spec.ts:24">
P1: Rename `dbMock` to a `mock*` variable (or inline the factory) so the hoisted `jest.mock()` can access it safely.</violation>
</file>
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
…mber "Confirm exemption" was failing for every customer with a "Failed to confirm exemption" toast. Root cause: apps/api/src/main.ts sets `forbidNonWhitelisted: true` on the global ValidationPipe. The V1 frontend sends `backgroundCheckExempt` together with `backgroundCheckExemptReason` + `backgroundCheckExemptJustification`, but the latter two were never declared on UpdatePeopleDto — so the PATCH /v1/people/:id request was rejected with 400 before the service ever ran. This fix: 1. Adds nullable columns `backgroundCheckExemptReason` (varchar) and `backgroundCheckExemptJustification` (text) to the Member model (named prisma migrate dev migration — not hand-written SQL). 2. Whitelists both fields on UpdatePeopleDto as optional strings with sensible length caps (100 / 2000 chars). 3. Persists both fields on member.update; clears them to null when backgroundCheckExempt is set to false so a future re-exemption starts from a clean state. Audit log retains the prior values from the original exempt-true request via AuditLogInterceptor. 4. Adds 4 unit tests in member-queries.spec.ts covering: persist on exempt=true, clear on exempt=false, clear overrides incoming stale reason on exempt=false, untouched when patch omits exempt. Pre-existing typecheck failures in unrelated specs and a pre-existing people.service.spec runtime error were verified to also occur on the baseline (origin/main) before this change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Prisma schema change in this branch added two non-optional (nullable) columns to Member — backgroundCheckExemptReason + backgroundCheckExemptJustification. The strict-typed test mock createMockMember returns Member, so the mock had to include both keys or the spread of overrides would surface them as undefined and fail the Vercel build: Type 'undefined' is not assignable to type 'string | null'. Initializing both to null in the default mock matches the DB default. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…persist fix(background-check): persist exemption reason + justification on member
Four real issues caught by cubic on the main→release deploy review. The two Jest-hoisting findings (exception/reconciliation specs) were skipped — that rule is babel-jest only, project uses ts-jest, all tests pass in CI. P1 — "Fix All" could target passing findings Service groups store the merged failed+passed set, and `canFixFinding` returns a key for any finding with a `findingKey` regardless of status. The batch dialog was happily including already-passing checks in the remediation target list. Now filter by `status === 'failed'` before consulting canFixFinding. P2 — exception-expiry accepted timezone-less timestamps ISO 8601 regex made the timezone offset optional, so `2026-08-13T23:59:59` passed validation but `new Date()` parsed it in server-local time — same input, different expiry on UTC vs Pacific hosts. Made the offset required; updated the spec to assert both acceptance (with offset) and rejection (without offset). P2 — dead `!prior.passed === false` in reconciliation The line evaluated identically to the very next `if (prior.passed) continue`. Removed; behavior unchanged, clarity restored. P2 — ISO control-number regex was case-sensitive `/\bA\.\d+\.\d+(\.\d+)?\b/` had no /i flag, so lowercase variants like "a.5.1.2" would slip past the forbidden-content guard. Added /i and a regression test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
fix(cloud-tests): cubic follow-ups from production deploy review
Contributor
|
🎉 This PR is included in version 3.56.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is an automated pull request to release the candidate branch into production, which will trigger a deployment.
It was created by the [Production PR] action.
Summary by cubic
Deploys the cloud-tests auditor visibility improvements: clearer evidence, per-check descriptions, grouping, comments on findings, and a new History tab; also fixes background-check exemptions to persist reason/justification and tightens “Fix All” to failed-only with stricter exception expiry parsing.
New Features
checkKey) with “X of Y failing”, display caps, and a renamed “Scan Results” tab; remediation text parsed into steps, reference link, and compliance chips; safer reference URLs.findingentity type and an entity-aware permission guard; thread shown on the finding detail sheet.Migration
scannedServices/failedServiceson IntegrationCheckRun andCommentEntityType.finding.backgroundCheckExemptReasonandbackgroundCheckExemptJustificationon Member; persisted when exempt is true and cleared when set to false.Written for commit e080cd2. Summary will update on new commits. Review in cubic