SP-1173: add CUI marking for files the CLI writes to disk - #405
SP-1173: add CUI marking for files the CLI writes to disk#405Dennis Woditsch (dwoditsch) wants to merge 7 commits into
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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.
ddaf42d to
f730ba9
Compare
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>
f730ba9 to
66a75f8
Compare
|
Kastriot Salihu (ksalihu)
left a comment
There was a problem hiding this comment.
Approach looks sound and the staged rollout across #407-#413 makes sense to me. None of my comments block merge - fine to pick them up in the follow-up PRs.
The one I would put for consideration is only the 200-with-an-unrecognised-body case in cui-api.ts. It is the only path where the feature silently produces an unverified Unclassified marking instead of failing closed, and it gets more surface area with every command that starts routing through CuiFileService. Also fine to address in followup PRs if this adds conflicts or smth




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 --jsonandlist packages --json. The remaining writers follow in later PRs.What a user gets depends on the team's CUI settings:
Unclassified - <name>CUI - <name>.zip, holding the payload plusCUI_Cover_Sheet.pdfThis 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
--jsonlistings and reportslist spaces,list packages--jsonlistings and reportslist assets/assignments/data-pools,config *,t2tc package list/diff,deployment *,asset-registry *-o, --outputToJsonFilereportsanalyze/import action-flows,export data-pool,import data-pools,t2tc package importreportconfig package export --zip,config branch export --zip,t2tc package export,export action-flows,pull packagepull asset/skill/data-pool/view-bookmarks/bookmarks,export bookmarksconfig package export,config branch export,t2tc package export --unzip--gitBranchvariantsconfig package export,config branch export,t2tc package exportTwo 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
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-coverand 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 aCUI - <name>.zipcontaining the JSON plusCUI_Cover_Sheet.pdfwhen classified.list spaces --jsonandlist packages --jsonnow route exports throughCuiFileServiceinstead of writing directly. Console-only listings skip the CUI probe.Supporting changes:
HttpClient.getStatusAndDatareturns status and body without treating 4xx as fatal (needed for 403/204 semantics),BaseManager.findAllawaits asynconFindAll, 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.