Skip to content

SP-1173: add CUI marking for files the CLI writes to disk - #405

Open
Dennis Woditsch (dwoditsch) wants to merge 7 commits into
mainfrom
feat/SP-1173-cui-marking-data
Open

SP-1173: add CUI marking for files the CLI writes to disk#405
Dennis Woditsch (dwoditsch) wants to merge 7 commits into
mainfrom
feat/SP-1173-cui-marking-data

Conversation

@dwoditsch

@dwoditschDennis Woditsch (dwoditsch) commented Aug 7, 2026

Copy link
Copy Markdown

Description

Every user-facing file content-cli writes to disk has to carry the team's CUI (Controlled Unclassified Information) marking. This PR adds the mechanism and wires up list spaces --json and list packages --json. The remaining writers follow in later PRs.

What a user gets depends on the team's CUI settings:

Team settingArtifact
marking does not applyoriginal filename, unchanged
marking applies, no categoriesUnclassified - <name>
marking applies, with categoriesCUI - <name>.zip, holding the payload plus CUI_Cover_Sheet.pdf
marking cannot be resolvednothing written, the command fails

This ships inert in production: the cover endpoint is allowlisted for staging only, so production keeps taking the unmarked path until the matching rule lands. Commands that only print to the console never probe the endpoint.

Scope: how the write is triggered

TriggerCommandsCUI marking
--json listings and reportslist spaces, list packagesThis PR, together with the marking mechanism
--json listings and reportslist assets/assignments/data-pools, config *, t2tc package list/diff, deployment *, asset-registry *To follow in #407
-o, --outputToJsonFile reportsanalyze/import action-flows, export data-pool, import data-pools, t2tc package import reportTo follow in #410
artifact is already an archiveconfig package export --zip, config branch export --zip, t2tc package export, export action-flows, pull packageTo follow in #411
single non-archive exportpull asset/skill/data-pool/view-bookmarks/bookmarks, export bookmarksTo follow in #412
output is a directoryconfig package export, config branch export, t2tc package export --unzipTo follow: cover sheet into the directory, prefix the directory name
--gitBranch variantsconfig package export, config branch export, t2tc package exportOut of scope, nothing reaches local disk
no output flagconsole-only listings, profile/git-profile/log filesOut of scope

Two things were left to settle here and both are answered further up the stack: an artifact that is already a zip gets the cover sheet merged in rather than nested (#411), and directory output keeps the cover sheet inside the directory with the directory name prefixed.

Relevant links

Checklist

  • I have self-reviewed this PR
  • I have tested the change and proved that it works in different scenarios
  • I have updated docs if needed

Note

Medium Risk
Changes what files users receive on export when CUI is enabled and fails closed on cover API errors; production stays on the unmarked path until the endpoint is allowlisted (403).

Overview
Adds CUI (Controlled Unclassified Information) marking for disk writes from Studio list commands. Before writing, the CLI calls GET /api/team/cui-settings/cui-pdf-cover and adjusts the output based on the response: no change when marking does not apply (403 feature flag or 204 team disabled), Unclassified - <name> when marking applies but there are no categories, or a CUI - <name>.zip containing the JSON plus CUI_Cover_Sheet.pdf when classified.

list spaces --json and list packages --json now route exports through CuiFileService instead of writing directly. Console-only listings skip the CUI probe.

Supporting changes: HttpClient.getStatusAndData returns status and body without treating 4xx as fatal (needed for 403/204 semantics), BaseManager.findAll awaits async onFindAll, and HTTP test mocks default the cover endpoint to 204 so existing tests stay unmarked unless they opt in.

Reviewed by Cursor Bugbot for commit 66a75f8. Bugbot is set up for automated code reviews on this repo. Configure here.

@dwoditschDennis Woditsch (dwoditsch) changed the title SP-1173: mark CUI content when writing Studio listings to diskSP-1173: add CUI marking for files the CLI writes to diskAug 7, 2026
@dwoditsch
Dennis Woditsch (dwoditsch) marked this pull request as ready for review August 10, 2026 07:49

@cursorcursorBot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 45d2e1f. Configure here.

Comment threadsrc/core/utils/cui-service.ts Outdated
Comment threadsrc/core/utils/cui-file-service.ts
Route the JSON output of `list spaces` and `list packages` through a new
CuiFileService, which asks the team's CUI settings how the content is
classified and names the output accordingly: a `CUI - <name>.zip` holding
the listing plus the decoded cover sheet when categories apply, an
`Unclassified - <name>` rename when none do, and the original filename
when no marking applies at all.
CuiService owns the API contract and derives enablement from the response
status, so the CLI translates the backend's answer rather than deciding
entitlement itself. HttpClient.getStatusAndData exposes the status code
because get() throws on 4xx and would otherwise hide the 204/403 signal.
BaseManager.findAll now awaits onFindAll so an async listing callback
completes before the command resolves.
Includes-AI-Code: true
Co-authored-by: Cursor <cursoragent@cursor.com>
Once findAll moved to async/await, return Promise.reject() was equivalent
to throwing undefined: Sonar flags it, and the command handler logged
"undefined" instead of the real cause. Rethrow the original error.
Includes-AI-Code: true
Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the 403 branch from CuiService. Any status other than 200 or 204 now
raises a FatalError, so no file is written when the marking cannot be
resolved.
Also flatten the nested template literal in the error path, which Sonar flags.
Includes-AI-Code: true
Co-authored-by: Cursor <cursoragent@cursor.com>
403 means the feature flag is disabled, 204 means the team has CUI disabled.
Both keep the original filename, as separate branches so the debug log says
which one applied. Any other non-200 status still fails closed.
Includes-AI-Code: true
Co-authored-by: Cursor <cursoragent@cursor.com>
CuiPdfCoverResponse is the only type consumed outside the module, so the three
helper interfaces are inlined into it. Categories are counted, never read, so
their element type carries no weight.
Includes-AI-Code: true
Co-authored-by: Cursor <cursoragent@cursor.com>
@sonarqubecloud

Copy link
Copy Markdown

Sign up for freeto 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.

2 participants

@dwoditsch@nikolai-celonis