fix(security): redact setting secrets + make the PI board live server-side - #2
Merged
Merged
Conversation
…live board Follow-ups to PR #1 (Teams notifications + real-time PI Program Board). Security fix — setting-secret leak - GET /settings returned the whole Setting table to any authenticated user, which exposed the Teams channel webhook URL (a secret) despite ADR-0060 stating it is never returned. Redact secret-valued keys (suffixes webhookurl/secret/token/password) from the response; the connector's status endpoint still surfaces only a masked host. Reality now matches the ADR. Real-time — server-driven change broadcast - The board previously only pushed live updates for changes made on the board itself. Wire IHubContext<BoardHub> into the PI mutation endpoints (objectives, dependencies, iterations create/update/delete) and the board-placement endpoint, broadcasting a contentless "BoardChanged" ping to the increment group after each commit. Now a change made on another tab or by another API client reaches every open board live. Best-effort: a transport hiccup can't fail the originating write. - Client no longer relays changes over the hub (server is the single broadcaster); removed the now-dead NotifyChanged hub RPC and hook method. The hook still sources presence + cursors. Frontend build, lint and the 72-test suite pass; the exact CI Semgrep config reports 0 findings over server/. .NET compiled by CI (no SDK locally). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F1g44VLAeYdf3FAoXsXRu7
nicpozent
pushed a commit
that referenced
this pull request
Jul 12, 2026
Addresses two of the remaining follow-ups from #1/#2. Accessibility (ADR-0025 baseline) - The board's only way to move a card between iterations was drag-and-drop (mouse-only). Add a native <select> on each card — fully keyboard-operable and screen-reader labelled — to move it to any iteration (or Unscheduled). Mouse users can still drag; both paths call the same placement mutation. Integration tests (new server/Atlas.Tests/PiBoardAndSettingsTests.cs) - GET /settings redacts secret-valued keys (teams.webhookUrl) while still returning non-secret toggles — regression guard for the #2 leak fix. - PI board placement roundtrip: place → appears in the map; clear (null) → drops out; cross-increment objective → 400 (not 500). - Teams status returns only the masked host, never the raw webhook (asserts the secret path/token never appear in the response). Frontend build, lint and the 72-test suite pass. The .NET tests are compiled and run by the API (build · test) CI job (no SDK in the authoring env). Note: item #2 from the gap list (broadcast from Jira/ADO sync) is N/A — those services write project tasks/sprints, not PI objectives/dependencies/iterations, so they don't change board state. The IterationId migration still needs a dotnet-ef environment. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F1g44VLAeYdf3FAoXsXRu7
nicpozent
added a commit
that referenced
this pull request
Jul 13, 2026
…e, screen splits (#25) * feat(soa): automated platform-evidence linkage for Annex A controls The SoA previously started every control blank. Now the ~20 ISO 27001 Annex A controls that the Atlas platform satisfies by construction carry a standing evidence note mapped to the concrete mechanism — RBAC → A.5.15/A.5.18/A.8.3, append-only audit log → A.8.15, backups → A.8.13, OpenTelemetry → A.8.16, CI SAST/SCA/DAST → A.8.8/A.8.25/A.8.28/A.8.29, secret redaction → A.8.11, TLS/CSP → A.8.20/A.8.24, Entra SSO → A.5.16/A.5.17/A.8.5 — so a review starts from what the product provides. - server/Soa.cs: PlatformEvidence map; GET attaches per-control autoEvidence + an autoEvidenced coverage count; PUT echoes it. - web: a "Platform-evidenced" KPI + a per-row evidence chip. - tests: assert the evidence surfaces (>=15), a known control (A.8.15 → audit log) carries it, and a control with no platform mechanism (A.7.1) does not. - ADR-0066 updated (this was previously listed as a gap there). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F1g44VLAeYdf3FAoXsXRu7 * feat(whiteboard): field-level merge for concurrent same-node edits Evaluated a full CRDT/OT upgrade and declined it as disproportionate for a bounded brainstorming canvas (heavy dep, would replace the typed rows with an opaque CRDT doc + binary protocol, for a sub-RTT same-field race that already self-heals). Implemented the proportionate step instead: field-level merge. - Co-editing node ops now send only the changed properties (WbNodePatch): a move sends geometry, a recolour sends the colour, a text edit sends the text. The server merges per field onto the existing row, so two people editing different aspects of one node (A moves, B recolours) both survive — no cross-field last-write-wins clobber. Create still sends a full node; only same-field edits remain LWW. - server/Whiteboards.cs: WbNodePatch + merge-on-existing node upsert (backward compatible — a full-node body still creates/replaces). - web: patchNode() for move/resize/recolour/text/keyboard-nudge. - tests: color-only then move-only patches both persist and don't revert each other or the untouched size (444 API, 111 web green). - ADR-0064 updated (convergence addendum + consequence). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F1g44VLAeYdf3FAoXsXRu7 * refactor(screens): split Governance out of Project.tsx, data-governance out of Admin.tsx Continues the ADR-0041 large-screen decomposition to the two biggest remaining screens. - Project.tsx (1617 → 1361): the Governance tab (stage-gate rail, review checkpoints, decision log + its modals) moves to project/Governance.tsx; Project.tsx imports it. - Admin.tsx (1177 → 897): the data-governance sections (database- password rotation, GDPR data-subject rights + retention, Backups & restore) move to admin/DataGovernance.tsx; the shared section styles (GRADIENT, sectionTitle/Sub, colHeadStyle) move to admin/styles.ts so both files share one source. Pure move — no behaviour change. Build + lint clean; 111 web tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F1g44VLAeYdf3FAoXsXRu7 --------- Co-authored-by: Claude <noreply@anthropic.com>
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.
Follow-ups to #1, addressing gaps found in review.
🔴 Security fix — setting-secret leak
GET /settingsreturned the entireSettingtable to any authenticated user with no permission gate. Since the Teams channel webhook URL is stored there, that secret was readable by any signed-in user — despite ADR-0060 stating it is "never returned to the client."webhookurl/secret/token/password) from theGET /settingsresponse. New secret settings are redacted by default./settingsand gets the webhook's masked host from/integrations/teams/status, so nothing in the UI breaks. Reality now matches the ADR.⚡ Real-time — server-driven change broadcast
The board previously only pushed live updates for changes made on the board itself. Now the change ping is server-driven:
IHubContext<BoardHub>is wired into the PI mutation endpoints — objectives, dependencies, iterations (create/update/delete) and board placement — broadcasting a contentlessBoardChangedping to thepi:{id}group after each commit.NotifyChangedhub RPC and hook method.Verification
build+eslint+ 72 tests pass locally.server/.API (build · test)CI job (no SDK in the authoring env).Still open (documented, not in this PR)
PiObjective.IterationIdcolumn — needs a generated EF migration.🤖 Generated with Claude Code
https://claude.ai/code/session_01F1g44VLAeYdf3FAoXsXRu7
Generated by Claude Code