Skip to content

[comp] Production Deploy#3463

Open
github-actions[bot] wants to merge 6 commits into
releasefrom
main
Open

[comp] Production Deploy#3463
github-actions[bot] wants to merge 6 commits into
releasefrom
main

Conversation

@github-actions

@github-actions github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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

Ship Internal Audit (ISO 27001 clause 9.2, CS-724) for ISMS: adds a new document with an editable programme, audits register (IA-YYYY-NN), Controls Tested, findings, sign-off, export, and submit gates; includes a DB migration and UI/API changes. Also deploy security fixes: upgrade adm-zip to ^0.6.0, pin engine.io to ^6.6.7, bump body-parser to 2.3.0, and override tar to ^7.5.19; and change policy regenerate to create a new draft version instead of overwriting the published policy.

Written for commit ff31dbd. Summary will update on new commits.

Review in cubic

github-actions Bot and others added 2 commits July 20, 2026 23:09
…) (#3462)

adm-zip < 0.6.0 allows a crafted ZIP to trigger a 4GB memory allocation
(DoS). This is reachable: apps/api/src/questionnaire/utils/content-extractor.ts
runs `new AdmZip(fileBuffer)` on user-uploaded questionnaire files (xlsx/docx
are ZIP containers).

Bumps the direct dependency to ^0.6.0 (patched) and updates bun.lock. Our usage
(new AdmZip, getEntry, getData, addFile, toBuffer) is unchanged in 0.6.0.
Supersedes Dependabot PR #3451, which bumped package.json but left bun.lock
out of sync.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app (staging) Ready Ready Preview, Comment Jul 21, 2026 11:04pm
comp-framework-editor (staging) Ready Ready Preview, Comment Jul 21, 2026 11:04pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
portal (staging) Skipped Skipped Jul 21, 2026 11:04pm

Request Review

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No issues found across 2 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Re-trigger cubic

…rts (#3464)

- engine.io: add bun override to ^6.6.7. The vulnerable 6.5.5 (Engine.IO
  polling connection-exhaustion DoS, <6.6.7) was pulled transitively via
  @trigger.dev/core -> socket.io@4.7.4. Override collapses it to 6.6.9 (the
  version top-level socket.io@4.8.3 already uses); bun.lock now has a single
  engine.io@6.6.9. Not a reachable DoS for us (we run no engine.io server;
  trigger.dev uses it client-side) but removes the alert.

- body-parser: bump the transitive dep in apps/mcp-server (runtime, via
  express@5.2.1) 2.2.2 -> 2.3.0 (patched; DoS via invalid limit silently
  disabling size enforcement). Lockfile-only; express's ^2.2.1 range permits
  2.3.0, so it stays transitive and survives SDK regeneration.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
#94-#104) (#3466)

A batch of 11 node-tar advisories (Critical->Moderate: decompression DoS, path
traversal, symlink/hardlink escape, PAX parsing differentials) flagged every
tar copy below 7.5.19. bun.lock had tar@6.2.1 (x5, via build tooling: node-gyp,
cacache, @electron/rebuild, app-builder-lib, giget), tar@7.5.11 (fleetctl), and
tar@7.5.20 (already patched).

node-tar backported no fixes to the 6.x line, so the only remediation is
tar >= 7.5.19. Added a bun override "tar": "^7.5.19" — bun.lock now resolves a
single tar@7.5.20 for all consumers. tar@7 is a dual CJS/ESM package with a
stable extract/create API, so the CJS build tools keep working via require().

Reachability was low pre-fix (all vulnerable copies were build-time tooling or
trusted-source CLI extraction, none extract attacker-controlled archives in the
runtime API), but the override removes the vulnerable versions entirely.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…S-724) (#3468)

* feat(isms): internal audit programme, plan and report — clause 9.2 (CS-724)

Adds the ninth ISMS document type: Compliance > ISMS > Internal Audit.

- Programme paragraph (editable, seeded with a default) rendered verbatim
  into the generated document
- Audit instances with server-generated IA-YYYY-NN references, templated
  scope/criteria, auditor pulled from ISMS > Roles > Internal Auditor,
  planned dates, status, and a conclusion verdict + narrative
- Controls Tested table seeded with the default 15-row sample set
  (management-system clauses + high-impact Annex A controls); result +
  notes recorded per row
- Findings with server-generated F-NN references, type/owner/due
  date/status/closure evidence, optional link back to a control row
- Three sign-off slots per audit, frozen into the published version
  snapshot and rendered as the document's sign-off table
- Submit gate: at least one audit, and a conclusion verdict on every
  completed audit (enforced server-side, mirrored in the client)

* fix(isms): align internal-audit components with design-system prop types

The Vercel next build enforces the design-system unions that the local
tsc pass missed: Stack gap has no '5', TableCell omits className, and
Select.onValueChange passes string | null.

* fix(isms): address internal-audit review findings

- scope audit/control/finding lookups to the internal-audit document type
- seed the programme paragraph only when the draft narrative is empty, so
  a concurrent setup call can never overwrite an edit
- trim required text fields (scope, criteria, control reference) so
  whitespace-only values are rejected
- marking a control row non-conformity/observation now opens the
  findings form pre-filled with the linked row (per the ticket flow),
  and the edit form pre-fills clause text like the add form
- preserve unsaved sign-off input across sibling register refreshes
- gate the control-row Save button on validity
- document the audit register fields in the OpenAPI body schema
- cover the internal-audit derivation path in generate.spec

* fix(isms): make the programme seed atomic and re-key the finding prompt

- seed the programme via a conditional update that matches only a NULL
  draft narrative, removing the read-then-write race entirely (and with
  it the duplicated empty-narrative predicate)
- key the prefilled linked-finding form by row and type, so re-marking
  the same control with a different result re-initializes the form

* fix(isms): treat an empty-object narrative as seedable, matching generate

The programme seed's conditional update now matches NULL or {} — the
same empty definition generateNarrative uses — so the two seed paths
can never drift while still never overwriting a populated draft.

* feat(isms): render closure evidence in the exported findings table

Closed corrective actions now show how they were evidenced in the
generated document — appended to the description cell, keeping the
reference document's seven-column findings table.

---------

Co-authored-by: Tofik Hasanov <annexcies@gmail.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

8 issues found across 60 files (changes from recent commits).

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/app/src/app/(app)/[orgId]/documents/isms/components/AuditControlsTable.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/documents/isms/components/AuditControlsTable.tsx:44">
P3: New control rows always save with blank notes because this form has no Notes field, despite the table exposing that column. Add the optional notes input so auditors can record it when creating the row rather than requiring a second edit.</violation>
</file>

<file name="apps/api/src/isms/isms.service.ts">

<violation number="1" location="apps/api/src/isms/isms.service.ts:575">
P3: ISMS service now exceeds the repository's 300-line file limit by more than 2x, making the lifecycle and validation responsibilities harder to maintain. Move Internal Audit validation/provisioning behavior into a focused helper/service module and retain a small delegation here.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/documents/isms/components/audit-schema.ts">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/documents/isms/components/audit-schema.ts:20">
P2: An audit can be saved with a planned end date before its planned start date, producing an invalid audit schedule. Add a cross-field date-order validation here and enforce the same invariant in the API for non-UI callers.</violation>
</file>

<file name="apps/api/src/isms/isms-audit-finding.service.ts">

<violation number="1" location="apps/api/src/isms/isms-audit-finding.service.ts:150">
P2: Deleting the highest-numbered finding lets its reference be reissued on the next create, breaking the immutable audit trail promised for `F-NN`. Persist a per-audit monotonically increasing sequence (or retain deleted reference history) rather than deriving it from live rows.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/documents/isms/components/AuditControlRow.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/documents/isms/components/AuditControlRow.tsx:143">
P3: Clearing the required control reference leaves Save disabled without any validation feedback, so users cannot tell what needs fixing. Render the `controlRef` field error (and ideally invalid state) in this edit row, matching the create form.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/documents/isms/components/ProgrammeCard.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/documents/isms/components/ProgrammeCard.tsx:132">
P3: Multi-line programme edits lose paragraph/line-break formatting in the read view because normal text rendering collapses newlines. Preserve whitespace when displaying the saved textarea value so it matches the entered/generated document content.</violation>
</file>

<file name="apps/app/src/app/(app)/[orgId]/documents/isms/components/AuditSignoffCard.tsx">

<violation number="1" location="apps/app/src/app/(app)/[orgId]/documents/isms/components/AuditSignoffCard.tsx:142">
P3: The signed badge can report a completed slot for a whitespace-only signatory name, although the API normalizes that name to `null`. Trim the name for this completeness check so badge and generated sign-off state agree.</violation>
</file>

<file name="apps/api/src/isms/isms-audit-control.service.ts">

<violation number="1" location="apps/api/src/isms/isms-audit-control.service.ts:103">
P2: Deleting a control can leave a linked finding with no control reference when its API request omitted optional `clauseOrControl`; the FK clears `controlId` and no frozen label is populated. Preserve `control.controlRef` for linked findings with a null label before deleting, or require/derive that label when creating linked findings.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

criteria: z.string().trim().min(1, 'Criteria is required'),
auditorName: z.string(),
plannedStartDate: z.string(),
plannedEndDate: z.string(),

@cubic-dev-ai cubic-dev-ai Bot Jul 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: An audit can be saved with a planned end date before its planned start date, producing an invalid audit schedule. Add a cross-field date-order validation here and enforce the same invariant in the API for non-UI callers.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/app/src/app/(app)/[orgId]/documents/isms/components/audit-schema.ts, line 20:

<comment>An audit can be saved with a planned end date before its planned start date, producing an invalid audit schedule. Add a cross-field date-order validation here and enforce the same invariant in the API for non-UI callers.</comment>

<file context>
@@ -0,0 +1,105 @@
+  criteria: z.string().trim().min(1, 'Criteria is required'),
+  auditorName: z.string(),
+  plannedStartDate: z.string(),
+  plannedEndDate: z.string(),
+  status: z.enum(AUDIT_STATUSES),
+  conclusionVerdict: z.union([z.enum(CONCLUSION_VERDICTS), z.literal('')]),
</file context>
Fix with cubic

const match = /^F-(\d+)$/.exec(finding.reference);
return match ? Math.max(max, Number.parseInt(match[1], 10)) : max;
}, 0);
return `F-${String(maxSequence + 1).padStart(2, '0')}`;

@cubic-dev-ai cubic-dev-ai Bot Jul 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Deleting the highest-numbered finding lets its reference be reissued on the next create, breaking the immutable audit trail promised for F-NN. Persist a per-audit monotonically increasing sequence (or retain deleted reference history) rather than deriving it from live rows.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/isms/isms-audit-finding.service.ts, line 150:

<comment>Deleting the highest-numbered finding lets its reference be reissued on the next create, breaking the immutable audit trail promised for `F-NN`. Persist a per-audit monotonically increasing sequence (or retain deleted reference history) rather than deriving it from live rows.</comment>

<file context>
@@ -0,0 +1,248 @@
+      const match = /^F-(\d+)$/.exec(finding.reference);
+      return match ? Math.max(max, Number.parseInt(match[1], 10)) : max;
+    }, 0);
+    return `F-${String(maxSequence + 1).padStart(2, '0')}`;
+  }
+
</file context>
Fix with cubic

await invalidateApprovalIfNeeded({ tx, documentId: control.documentId });
// Findings linked to this row survive: the FK is SET NULL and each
// finding keeps its own clauseOrControl text as the frozen label.
await tx.ismsAuditControl.delete({ where: { id: controlId } });

@cubic-dev-ai cubic-dev-ai Bot Jul 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Deleting a control can leave a linked finding with no control reference when its API request omitted optional clauseOrControl; the FK clears controlId and no frozen label is populated. Preserve control.controlRef for linked findings with a null label before deleting, or require/derive that label when creating linked findings.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/isms/isms-audit-control.service.ts, line 103:

<comment>Deleting a control can leave a linked finding with no control reference when its API request omitted optional `clauseOrControl`; the FK clears `controlId` and no frozen label is populated. Preserve `control.controlRef` for linked findings with a null label before deleting, or require/derive that label when creating linked findings.</comment>

<file context>
@@ -0,0 +1,165 @@
+      await invalidateApprovalIfNeeded({ tx, documentId: control.documentId });
+      // Findings linked to this row survive: the FK is SET NULL and each
+      // finding keeps its own clauseOrControl text as the frozen label.
+      await tx.ismsAuditControl.delete({ where: { id: controlId } });
+    });
+    return { success: true };
</file context>
Fix with cubic

controlRef: '',
whatWasTested: '',
whereToFind: '',
notes: '',

@cubic-dev-ai cubic-dev-ai Bot Jul 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: New control rows always save with blank notes because this form has no Notes field, despite the table exposing that column. Add the optional notes input so auditors can record it when creating the row rather than requiring a second edit.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/app/src/app/(app)/[orgId]/documents/isms/components/AuditControlsTable.tsx, line 44:

<comment>New control rows always save with blank notes because this form has no Notes field, despite the table exposing that column. Add the optional notes input so auditors can record it when creating the row rather than requiring a second edit.</comment>

<file context>
@@ -0,0 +1,202 @@
+  controlRef: '',
+  whatWasTested: '',
+  whereToFind: '',
+  notes: '',
+};
+
</file context>
Fix with cubic

}
}

private async assertInternalAuditComplete({

@cubic-dev-ai cubic-dev-ai Bot Jul 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: ISMS service now exceeds the repository's 300-line file limit by more than 2x, making the lifecycle and validation responsibilities harder to maintain. Move Internal Audit validation/provisioning behavior into a focused helper/service module and retain a small delegation here.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/isms/isms.service.ts, line 575:

<comment>ISMS service now exceeds the repository's 300-line file limit by more than 2x, making the lifecycle and validation responsibilities harder to maintain. Move Internal Audit validation/provisioning behavior into a focused helper/service module and retain a small delegation here.</comment>

<file context>
@@ -525,6 +572,25 @@ export class IsmsService {
     }
   }
 
+  private async assertInternalAuditComplete({
+    tx,
+    documentId,
</file context>
Fix with cubic

control={control}
name="controlRef"
render={({ field: { ref: _ref, ...field } }) => (
<Input {...field} aria-label="Control reference" />

@cubic-dev-ai cubic-dev-ai Bot Jul 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: Clearing the required control reference leaves Save disabled without any validation feedback, so users cannot tell what needs fixing. Render the controlRef field error (and ideally invalid state) in this edit row, matching the create form.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/app/src/app/(app)/[orgId]/documents/isms/components/AuditControlRow.tsx, line 143:

<comment>Clearing the required control reference leaves Save disabled without any validation feedback, so users cannot tell what needs fixing. Render the `controlRef` field error (and ideally invalid state) in this edit row, matching the create form.</comment>

<file context>
@@ -0,0 +1,282 @@
+            control={control}
+            name="controlRef"
+            render={({ field: { ref: _ref, ...field } }) => (
+              <Input {...field} aria-label="Control reference" />
+            )}
+          />
</file context>
Fix with cubic

/>
</Field>
) : (
<Text size="sm">{programme || 'No programme recorded yet.'}</Text>

@cubic-dev-ai cubic-dev-ai Bot Jul 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: Multi-line programme edits lose paragraph/line-break formatting in the read view because normal text rendering collapses newlines. Preserve whitespace when displaying the saved textarea value so it matches the entered/generated document content.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/app/src/app/(app)/[orgId]/documents/isms/components/ProgrammeCard.tsx, line 132:

<comment>Multi-line programme edits lose paragraph/line-break formatting in the read view because normal text rendering collapses newlines. Preserve whitespace when displaying the saved textarea value so it matches the entered/generated document content.</comment>

<file context>
@@ -0,0 +1,136 @@
+          />
+        </Field>
+      ) : (
+        <Text size="sm">{programme || 'No programme recorded yet.'}</Text>
+      )}
+    </IsmsRegisterCard>
</file context>
Fix with cubic


const values = toFormValues(audit);
const signedCount = SLOTS.filter(
(slot) => values[slot.nameField] && values[slot.dateField],

@cubic-dev-ai cubic-dev-ai Bot Jul 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: The signed badge can report a completed slot for a whitespace-only signatory name, although the API normalizes that name to null. Trim the name for this completeness check so badge and generated sign-off state agree.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/app/src/app/(app)/[orgId]/documents/isms/components/AuditSignoffCard.tsx, line 142:

<comment>The signed badge can report a completed slot for a whitespace-only signatory name, although the API normalizes that name to `null`. Trim the name for this completeness check so badge and generated sign-off state agree.</comment>

<file context>
@@ -0,0 +1,183 @@
+
+  const values = toFormValues(audit);
+  const signedCount = SLOTS.filter(
+    (slot) => values[slot.nameField] && values[slot.dateField],
+  ).length;
+
</file context>
Fix with cubic

…verwriting published (#3471)

* fix(policies): create draft version on policy regenerate instead of overwriting published

## Problem

When regenerating a policy, the published version's content is overwritten immediately. This bypasses the approval workflow that should gate any content changes. Regenerating a published policy currently mutates its live content, signedBy metadata, and currentVersionId directly without creating a draft for review.

## Root cause

updatePolicyInDatabase in update-policy-helpers.ts rewrites policy.content, currentVersionId, and signedBy on the live published policy record without changing status or requiring approval. Every other content-change path (createVersion, submitForApproval, acceptChanges) creates a draft version first and only publishes on explicit approval; regenerate was the sole exception.

## Fix

For published or needs_review policies, regenerate now creates a new draft version (incrementing the version number) with the regenerated content, leaving the published version's content, currentVersionId, signedBy, and status unchanged. This mirrors the existing approval workflow: draft versions are created, then approved, then published with the signing flow triggered for all employees.

## Explicitly NOT touched

- Policies not yet published (status: draft)
- The signing flow (runs only on explicit publish, unchanged)
- Approval/rejection workflows
- Version history or deletion logic

## Verification

Added regression test asserting that regenerating a published policy creates a new draft version without modifying the published content or status. Existing policy regenerate and versioning unit tests pass locally ✅

* fix(bug-todo): address cubic review

Address review findings.

* fix(bug-todo): address cubic review

Address review findings.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 3 files (changes from recent commits).

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/trigger/policies/update-policy-helpers.ts">

<violation number="1" location="apps/api/src/trigger/policies/update-policy-helpers.ts:113">
P2: Regenerating an uploaded draft now detaches its PDF without deleting the S3 object, leaving an orphaned document on every regeneration. Delete the old policy/current-version keys after the database update commits (deduplicating keys and retaining failures for cleanup).</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

data: {
content: versionContent,
changelog: 'Regenerated policy content',
pdfUrl: null,

@cubic-dev-ai cubic-dev-ai Bot Jul 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Regenerating an uploaded draft now detaches its PDF without deleting the S3 object, leaving an orphaned document on every regeneration. Delete the old policy/current-version keys after the database update commits (deduplicating keys and retaining failures for cleanup).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/trigger/policies/update-policy-helpers.ts, line 113:

<comment>Regenerating an uploaded draft now detaches its PDF without deleting the S3 object, leaving an orphaned document on every regeneration. Delete the old policy/current-version keys after the database update commits (deduplicating keys and retaining failures for cleanup).</comment>

<file context>
@@ -75,79 +79,99 @@ export async function updatePolicyInDatabase(
+            data: {
+              content: versionContent,
+              changelog: 'Regenerated policy content',
+              pdfUrl: null,
+            },
           });
</file context>
Fix with cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants