Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
141 commits
Select commit Hold shift + click to select a range
fca1a65
Added a cfbs init operation to the Python sidecar
aleksandrychev Sep 29, 2026
5cfd7f7
New Project now creates a cfbs project on disk, saved with Cmd+S
aleksandrychev Sep 29, 2026
f65b0f2
Added Open Project, with recent projects
aleksandrychev Sep 29, 2026
4e00388
Projects now mirror the file tree as cfbs modules, in the default nam…
aleksandrychev Oct 1, 2026
b3cffcb
Added policy generation: each save writes the project's .cf files
aleksandrychev Oct 1, 2026
e1d9b1f
Generated policy now explains each data transformation in a comment
aleksandrychev Oct 1, 2026
b32da9d
Render Template now writes its template to its own file in templates/
aleksandrychev Oct 1, 2026
4382ae4
Demo project now publishes a landing page and keeps nginx running
aleksandrychev Oct 1, 2026
2426c74
Generated policy is now one top-down bundle per file, for CFEngine 3.27+
aleksandrychev Oct 1, 2026
219e1b8
Projects can be stored as a policy set or a cfbs module; builder data…
aleksandrychev Oct 1, 2026
444b471
Added a Save button to the top bar
aleksandrychev Oct 1, 2026
38b8599
Groups now compile into their own bundles and take arrows
aleksandrychev Oct 1, 2026
d7dc81a
Generated Policy tab shows the open file's compiled policy
aleksandrychev Oct 1, 2026
b2aeff5
Template editor now follows the app's dark mode
aleksandrychev Oct 1, 2026
11a7114
Mustache syntax help moved from Properties into the template editor
aleksandrychev Oct 1, 2026
0e8f8bf
Properties field labels and compute links are readable in dark mode
aleksandrychev Oct 1, 2026
6c2108a
Hid the React Flow attribution on the canvas
aleksandrychev Oct 1, 2026
4207dc2
blocks: Added file editing, directory, symlink, cleanup and watch blocks
aleksandrychev Oct 1, 2026
9308e6b
blocks: Added Cron Job, Rotate Log and Remove Directory on stdlib bun…
aleksandrychev Oct 1, 2026
a42d7b0
Copy in the Generated Policy tab now copies the policy
aleksandrychev Oct 2, 2026
2e0dd09
Demo project opens already tidied
aleksandrychev Oct 2, 2026
de3a65e
Demo project gained a web root, log pruning and a grouped Security file
aleksandrychev Oct 2, 2026
5c1095d
Ctrl/Cmd +/-/0 now zoom the canvas instead of the whole window
aleksandrychev Oct 2, 2026
0dd7c20
File explorer rows are more compact and its area is larger by default
aleksandrychev Oct 2, 2026
976ba74
New Project asks for the masterfiles version before how to store it
aleksandrychev Oct 2, 2026
7695c10
The Python sidecar warms up in the background at launch
aleksandrychev Oct 2, 2026
a87f3dc
Run Command's "Every agent run" no longer compiles to once a day
aleksandrychev Oct 2, 2026
4d7a39e
Added per-block compiler tests: generated input variants and snapshots
aleksandrychev Oct 2, 2026
2e086d7
Test Results & Logs tab checks Docker and pulls base images
aleksandrychev Oct 2, 2026
2d4d335
Test environments run the policy on Docker hosts from the Test Result…
aleksandrychev Oct 2, 2026
db80a33
Test Results tab shows hosts as a topology, with a coloured log and a…
aleksandrychev Oct 2, 2026
1db93dd
Test runs list their problems, traced to the blocks that caused them
aleksandrychev Oct 2, 2026
e91a8a6
Define Variable can take a different value per condition
aleksandrychev Oct 2, 2026
76c23bf
Test host port fields can be cleared and retyped
aleksandrychev Oct 2, 2026
7e216da
Test host cards got Deploy & run, Start and Stop buttons
aleksandrychev Oct 2, 2026
1f78d8c
Adding a test host starts with picking its platform
aleksandrychev Oct 2, 2026
c7ddaf4
Test hosts can run any Docker image
aleksandrychev Oct 2, 2026
755db2f
Running the policy on one test client no longer fails in update.cf
aleksandrychev Oct 2, 2026
04bd758
Test runs report update.cf errors as problems
aleksandrychev Oct 2, 2026
b1e8479
Starting a test action clears the log filters
aleksandrychev Oct 2, 2026
86751b4
Generated Policy shows its blocks and links code back to them
aleksandrychev Oct 2, 2026
3d42371
Added a Deployment tab: build and check, push to git, or deploy over SSH
aleksandrychev Oct 5, 2026
ce01534
A rejected push offers to pull the remote's commits or overwrite them
aleksandrychev Oct 5, 2026
6760987
Deployment can deploy through an Enterprise hub
aleksandrychev Oct 5, 2026
80f6034
Test environment settings explain the secrets (.env) file
aleksandrychev Oct 5, 2026
d90add1
Test environments set how many agent runs a Deploy & run makes
aleksandrychev Oct 5, 2026
2086f14
A test host added after setup is set up from its card
aleksandrychev Oct 5, 2026
ea48d40
Test host cards can recreate a host from scratch
aleksandrychev Oct 5, 2026
5908933
Setting up a test host no longer fails to fetch cf_promises_validated
aleksandrychev Oct 5, 2026
9562948
Deployment tab: Redesigned around pre-flight checks and one tab per way
aleksandrychev Oct 5, 2026
8a333fa
Top bar views became a segmented control with icons
aleksandrychev Oct 5, 2026
a91220d
SSH deploy works with CFEngine installed from a distro package
aleksandrychev Oct 5, 2026
f97082a
SSH deploy no longer fails on a host missing from known_hosts
aleksandrychev Oct 5, 2026
3c2ba30
Demo project opens with a hand-arranged layout
aleksandrychev Oct 5, 2026
cb11da7
Policy files can have a description, shown on hovering their name
aleksandrychev Oct 5, 2026
8331b9d
compiler: Generated .cf files open with a masterfiles-style banner
aleksandrychev Oct 5, 2026
2175fc0
SSH deploy's remote script moved to deploy.sh
aleksandrychev Oct 5, 2026
a114915
Commit & push no longer commits the test environments' .env files
aleksandrychev Oct 6, 2026
401ee93
Test hosts' published ports listen on 127.0.0.1 only
aleksandrychev Oct 6, 2026
c275760
Test environments read their .env file only from inside the project
aleksandrychev Oct 6, 2026
312d57c
SSH deploy uploads the policy into a private directory on the hub
aleksandrychev Oct 6, 2026
7083e65
SSH deploy runs cf-remote deploy instead of its own script
aleksandrychev Oct 6, 2026
9dcc62c
Saving a project no longer deletes its test environments
aleksandrychev Oct 6, 2026
d7fa226
Hub repository match no longer fails on a URL ending in a slash
aleksandrychev Oct 6, 2026
4a951b2
Class picker groups read "Defined in Web · runs only if linux"
aleksandrychev Oct 6, 2026
6626d04
Enterprise hub deploy reports again when it turned on VCS deploys
aleksandrychev Oct 6, 2026
d611d78
Enterprise hub connection errors say why for dual-stack hosts
aleksandrychev Oct 6, 2026
d77ae04
Module projects made with the builder can be opened
aleksandrychev Oct 6, 2026
5d994da
Save As with test environments leaves the new git project clean
aleksandrychev Oct 6, 2026
16c2a4d
python: cfpb_build.py is part of the wheel
aleksandrychev Oct 6, 2026
46d9292
End-to-end runs can keep the app off screen
aleksandrychev Oct 6, 2026
9e961da
Tests run in projects by speed, including the main process
aleksandrychev Oct 6, 2026
462fff6
Added unit tests for the renderer's deployment, test run and editor l…
aleksandrychev Oct 6, 2026
65c04af
Added unit tests for the main process
aleksandrychev Oct 6, 2026
c9069a1
Added integration tests for projects, git flows and the Enterprise hub
aleksandrychev Oct 6, 2026
7854cd1
python: Added tests for the build and deploy commands
aleksandrychev Oct 6, 2026
1ac0477
Added component tests for panels, dialogs and the Deployment and Test…
aleksandrychev Oct 6, 2026
5734400
Added end-to-end journeys for building policy, deployment and test en…
aleksandrychev Oct 6, 2026
f9a11dd
README: Added the CI status badge
aleksandrychev Oct 6, 2026
88f5e92
Added the Electron security checklist and a workflow that checks it
aleksandrychev Oct 6, 2026
ac5a471
Added APP icon
aleksandrychev Oct 6, 2026
8fb9ef0
e2e: CI runs the app on its virtual screen, not hidden
aleksandrychev Oct 6, 2026
27e576a
The app loads from app://bundle instead of file://
aleksandrychev Oct 6, 2026
5ce78c8
e2e: Tests on CI wait for the app's window to be shown
aleksandrychev Oct 6, 2026
ae504ba
Added test coverage for TypeScript and Python, reported to Codecov
aleksandrychev Oct 6, 2026
6cb5421
Added a check that pull request commits are signed off with valid titles
aleksandrychev Oct 6, 2026
d4eeeb5
fileTree: Comment no longer points to an untracked plan
aleksandrychev Oct 6, 2026
8cf2db3
Added a CodeQL workflow with the security-and-quality queries
aleksandrychev Oct 6, 2026
7dd20a8
SSH deploy no longer accepts a host whose user part starts an ssh option
aleksandrychev Oct 6, 2026
4c3a7c2
Git no longer runs a project's own hooks, fsmonitor or command settings
aleksandrychev Oct 6, 2026
829bf24
Git fetches and pushes use the user's own core.sshCommand when one is…
aleksandrychev Oct 6, 2026
160eaed
Saving no longer writes or deletes through a symlinked folder out of …
aleksandrychev Oct 6, 2026
19e71d9
Commit no longer appends lines to a file a symlinked .gitignore point…
aleksandrychev Oct 6, 2026
7246d78
Saving no longer overwrites a policy file the builder didn't generate
aleksandrychev Oct 6, 2026
0d69b53
Packaged builds no longer load the page ELECTRON_RENDERER_URL names
aleksandrychev Oct 6, 2026
26fcfdd
app:// answers 404 to a malformed %-escape instead of throwing
aleksandrychev Oct 6, 2026
103b196
Permission checks are denied like permission requests, except clipboa…
aleksandrychev Oct 6, 2026
e47237e
SECURITY-CHECKLIST.md: Item 1 no longer claims git remotes are https …
aleksandrychev Oct 6, 2026
cf28c18
Saving no longer accepts cfbs run steps in the builder's modules
aleksandrychev Oct 6, 2026
2b4026c
Hub VCS settings no longer upload a key file whose path the page names
aleksandrychev Oct 6, 2026
4ef70a4
testenv: Added tests for the IPC handlers' request checks
aleksandrychev Oct 6, 2026
55ef17b
testenv: A project's masterfiles version can no longer name a folder …
aleksandrychev Oct 6, 2026
c05af8c
testenv: Test runs build only local modules with the builder's own steps
aleksandrychev Oct 6, 2026
d6e0395
Cancel, quit and timeouts no longer leave containers and ssh running
aleksandrychev Oct 6, 2026
e854d51
testenv: A failed masterfiles download no longer breaks later test runs
aleksandrychev Oct 6, 2026
4c22a17
testenv: Overlapping test runs no longer build in the same folder
aleksandrychev Oct 6, 2026
58b66cf
python: Init cleans up after a timeout or an exit from cfbs too
aleksandrychev Oct 6, 2026
bd1fc4c
testenv: Clients bootstrap again when Start recreates the hub
aleksandrychev Oct 6, 2026
8d45535
testenv: Test hosts install a CFEngine package only if its checksum m…
aleksandrychev Oct 6, 2026
b20e8b5
testenv: .env values no longer go into the test containers' config
aleksandrychev Oct 6, 2026
ce4f28a
compiler: Fixed "unless" conditions negating only their first class
aleksandrychev Oct 6, 2026
a45463c
compiler: Malformed custom class expressions are skipped with a note
aleksandrychev Oct 6, 2026
cdf1a4c
compiler: Template data copies no longer collide with a block's other…
aleksandrychev Oct 6, 2026
5f04ae0
compiler: Templates render like datastate() for variables that aren't…
aleksandrychev Oct 6, 2026
7a24fc3
compiler: A Render Template computed from data renders it, not a plac…
aleksandrychev Oct 6, 2026
818b3d0
compiler: Set Config Values splits settings computed from data at run…
aleksandrychev Oct 6, 2026
51cd548
compiler: Define Variable and Define Class run commands in cf-agent only
aleksandrychev Oct 6, 2026
8d8cd62
compiler: Labels and conditions can no longer inject promises
aleksandrychev Oct 6, 2026
d8e1e48
compiler: A backslash before a single quote is kept in CFEngine strings
aleksandrychev Oct 6, 2026
b1523f5
compiler: Literal lists in a chain keep backslashes and single quotes
aleksandrychev Oct 6, 2026
a8000e6
compiler: Names are checked before they're written into policy
aleksandrychev Oct 6, 2026
9f3cd03
evaluateDecorator: An emptied parameter is read as its default, as co…
aleksandrychev Oct 6, 2026
62bec8e
Deployment: Pull theirs, then push no longer discards unsaved edits
aleksandrychev Oct 6, 2026
ede5d46
Builds and test runs no longer look out of date after layout-only cha…
aleksandrychev Oct 6, 2026
b936f1a
Selecting another file no longer marks the project unsaved
aleksandrychev Oct 6, 2026
3e51111
Environment variables are no longer lost when their dialog closes wit…
aleksandrychev Oct 6, 2026
e2f51ef
Test environment name, .env file and ports are saved on blur, one und…
aleksandrychev Oct 6, 2026
305b8f4
The CFEngine version field no longer starts a sidecar per keystroke
aleksandrychev Oct 6, 2026
f7e5bda
Saving while another save runs now saves after it instead of doing no…
aleksandrychev Oct 6, 2026
b7b6042
A malformed project.json now says so instead of showing a raw TypeError
aleksandrychev Oct 6, 2026
498ffb8
A project saved from the demo gets a test environment id of its own
aleksandrychev Oct 6, 2026
dbc423b
e2e: Docker cleanup no longer removes demo containers the suite didn'…
aleksandrychev Oct 6, 2026
0dc8c5d
Coverage: Pull requests from forks no longer run the Codecov upload
aleksandrychev Oct 6, 2026
9b6f358
Coverage: Only the upload job gets an OIDC token, actions pinned to c…
aleksandrychev Oct 6, 2026
9cb90fa
e2e: Console errors are collected from launch, the main process's too
aleksandrychev Oct 6, 2026
039d1c0
e2e: The encoded traversal check now asks for a path that could escape
aleksandrychev Oct 6, 2026
e921744
e2e: The navigation check waits for Chromium's refusal, not a fixed 5…
aleksandrychev Oct 6, 2026
16ce01e
Shortened header comments to the house style
aleksandrychev Oct 6, 2026
8ed7be8
Offline, the masterfiles version fallback is logged as a warning
aleksandrychev Oct 6, 2026
7cab366
Removed the CodeQL workflow: the org enforces CodeQL's default setup
aleksandrychev Oct 6, 2026
5c80d47
Each policy file is its own namespace again
aleksandrychev Oct 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,25 @@ jobs:
- run: npm ci
- run: npm run test

integration:
name: Integration tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
# The main process runs the real Python sidecar.
- uses: astral-sh/setup-uv@v7
with:
enable-cache: true
- run: uv sync --frozen --directory python
- run: npm run test:integration

e2e:
name: E2E smoke test
name: E2E tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
Expand All @@ -63,6 +80,11 @@ jobs:
node-version-file: .nvmrc
cache: npm
- run: npm ci
# Saving a project runs the Python sidecar's policy compiler.
- uses: astral-sh/setup-uv@v7
with:
enable-cache: true
- run: uv sync --frozen --directory python
# Ubuntu 24.04's AppArmor blocks the unprivileged user namespaces Chromium's sandbox needs.
- run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- run: xvfb-run -a npm run test:e2e
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/commits.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Commits

# Every commit a pull request adds follows the commit rules:
# signed off by its author (git commit -s), a title of at most 80 characters with no ending
# punctuation, and a blank line before any body.
on:
pull_request:

permissions:
contents: read

jobs:
rules:
name: Sign-off and titles
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-node@v7
with:
node-version-file: .nvmrc
- run: node scripts/commit-checks.mjs ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}
86 changes: 86 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Coverage

# Measures test coverage and uploads it to Codecov, one flag per language (badges in README.md).
# Codecov's GitHub app authenticates the upload through OIDC: no token, so it runs in the
# org's own repository only, for pushes and same-repository pull requests (forks get no OIDC token).
on:
push:
branches: [main]
pull_request:

permissions:
contents: read

concurrency:
group: coverage-${{ github.ref }}
cancel-in-progress: true

jobs:
typescript:
name: TypeScript
if: github.repository_owner == 'cfengine' && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm run test:coverage
- uses: actions/upload-artifact@v7
with:
name: coverage-typescript
path: coverage/lcov.info
retention-days: 1

python:
name: Python
if: github.repository_owner == 'cfengine' && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
with:
enable-cache: true
- run: uv sync --frozen --directory python
- run: uv run --no-sync --directory python pytest --cov --cov-report=xml
- uses: actions/upload-artifact@v7
with:
name: coverage-python
path: python/coverage.xml
retention-days: 1

# Only this job gets an OIDC token: it runs none of the project's code or dependencies.
upload:
name: Upload
needs: [typescript, python]
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
# Codecov maps the reports' paths onto the repository's files.
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/download-artifact@v7
with:
name: coverage-typescript
path: coverage
- uses: actions/download-artifact@v7
with:
name: coverage-python
path: python
- uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7
with:
use_oidc: true
files: coverage/lcov.info
flags: typescript
fail_ci_if_error: true
- uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7
with:
use_oidc: true
files: python/coverage.xml
flags: python
fail_ci_if_error: true
71 changes: 71 additions & 0 deletions .github/workflows/electron-security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Electron security

# Checks the app follows Electron's security checklist (SECURITY-CHECKLIST.md): in the source,
# in the running window, and in the packaged binary. Weekly too: Electron support can lapse
# and advisories appear without any change here.
on:
push:
branches: [main]
pull_request:
schedule:
- cron: '0 6 * * 1'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: electron-security-${{ github.ref }}
cancel-in-progress: true

jobs:
source:
name: Source checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version-file: .nvmrc
- run: node scripts/security-checks.mjs

electron:
name: Supported Electron, no advisories
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version-file: .nvmrc
- run: node scripts/electron-support-check.mjs

runtime:
name: Running window
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
# Ubuntu 24.04's AppArmor blocks the unprivileged user namespaces Chromium's sandbox needs.
- run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- run: npx electron-vite build
- run: xvfb-run -a npx playwright test -c e2e/playwright.config.ts e2e/security.spec.ts

packaged:
name: Fuses in the packaged app
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npx electron-vite build
# The sidecar isn't what's checked here: an empty bundle folder satisfies extraResources.
- run: mkdir -p python/dist/cfpb-backend
- run: npx electron-builder --linux --dir
- run: node scripts/fuses-check.mjs release/linux-unpacked/cfengine-policy-builder
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,7 @@ python/build/
# Playwright (e2e/)
test-results/
playwright-report/

# Python coverage (npm run backend:coverage)
.coverage
coverage.xml
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# CFEngine Policy Builder

[![CI](https://github.com/cfengine/policy-builder/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/cfengine/policy-builder/actions/workflows/ci.yml)
[![Electron security](https://github.com/cfengine/policy-builder/actions/workflows/electron-security.yml/badge.svg?branch=main)](SECURITY-CHECKLIST.md)
[![TypeScript coverage](https://img.shields.io/codecov/c/github/cfengine/policy-builder/main?flag=typescript&label=TypeScript%20coverage)](https://app.codecov.io/gh/cfengine/policy-builder?flags%5B0%5D=typescript)
[![Python coverage](https://img.shields.io/codecov/c/github/cfengine/policy-builder/main?flag=python&label=Python%20coverage)](https://app.codecov.io/gh/cfengine/policy-builder?flags%5B0%5D=python)

Desktop application for building CFEngine policy.

**Stack:**
Expand Down Expand Up @@ -27,6 +32,11 @@ npm run dev # launches Electron app
prerequisite. Without it, the app runs but anything touching the backend
reports that it cannot be found.

## Security

How the app follows Electron's security recommendations, item by item, and
how that is checked: [SECURITY-CHECKLIST.md](SECURITY-CHECKLIST.md).

## Project structure

electron-vite builds three targets from `src/`:
Expand Down
46 changes: 46 additions & 0 deletions SECURITY-CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Electron security checklist

[![Electron security](https://github.com/cfengine/policy-builder/actions/workflows/electron-security.yml/badge.svg?branch=main)](https://github.com/cfengine/policy-builder/actions/workflows/electron-security.yml)

How CFEngine Policy Builder follows Electron's
[security recommendations](https://www.electronjs.org/docs/latest/tutorial/security#checklist-security-recommendations),
item by item, with the code that shows it. The
[Electron security](.github/workflows/electron-security.yml) workflow checks every item marked ✅
on each push and pull request, and weekly:

- **Source checks:** [`scripts/security-checks.mjs`](scripts/security-checks.mjs) reads the code and config each item depends on.
- **Running window:** [`e2e/security.spec.ts`](e2e/security.spec.ts) inspects the real app's window: its web preferences, CSP, permissions, navigation and popups.
- **Packaged app:** [`scripts/fuses-check.mjs`](scripts/fuses-check.mjs) reads the fuses from a packaged binary.
- **Electron version:** [`scripts/electron-support-check.mjs`](scripts/electron-support-check.mjs) checks the Electron major is still supported and `npm audit` has no advisory for it.

| # | Recommendation | Status | Evidence |
| --- | --- | --- | --- |
| 1 | [Only load secure content](https://www.electronjs.org/docs/latest/tutorial/security#1-only-load-secure-content) | ✅ | The packaged app loads its own files only, from `app://bundle/` (item 18). Everything the main process fetches itself is https: the Enterprise hub (`checkedUrl` in [`src/main/hub.ts`](src/main/hub.ts) refuses anything else), the masterfiles versions index (`VERSIONS_URL` in [`src/main/project.ts`](src/main/project.ts)). `http://` appears only for the development server on localhost. The exception is a project's git remote: the user's choice of URL (`REMOTE_URL` in [`src/main/deploy.ts`](src/main/deploy.ts) also takes `http://`, `git://`, `file://` and local paths), fetched and pushed by the system's git. |
| 2 | [Do not enable Node.js integration for remote content](https://www.electronjs.org/docs/latest/tutorial/security#2-do-not-enable-nodejs-integration-for-remote-content) | ✅ | `nodeIntegration` is never enabled (`createWindow` in [`src/main/index.ts`](src/main/index.ts)); the running page has no `require` or `process`. |
| 3 | [Enable context isolation](https://www.electronjs.org/docs/latest/tutorial/security#3-enable-context-isolation) | ✅ | `contextIsolation: true` in `createWindow` ([`src/main/index.ts`](src/main/index.ts)). |
| 4 | [Enable process sandboxing](https://www.electronjs.org/docs/latest/tutorial/security#4-enable-process-sandboxing) | ✅ | `sandbox: true` in `createWindow` ([`src/main/index.ts`](src/main/index.ts)); no `--no-sandbox`. |
| 5 | [Handle session permission requests from remote content](https://www.electronjs.org/docs/latest/tutorial/security#5-handle-session-permission-requests-from-remote-content) | ✅ | `setPermissionRequestHandler` and `setPermissionCheckHandler` in [`src/main/index.ts`](src/main/index.ts) grant only `clipboard-sanitized-write` (Copy in Generated Policy) and deny the rest, whether requested or only checked. |
| 6 | [Do not disable `webSecurity`](https://www.electronjs.org/docs/latest/tutorial/security#6-do-not-disable-websecurity) | ✅ | Never set; the running window reports `webSecurity: true`. |
| 7 | [Define a Content Security Policy](https://www.electronjs.org/docs/latest/tutorial/security#7-define-a-content-security-policy) | ✅ | The `<meta http-equiv="Content-Security-Policy">` in [`src/renderer/index.html`](src/renderer/index.html): `default-src 'self'`, `script-src 'self'` (no inline scripts, no `eval`). `style-src` allows `'unsafe-inline'`, which Emotion/MUI need for their injected styles. |
| 8 | [Do not enable `allowRunningInsecureContent`](https://www.electronjs.org/docs/latest/tutorial/security#8-do-not-enable-allowrunninginsecurecontent) | ✅ | Never set; the running window reports `false`. |
| 9 | [Do not enable experimental features](https://www.electronjs.org/docs/latest/tutorial/security#9-do-not-enable-experimental-features) | ✅ | Never set; the running window reports `false`. |
| 10 | [Do not use `enableBlinkFeatures`](https://www.electronjs.org/docs/latest/tutorial/security#10-do-not-use-enableblinkfeatures) | ✅ | Never set. |
| 11 | [Do not use `allowpopups` for WebViews](https://www.electronjs.org/docs/latest/tutorial/security#11-do-not-use-allowpopups-for-webviews) | ✅ | The app has no `<webview>`. |
| 12 | [Verify WebView options before creation](https://www.electronjs.org/docs/latest/tutorial/security#12-verify-webview-options-before-creation) | ✅ | `webviewTag` stays off (the running window reports `false`), so no webview can be attached. |
| 13 | [Disable or limit navigation](https://www.electronjs.org/docs/latest/tutorial/security#13-disable-or-limit-navigation) | ✅ | The `will-navigate` handler in [`src/main/index.ts`](src/main/index.ts) prevents any navigation away from the loaded page; http(s) links go to the system browser instead. |
| 14 | [Disable or limit creation of new windows](https://www.electronjs.org/docs/latest/tutorial/security#14-disable-or-limit-creation-of-new-windows) | ✅ | `setWindowOpenHandler` in [`src/main/index.ts`](src/main/index.ts) always returns `{ action: 'deny' }`; http(s) links open in the system browser. |
| 15 | [Do not use `shell.openExternal` with untrusted content](https://www.electronjs.org/docs/latest/tutorial/security#15-do-not-use-shellopenexternal-with-untrusted-content) | ✅ | The only `shell.openExternal` call is in `openExternalIfSafe` ([`src/main/index.ts`](src/main/index.ts)), which passes only `http:` and `https:` URLs. |
| 16 | [Use a current version of Electron](https://www.electronjs.org/docs/latest/tutorial/security#16-use-a-current-version-of-electron) | ✅ | Electron 44 ([`package.json`](package.json)), checked weekly against Electron's supported majors and `npm audit`. |
| 17 | [Validate the sender of all IPC messages](https://www.electronjs.org/docs/latest/tutorial/security#17-validate-the-sender-of-all-ipc-messages) | ✅ | Every `ipcMain.handle` checks `isTrustedFrame` ([`src/main/index.ts`](src/main/index.ts)): the top frame of our own page only. Feature modules wrap their handlers in `trusted(…)` ([`project.ts`](src/main/project.ts), [`deploy.ts`](src/main/deploy.ts), [`hub.ts`](src/main/hub.ts), [`testenv.ts`](src/main/testenv.ts)). Unit tests check untrusted senders are refused ([`project.test.ts`](src/main/project.test.ts), [`deploy.test.ts`](src/main/deploy.test.ts)). |
| 18 | [Avoid usage of the `file` protocol and prefer usage of custom protocols](https://www.electronjs.org/docs/latest/tutorial/security#18-avoid-usage-of-the-file-protocol-and-prefer-usage-of-custom-protocols) | ✅ | The packaged app's page is served from `app://bundle/` (`serveRenderer` in [`src/main/index.ts`](src/main/index.ts)), which maps to the built renderer folder and returns 404 for anything outside it, encoded `..` included. Nothing calls `loadFile`, and the `GrantFileProtocolExtraPrivileges` fuse is off ([`electron-builder.yml`](electron-builder.yml)). |
| 19 | [Check which fuses you can change](https://www.electronjs.org/docs/latest/tutorial/security#19-check-which-fuses-you-can-change) | ✅ | `electronFuses` in [`electron-builder.yml`](electron-builder.yml): `RunAsNode`, `EnableNodeOptionsEnvironmentVariable`, `EnableNodeCliInspectArguments` and `GrantFileProtocolExtraPrivileges` off; `EnableCookieEncryption`, `OnlyLoadAppFromAsar` and `EnableEmbeddedAsarIntegrityValidation` on. Read back from the packaged binary. |
| 20 | [Do not expose Electron APIs to untrusted web content](https://www.electronjs.org/docs/latest/tutorial/security#20-do-not-expose-electron-apis-to-untrusted-web-content) | ✅ | The preload ([`src/preload/index.ts`](src/preload/index.ts)) exposes one object, `window.api`, of functions over fixed channels; never `ipcRenderer` itself, and event listeners get only the payload, not the `IpcRendererEvent`. |

## Running the checks locally

```sh
node scripts/security-checks.mjs # source checks
node scripts/electron-support-check.mjs # supported Electron, npm audit
npx electron-vite build && npx playwright test -c e2e/playwright.config.ts e2e/security.spec.ts
npx electron-builder --dir && node scripts/fuses-check.mjs "release/mac/CFEngine Policy Builder.app"
```
Loading
Loading