Skip to content

feat: Allow toolmakers to explain why they need CSP exceptions with selective optional domain consent#431

Merged
Power-Maverick merged 9 commits intodevfrom
copilot/add-csp-exception-explanation
Mar 5, 2026
Merged

feat: Allow toolmakers to explain why they need CSP exceptions with selective optional domain consent#431
Power-Maverick merged 9 commits intodevfrom
copilot/add-csp-exception-explanation

Conversation

Copy link
Contributor

Copilot AI commented Mar 3, 2026

Tool developers can now provide a markdown-formatted explanation (exceptionReason) and an optional flag for each CSP exception domain. The consent modal shows required and optional exceptions in separate sections — both using checkboxes for visual consistency — with required ones pre-checked and disabled, and optional ones pre-checked but user-toggleable. Consent records are stored in a single unified object per tool to support future re-consent detection when domain lists change.

Type changes (src/common/types/common.ts)

  • New CspExceptionEntry interface: { domain, exceptionReason?, optional? }
  • CspExceptionSource = string | CspExceptionEntry — fully backward-compatible with existing string arrays
  • normalizeCspExceptionSource() helper exported for converting either format to CspExceptionEntry
  • CspExceptions directives now typed as CspExceptionSource[]

Type changes (src/common/types/settings.ts)

  • New CspConsentRecord interface: { allowed: boolean; required: string[]; optional: string[] }
  • UserSettings.cspConsents type updated to { [toolId: string]: CspConsentRecord } — consolidates the former separate cspRequiredConsents and cspOptionalConsents keys into one record per tool

Runtime changes

  • browserviewProtocolManager: CSP header builder only applies optional domains that were explicitly approved by the user; required domains are always applied when consent is granted
  • modals/cspException/view: Both Required and Optional sections use checkboxes for consistency — required items are pre-checked and disabled (with cursor: not-allowed styling); optional items are pre-checked and user-toggleable with an "Uncheck any you do not want to allow" hint; both show exceptionReason rendered as inline markdown (**bold**, *italic*, `code`)
  • modals/cspException/controller: Accept handler collects only non-disabled (optional) checkbox values using .csp-optional-checkbox:not([disabled]) selector
  • cspExceptionModal: Return type is Promise<string[] | null>null means declined, string[] contains the approved optional domains
  • settingsManager: grantCspConsent(toolId, requiredDomains, approvedOptionalDomains) writes a single CspConsentRecord; revokeCspConsent deletes the record; getApprovedRequiredDomains() and getApprovedOptionalDomains() read from the unified record
  • toolManagement: Computes required domains from tool.cspExceptions (all non-optional entries) before granting consent and passes them alongside approved optional domains

New manifest format

"cspExceptions": {
  "connect-src": [
    { "domain": "api.example.com", "exceptionReason": "Reads **environment** configuration via `OData`" }
  ],
  "style-src": [
    { "domain": "fonts.googleapis.com", "exceptionReason": "Loads *Google Fonts* for improved typography", "optional": true }
  ],
  "script-src": ["cdn.example.com"]
}

The old string-only format continues to work unchanged.

Stored consent format

"cspConsents": {
  "12345678-27a1-4acb-b8f4-6b62ef0021e0": {
    "allowed": true,
    "required": ["https://raw.githubusercontent.com"],
    "optional": []
  },
  "12345678-c6cb-4c3c-80b2-ea46490c2b79": {
    "allowed": true,
    "required": ["fonts.googleapis.com", "api.example.com"],
    "optional": ["cdn.example.com"]
  }
}
  • Use a meaningful title for the pull request.
  • Follow the guidelines from the CONTRIBUTING.md.
  • Mention the bug or the feature number the PR will be targeting.
  • Test the change in your own code. (Compile and run)
  • Resolve all GH Copilot comments.
Original prompt

This section details on the original issue you should resolve

<issue_title>[Feature]: Allow toolmakers to explain why they need csp exception</issue_title>
<issue_description>### Provide an idea you would like to see in the tool?

We should allow, for each exception, the toolmaker to have some text to explain what they are doing with the exception. And allow markdown

e.g. Environment manager - Each exception is used to read configuration details for the settings and further context on the meaning behind each setting.

Relevant log output

```</issue_description>

## Comments on the Issue (you are @copilot in this section)

<comments>
<comment_new><author>@Power-Maverick</author><body>
The current full format for cspException is

"cspExceptions": {
"connect-src": ["api.example.com"],
"script-src": ["cdn.example.com"],
"style-src": ["fonts.googleapis.com"],
"img-src": ["img.example.com"],
"font-src": ["fonts.gstatic.com"],
"frame-src": ["embed.example.com"],
"media-src": ["media.example.com"]
}


What you are proposing is that the tool dev needs to provide markdownDescription for each of the domain mentioned in the cspException (adding optional feature here as well)

Example:

"cspExceptions": {
"connect-src": [
{
"domain": "api.example.com",
"markdownDetail": "Do some amazing stuff!!"
}
],
"script-src": [
{
"domain": "cdn.example.com",
"markdownDetail": "Invoke some great APIs"
}
],
"style-src": [
{
"domain": "fonts.googleapis.com",
"markdownDetail": "Get fonts from Google",
"optional": true
}
]
}

<comment_new><author>@LinkeD365</author><body>
markdownDetail doesn't explain what it is for. Can we change to exceptionReason ?</body></comment_new>
</comments>


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Co-authored-by: Power-Maverick <36135520+Power-Maverick@users.noreply.github.com>
Copilot AI changed the title [WIP] Add explanation field for csp exceptions feat: Allow toolmakers to explain why they need CSP exceptions Mar 3, 2026
@Power-Maverick
Copy link
Contributor

@copilot show required and optional in separate section and user should have the ability to allow all required but selet which optional to allow by selecting them.

… checkboxes

Co-authored-by: Power-Maverick <36135520+Power-Maverick@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 3, 2026

@copilot show required and optional in separate section and user should have the ability to allow all required but selet which optional to allow by selecting them.

Done in dc44571. The modal now has two distinct sections:

  • Required — plain list showing all non-optional domains with their exceptionReason
  • Optional — each domain has a pre-checked checkbox; the user can uncheck any they don't want to allow before clicking "Allow & Continue"

Only the checked optional domains are stored and applied to the tool's CSP. Required domains are always applied when consent is granted.

CSP modal with required/optional sections

Copilot AI changed the title feat: Allow toolmakers to explain why they need CSP exceptions feat: Allow toolmakers to explain why they need CSP exceptions with selective optional domain consent Mar 3, 2026
@Power-Maverick
Copy link
Contributor

@copilot Store the required domain list as part of the consent as well. In future we would need to track at given point in time which domains were given consent and do we need to trigger getting re-consent because the domain list has changed

Co-authored-by: Power-Maverick <36135520+Power-Maverick@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 3, 2026

@copilot Store the required domain list as part of the consent as well. In future we would need to track at given point in time which domains were given consent and do we need to trigger getting re-co...

Done in c304e8d. Required domains are now stored at consent time in a new cspRequiredConsents key alongside cspOptionalConsents.

Changes:

  • settings.ts: New cspRequiredConsents: { [toolId: string]: string[] } field
  • settingsManager.ts: grantCspConsent(toolId, requiredDomains, approvedOptionalDomains) — stores required domains separately; revokeCspConsent clears them; new getApprovedRequiredDomains() method
  • toolManagement.ts: Computes required domains from tool.cspExceptions (all non-optional entries) before granting consent
  • IPC chain (api.ts, preload.ts, index.ts) updated to carry requiredDomains through

This means when re-consent detection is implemented, it can compare the stored cspRequiredConsents[toolId] against the tool's current cspExceptions to know if re-consent is needed.

@Power-Maverick
Copy link
Contributor

@copilot few more changes

  1. Show checkbox for required consent as well but already checked and disabled (for consistency)
  2. In the user-settings (storing the consent) group all the different cspConsent into one attribute so it becomes easy for reviewing (see more below)

Instead of this

"cspConsents": {
		"12345678-27a1-4acb-b8f4-6b62ef0021e0": true,
		"12345678-c6cb-4c3c-80b2-ea46490c2b79": true
	},
	"cspRequiredConsents": {
		"12345678-27a1-4acb-b8f4-6b62ef0021e0": [
			"https://raw.githubusercontent.com"
		],
		"12345678-c6cb-4c3c-80b2-ea46490c2b79": [
			"fonts.googleapis.com",
			"api.example.com"
		]
	},
	"cspOptionalConsents": {
		"12345678-27a1-4acb-b8f4-6b62ef0021e0": [],
		"12345678-c6cb-4c3c-80b2-ea46490c2b79": [
			"cdn.example.com"
		]
	},

Do this

"cspConsents": {
		"12345678-27a1-4acb-b8f4-6b62ef0021e0": {
                      "allowed": true,
                      "required": [
			"https://raw.githubusercontent.com"
		      ],
                      "optional": []
                  },
		"12345678-c6cb-4c3c-80b2-ea46490c2b79": {
                      "allowed": true,
                      "required": [
			"fonts.googleapis.com",
			"api.example.com"
		      ],
                      "optional": [
                        "cdn.example.com"
                      ]
                  },
	}

…red domains

Co-authored-by: Power-Maverick <36135520+Power-Maverick@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 3, 2026

@copilot few more changes

  1. Show checkbox for required consent as well but already checked and disabled (for consistency)
  2. In the user-settings (storing the consent) group all the different csp...

Done in 8e1f1a4.

1. Required checkboxes: Required domains now render as pre-checked, disabled checkboxes — same layout as optional but with cursor: not-allowed and reduced opacity to signal they can't be changed.

2. Unified consent storage — three separate keys collapsed into one:

"cspConsents": {
    "12345678-27a1-4acb-b8f4-6b62ef0021e0": {
        "allowed": true,
        "required": ["https://raw.githubusercontent.com"],
        "optional": []
    },
    "12345678-c6cb-4c3c-80b2-ea46490c2b79": {
        "allowed": true,
        "required": ["fonts.googleapis.com", "api.example.com"],
        "optional": ["cdn.example.com"]
    }
}

cspRequiredConsents and cspOptionalConsents are removed. A new CspConsentRecord interface ({ allowed, required, optional }) is exported from settings.ts for use in future re-consent detection logic.

@Power-Maverick Power-Maverick marked this pull request as ready for review March 3, 2026 21:43
Copilot AI review requested due to automatic review settings March 3, 2026 21:43
@github-actions
Copy link

github-actions bot commented Mar 3, 2026

Bundle Size Report 📦

Bundle Size
Main Process 4.88 MB
Renderer JS 846.99 KB
Renderer CSS 85.5 KB
Total 5.79 MB

Bundle Analysis Reports

The detailed bundle analysis reports are available in the workflow artifacts:

  • 📊 Main Process: stats-main.html
  • 📊 Renderer Process: stats-renderer.html

Download the artifacts from the workflow run to view interactive visualizations.


Bundle size tracking is now active! This helps prevent bundle bloat.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds richer CSP exception metadata for tool manifests (reason + optionality) and updates the CSP consent flow so users can selectively approve optional domains while required domains are always enforced once consent is granted.

Changes:

  • Introduces CspExceptionEntry / CspExceptionSource and a normalizeCspExceptionSource() helper to support both legacy string sources and detailed entries.
  • Updates the CSP consent modal UI + controller to display required vs optional exceptions separately and return the user-approved optional domain list.
  • Reworks persisted CSP consent storage to a per-tool CspConsentRecord (allowed + required/optional domain snapshots) and applies only approved optional domains when building tool CSP.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/common/types/common.ts Adds CspExceptionEntry, CspExceptionSource, and normalizeCspExceptionSource(); updates CSP exception typings.
src/common/types/settings.ts Introduces CspConsentRecord and updates UserSettings.cspConsents to store richer consent records.
src/common/types/api.ts Updates Toolbox API typings for grantCspConsent and getCspConsents.
src/main/managers/settingsManager.ts Stores/reads unified CSP consent records and exposes required/optional domain accessors.
src/main/managers/browserviewProtocolManager.ts Applies optional CSP domains only if explicitly approved by the user.
src/main/preload.ts Updates the exposed toolboxAPI.grantCspConsent signature to pass required + approved optional domains.
src/main/index.ts Updates IPC handler for granting CSP consent to accept required/optional domain lists.
src/renderer/modules/cspExceptionModal.ts Changes modal return type to string[] | null and forwards approved optional domains from modal to caller.
src/renderer/modals/cspException/view.ts Renders required/optional sections with checkboxes and inline-markdown reasons (escaped).
src/renderer/modals/cspException/controller.ts Collects checked optional domains and sends them back on accept.
src/renderer/modules/toolManagement.ts Computes required domains from manifest and persists consent with required + approved optional domains.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@github-actions
Copy link

github-actions bot commented Mar 3, 2026

Bundle Size Report 📦

Bundle Size
Main Process 4.88 MB
Renderer JS 847.31 KB
Renderer CSS 85.5 KB
Total 5.79 MB

Bundle Analysis Reports

The detailed bundle analysis reports are available in the workflow artifacts:

  • 📊 Main Process: stats-main.html
  • 📊 Renderer Process: stats-renderer.html

Download the artifacts from the workflow run to view interactive visualizations.


Bundle size tracking is now active! This helps prevent bundle bloat.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@github-actions
Copy link

github-actions bot commented Mar 3, 2026

Bundle Size Report 📦

Bundle Size
Main Process 4.88 MB
Renderer JS 847.34 KB
Renderer CSS 85.5 KB
Total 5.79 MB

Bundle Analysis Reports

The detailed bundle analysis reports are available in the workflow artifacts:

  • 📊 Main Process: stats-main.html
  • 📊 Renderer Process: stats-renderer.html

Download the artifacts from the workflow run to view interactive visualizations.


Bundle size tracking is now active! This helps prevent bundle bloat.

@Power-Maverick
Copy link
Contributor

Power-Maverick commented Mar 3, 2026

Need following test:

  • ✅ one domain across multiple category but all required: will only show first exceptionReason though
  • ✅ one domain across required and optional
  • ✅ one domain across same category and required

@Power-Maverick Power-Maverick added the unit-testing Unit testing is in process. Dev was completed label Mar 3, 2026
@github-actions
Copy link

github-actions bot commented Mar 5, 2026

Bundle Size Report 📦

Bundle Size
Main Process 4.88 MB
Renderer JS 866.58 KB
Renderer CSS 86.25 KB
Total 5.81 MB

Bundle Analysis Reports

The detailed bundle analysis reports are available in the workflow artifacts:

  • 📊 Main Process: stats-main.html
  • 📊 Renderer Process: stats-renderer.html

Download the artifacts from the workflow run to view interactive visualizations.


Bundle size tracking is now active! This helps prevent bundle bloat.

@Power-Maverick Power-Maverick merged commit aebd2a8 into dev Mar 5, 2026
2 checks passed
@Power-Maverick Power-Maverick deleted the copilot/add-csp-exception-explanation branch March 5, 2026 02:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

unit-testing Unit testing is in process. Dev was completed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants