SP-1173: add CUI marking for files the CLI writes to disk - #405
Conversation
ddaf42d to
f730ba9
Compare
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
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>
66a75f8 to
ac4b255
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ 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 ac4b255. Configure here.
|




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 land on disk and their names for JSON listings, with an extra API call per write; scope is limited to two commands and fails closed when cover fetch fails.
Overview
Adds CUI (Controlled Unclassified Information) marking for disk writes from
list spaces --jsonandlist packages --json, with a reusable path for later commands.Before writing JSON, the CLI calls the team CUI PDF cover API. Outcomes: no marking (unchanged filename), unclassified (
Unclassified - …), or classified (CUI - ….zipcontaining the JSON plusCUI_Cover_Sheet.pdf). If marking cannot be resolved, the command fails and nothing is written. Console-only listings skip the CUI probe.New pieces:
CuiApi/CuiFileService,HttpClient.getStatusAndDatafor status-aware GETs, andBaseManager.findAllawaiting asynconFindAllhandlers. Test mocks default the cover endpoint to 204 so existing tests stay unmarked unless they opt in.Reviewed by Cursor Bugbot for commit ac4b255. Bugbot is set up for automated code reviews on this repo. Configure here.