Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d5dc2af
Added a Modules view: browse build.cfengine.com modules and open thei…
aleksandrychev Oct 16, 2026
55baa4e
Added modules are saved with cfbs, and their input answered on the mo…
aleksandrychev Oct 16, 2026
b8caabf
Added Git Checkout, Systemd Unit, HTTP Request and Run Ansible Playbo…
aleksandrychev Oct 16, 2026
e8e7abe
Added a Custom Promise block, for any CFEngine Build module's promise…
aleksandrychev Oct 16, 2026
75400c3
A block search that finds nothing points at the modules that match it
aleksandrychev Oct 16, 2026
22c5bcc
AI agents can search, add, answer and remove CFEngine Build modules
aleksandrychev Oct 16, 2026
26ca3ff
Tested downloading the module index and saving modules with cfbs
aleksandrychev Oct 16, 2026
9a5c48e
Tested the Modules view, module pages and modules from the Blocks pal…
aleksandrychev Oct 16, 2026
cb9aadf
Tested saving a module with the real sidecar and cfbs, offline
aleksandrychev Oct 16, 2026
61967d7
A module project lists CFEngine Build modules but can't add them
aleksandrychev Oct 16, 2026
4cdfba6
Tested modules end to end: browsing, pages, input, promise-type block…
aleksandrychev Oct 16, 2026
37f9349
Test runs build the project with its CFEngine Build modules and their…
aleksandrychev Oct 16, 2026
3828f29
A promise-type block's note is a policy comment, not a comment attribute
aleksandrychev Oct 16, 2026
a5979df
Git Checkout asks for the branch, tag or commit
aleksandrychev Oct 16, 2026
c5569fa
Module search matches every word of the search, in any order
aleksandrychev Oct 16, 2026
941bf41
Custom Promise reads attribute rows as CFEngine writes them too
aleksandrychev Oct 16, 2026
7bc20a5
Module tools confirm an answer and read a README's tables as rows
aleksandrychev Oct 16, 2026
1f402d8
A block's module shows the dependencies cfbs will add with it on save
aleksandrychev Oct 16, 2026
5a7fcb7
A promise module's error is traced to its block by the file and line …
aleksandrychev Oct 16, 2026
8ea2777
AI agents see the dependencies saving adds, and clearer module wording
aleksandrychev Oct 16, 2026
0147c83
Promise-type blocks advise installing python3, with a button that add…
aleksandrychev Oct 16, 2026
51777e2
The python3 advice can be ignored for the project, and its buttons si…
aleksandrychev Oct 16, 2026
acec1a4
The demo downloads the CFEngine logo with an HTTP Request and shows it
aleksandrychev Oct 16, 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
4 changes: 2 additions & 2 deletions SECURITY-CHECKLIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ on each push and pull request, and weekly:

| # | 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. |
| 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)), the CFEngine Build module index and module details (`INDEX_URL`, `DETAILS_URL` in [`src/main/modules.ts`](src/main/modules.ts); a README from them becomes plain data (`readmeTree`: allowlisted tags, http(s) links, no attributes) rendered as React elements, never as HTML). `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. |
| 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. `img-src` adds GitHub's hosts (`github.com`, `raw.github.com`, `*.githubusercontent.com`) for module READMEs' images and maintainers' avatars; images elsewhere are shown as links (`githubImage` in [`src/renderer/src/modules/moduleDetails.ts`](src/renderer/src/modules/moduleDetails.ts)). |
| 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. |
Expand Down
101 changes: 101 additions & 0 deletions blocks/ansible-playbook.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
{
"$schema": "./schemas/block-descriptor.v1.json",
"schema_version": 1,
"id": "ansible-playbook",
"version": 1,
"name": "Run Ansible Playbook",
"category": "From modules",
"description": "Runs an Ansible playbook on the host (connection local by default).",
"compile_target": "own_bundle",
"requires_module": "promise-type-ansible",
"steps": [
{
"promise_type": "ansible",
"promiser": "{{playbook}}",
"attributes": {
"inventory": {
"if_set": "inventory",
"value": "{{inventory}}"
},
"tags": {
"if_set": "tags",
"value": {
"list_param": "tags"
}
},
"limit": {
"if_set": "limit",
"value": {
"list_param": "limit"
}
},
"become": "{{become}}",
"become_user": {
"if_set": "become_user",
"value": "{{become_user}}"
}
}
}
],
"parameters": [
{
"name": "playbook",
"label": "Playbook",
"type": "string",
"required": true,
"path": "absolute",
"help": "Its absolute path on the host, e.g. /srv/ansible/site.yml. Needs Ansible on the host."
},
{
"name": "inventory",
"label": "Inventory",
"type": "string",
"required": false,
"default": "",
"path": "absolute"
},
{
"name": "tags",
"label": "Tags",
"type": "text",
"required": false,
"default": "",
"allow_list": true,
"help": "Only the plays and tasks with these tags, one per line."
},
{
"name": "limit",
"label": "Limit to hosts",
"type": "text",
"required": false,
"default": "",
"allow_list": true,
"help": "Hosts or groups, one per line."
},
{
"name": "become",
"label": "Become",
"type": "string",
"required": true,
"default": "false",
"options": [
{
"value": "false",
"label": "No"
},
{
"value": "true",
"label": "Yes (sudo)"
}
]
},
{
"name": "become_user",
"label": "Become user",
"type": "string",
"required": false,
"default": "",
"help": "Empty: root."
}
]
}
94 changes: 94 additions & 0 deletions blocks/custom-promise.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
{
"$schema": "./schemas/block-descriptor.v1.json",
"schema_version": 1,
"id": "custom-promise",
"version": 1,
"name": "Custom Promise",
"category": "From modules",
"description": "A promise of a CFEngine Build module's promise type, e.g. json or groups.",
"compile_target": "own_bundle",
"requires_module": "promise-type-{{promise_type}}",
"steps": [
{
"promise_type": "{{promise_type}}",
"promiser": "{{promiser}}",
"attribute_rows": "attributes",
"attributes": {}
}
],
"parameters": [
{
"name": "promise_type",
"label": "Promise type",
"type": "string",
"required": true,
"default": "json",
"options": [
{
"value": "appstreams",
"label": "AppStream modules"
},
{
"value": "assert",
"label": "assert (for test policy)"
},
{
"value": "docker_compose",
"label": "docker_compose (Docker Compose)"
},
{
"value": "groups",
"label": "groups (local groups)"
},
{
"value": "json",
"label": "json (edit JSON files)"
},
{
"value": "sshd",
"label": "sshd (sshd configuration)"
},
{
"value": "symlinks",
"label": "symlinks"
},
{
"value": "yaml",
"label": "yaml (edit YAML files)"
},
{
"value": "git",
"label": "git"
},
{
"value": "systemd",
"label": "systemd"
},
{
"value": "http",
"label": "http"
},
{
"value": "ansible",
"label": "ansible"
}
],
"help": "Adds its module (promise-type-…) to the project. Its README lists the attributes: on the module's page, or get_module for AI agents."
},
{
"name": "promiser",
"label": "Promiser",
"type": "string",
"required": true,
"help": "What the promise is about, e.g. a file path or a group name."
},
{
"name": "attributes",
"label": "Attributes",
"type": "text",
"required": false,
"default": "",
"help": "One per line: name = value (or name => \"value\"). A list: name = { \"a\", \"b\" }."
}
]
}
94 changes: 94 additions & 0 deletions blocks/git-checkout.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
{
"$schema": "./schemas/block-descriptor.v1.json",
"schema_version": 1,
"id": "git-checkout",
"version": 1,
"name": "Git Checkout",
"category": "From modules",
"description": "Clones a git repository into a folder and keeps it at a branch, tag or commit.",
"compile_target": "own_bundle",
"requires_module": "promise-type-git",
"steps": [
{
"promise_type": "git",
"promiser": "{{destination}}",
"attributes": {
"repository": "{{repository}}",
"version": "{{version}}",
"update": "{{update}}",
"force": "{{force}}",
"depth": {
"if_set": "depth",
"value": "{{depth}}"
}
}
}
],
"parameters": [
{
"name": "destination",
"label": "Folder",
"type": "string",
"required": true,
"path": "absolute",
"help": "Where the repository is cloned, e.g. /srv/app. Needs git on the host."
},
{
"name": "repository",
"label": "Repository URL",
"type": "string",
"required": true,
"help": "E.g. https://github.com/cfengine/starter_pack. A private one needs credentials for root (gitcredentials)."
},
{
"name": "version",
"label": "Branch, tag or commit",
"type": "string",
"required": true,
"help": "E.g. main or master: the git module can't follow the remote's default branch unnamed."
},
{
"name": "update",
"label": "Keep it updated",
"type": "string",
"required": true,
"default": "true",
"options": [
{
"value": "true",
"label": "Yes: pull new commits"
},
{
"value": "false",
"label": "No: clone once"
}
]
},
{
"name": "force",
"label": "Local changes",
"type": "string",
"required": true,
"default": "false",
"options": [
{
"value": "false",
"label": "Keep them"
},
{
"value": "true",
"label": "Discard them"
}
]
},
{
"name": "depth",
"label": "History depth",
"type": "number",
"required": false,
"integer": true,
"minimum": 1,
"help": "Empty: the full history. 1: the latest commit only."
}
]
}
Loading
Loading