Security hardening and release readiness
ober
0b7d316176f0304df1e971d63c0947316832e27d
new file mode 100644 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,49 @@ +name: CI + +on: + push: + branches: [main, master] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +env: + JERBOA_VERSION: v0.2.3 + JERBUILD_TOOL: ${{ github.workspace }}/.jerboa/bin/jerbuild + +jobs: + build-test-audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install system tools + run: | + set -eu + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + curl \ + file \ + git \ + libpcre2-dev \ + libvterm-dev \ + pkg-config \ + qt6-base-dev \ + xauth \ + xvfb + + - name: Install Jerboa toolchain + run: sh support/ensure-jerboa.sh "$JERBOA_VERSION" .jerboa/bin + + - name: Build generated libraries + run: make build + + - name: Full test suite + run: make test + + - name: Native dependency audit + run: make audit new file mode 100644 --- /dev/null +++ b/.github/workflows/security-baseline.yml @@ -0,0 +1,35 @@ +name: Security Baseline + +on: + push: + branches: [main, master] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + baseline: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Required release files + run: | + set -eu + test -f LICENSE + test -f SECURITY.md + test -f .gitignore + find . -maxdepth 1 -iname "README*" -type f | grep -q . + + - name: High-confidence secret scan + run: | + set -eu + pattern="(BEGIN (RSA|OPENSSH|EC|DSA|PRIVATE) KEY|ghp_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,}|sk-(ant-api03|proj|svcacct)-[A-Za-z0-9_-]{30,}|AKIA[0-9A-Z]{16})" + matches="$(git grep -n -I -E "$pattern" -- . ":!*.png" ":!*.jpg" ":!*.jpeg" ":!*.gif" ":!*.so" ":!*.dylib" ":!*.o" ":!*.a" ":!*.boot" ":!*.tar.gz" || true)" + if [ -n "$matches" ]; then + echo "$matches" + echo "High-confidence secret pattern found." + exit 1 + fi --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ lib/**/*.wpo # Build symlinks / local artifacts libjsh-ffi.so /.jerboa/ +/dist/ # Build outputs (binaries, objects, boot files, generated headers) jemacs --- a/.jerboa/security.json +++ b/.jerboa/security.json @@ -3,7 +3,7 @@ "repo": "jerboa-emacs", "extends": ["jerboa:cli", "jerboa:gui", "jerboa:ffi"], "paths": { - "production": ["*.ss", "*.sls", "lib/**/*.ss", "lib/**/*.sls", "src/**/*.{ss,sls,c,h,cpp,hpp}", "tools/**", "Makefile"], + "production": ["*.ss", "*.sls", "lib/**/*.ss", "lib/**/*.sls", "src/**/*.{ss,sls,c,h,cpp,hpp}", "scripts/**", "tools/**", "Makefile"], "tests": ["test/**", "tests/**", "**/*-test.ss"], "generated": ["build/**", "dist/**", "target/**", "*.so", "*.dylib", "*.wpo"], "vendor": ["vendor/**", "third_party/**"], @@ -16,7 +16,11 @@ "process": { "shellInterpolation": "deny", "promptForExternalCommands": true }, "network": { "requireTimeouts": true }, "eval": { "stringEval": "deny", "bareRead": "deny", "allowReadEval": false }, - "gui": { "requireCallbackLifetimeGuards": true } + "gui": { "requireCallbackLifetimeGuards": true }, + "releaseEvidence": { + "requireEditorFlowProofForProduction": true, + "sensitiveArtifactPolicy": "no-buffers-prompts-lsp-payloads-clipboard-or-host-data" + } }, "suppressions": [] } new file mode 100644 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. --- a/Makefile +++ b/Makefile @@ -1,5 +1,10 @@ VENDOR ?= $(CURDIR)/vendor BIN_DIR ?= $(HOME)/.local/bin +DIST_DIR ?= dist +SBOM_DIR ?= $(DIST_DIR)/sbom +REPRO_DIR ?= $(DIST_DIR)/reproducibility +EDITOR_FLOW_EVIDENCE_DIR ?= $(DIST_DIR)/editor-flow-evidence +RELEASE_EVIDENCE_DIR ?= $(DIST_DIR)/release-evidence # Standalone jerbuild toolchain. This mirrors the bootstrap path used by # jerboa-code/jerboa-shell: prefer project-local tools, then sibling Jerboa, @@ -147,7 +152,7 @@ SCHEME_CHEZ_DIR = $(shell if [ -n "$(SCHEME_MT)" ]; then ls -d "$(SCHEME_PREFIX) CHEZ_DIR ?= $(SCHEME_CHEZ_DIR) export CHEZ_DIR -.PHONY: all vendor-deps vendor-update check-pcre2-system-deps check-vterm-system-deps deps ensure-vendor-jerboa-tools ensure-jerboa-tools build-jerbuild jemacs-jerbuild jerbuild-check scintilla-vendor-deps scintilla-vendor-clean build rebuild run test-tier0 test-tier2 test-tier3 test-tier4 test-tier5 test-org test-extra test clean clean-generated install \ +.PHONY: all vendor-deps vendor-update check-pcre2-system-deps check-vterm-system-deps deps ensure-vendor-jerboa-tools ensure-jerboa-tools build-jerbuild jemacs-jerbuild jerbuild-check scintilla-vendor-deps scintilla-vendor-clean build rebuild run test-tier0 test-tier2 test-tier3 test-tier4 test-tier5 test-org test-extra test clean clean-generated install audit sbom reproducibility-report editor-flow-evidence release-evidence \ test-org-duration test-org-element test-org-fold test-org-footnote \ test-org-lint test-org-num test-org-property test-org-src test-org-tempo \ test-vtscreen test-debug-repl test-qt test-qt-e2e build-qt binary-qt \ @@ -197,6 +202,11 @@ all: @echo " test-tier4 Shell integration" @echo " test-tier5 Full editor commands" @echo " check-generated Verify generated lib/*.sls is in sync with src/*.ss" + @echo " audit Check native dependency versions for release evidence" + @echo " sbom Write SBOM/provenance evidence to $(SBOM_DIR)" + @echo " reproducibility-report Compare two local jemacs builds" + @echo " editor-flow-evidence Record target-host editor-flow review status" + @echo " release-evidence Write test/audit/SBOM/reproducibility bundle" @echo " stress-window Focused Qt window stress profile" @echo " clean Remove build artifacts" @echo " JEMACS_FEATURES=aws make <target> Enable optional AWS EC2 SSH support" @@ -380,6 +390,45 @@ check-generated: $(MAKE) build git diff --exit-code -- lib +audit: + sh scripts/native-dependency-audit.sh + +sbom: build + JERBUILD_TOOL="$(JERBUILD_TOOL)" SBOM_DIR="$(SBOM_DIR)" scripts/sbom.sh + +reproducibility-report: + JERBUILD_TOOL="$(JERBUILD_TOOL)" REPRO_DIR="$(REPRO_DIR)" MAKE="$(MAKE)" scripts/reproducibility-report.sh + +editor-flow-evidence: + EDITOR_FLOW_EVIDENCE_DIR="$(EDITOR_FLOW_EVIDENCE_DIR)" scripts/editor-flow-evidence.sh + +release-evidence: + rm -rf "$(RELEASE_EVIDENCE_DIR)" + mkdir -p "$(RELEASE_EVIDENCE_DIR)" + $(MAKE) test > "$(RELEASE_EVIDENCE_DIR)/test.log" 2>&1 + $(MAKE) audit > "$(RELEASE_EVIDENCE_DIR)/audit.log" 2>&1 + $(MAKE) reproducibility-report > "$(RELEASE_EVIDENCE_DIR)/reproducibility-report.log" 2>&1 + $(MAKE) editor-flow-evidence > "$(RELEASE_EVIDENCE_DIR)/editor-flow-evidence.log" 2>&1 + $(MAKE) sbom > "$(RELEASE_EVIDENCE_DIR)/sbom.log" 2>&1 + rm -rf "$(RELEASE_EVIDENCE_DIR)/sbom" "$(RELEASE_EVIDENCE_DIR)/reproducibility" "$(RELEASE_EVIDENCE_DIR)/editor-flow-evidence" + cp -R "$(SBOM_DIR)" "$(RELEASE_EVIDENCE_DIR)/sbom" + cp -R "$(REPRO_DIR)" "$(RELEASE_EVIDENCE_DIR)/reproducibility" + cp -R "$(EDITOR_FLOW_EVIDENCE_DIR)" "$(RELEASE_EVIDENCE_DIR)/editor-flow-evidence" + git rev-parse HEAD > "$(RELEASE_EVIDENCE_DIR)/git-commit.txt" 2>/dev/null || true + git status --short > "$(RELEASE_EVIDENCE_DIR)/git-status.txt" 2>/dev/null || true + git diff --stat > "$(RELEASE_EVIDENCE_DIR)/diff-stat.txt" 2>/dev/null || true + uname -a > "$(RELEASE_EVIDENCE_DIR)/build-environment.txt" + "$(JERBUILD_TOOL)" --version >> "$(RELEASE_EVIDENCE_DIR)/build-environment.txt" 2>/dev/null || true + "$(JERBUILD_TOOL)" --jerboa-home >> "$(RELEASE_EVIDENCE_DIR)/build-environment.txt" 2>/dev/null || true + (cc --version || cc -v) > "$(RELEASE_EVIDENCE_DIR)/cc-version.txt" 2>&1 || true + if [ -f jemacs ]; then ./jemacs --version > "$(RELEASE_EVIDENCE_DIR)/binary-version.txt" 2>&1; shasum -a 256 jemacs > "$(RELEASE_EVIDENCE_DIR)/binary-sha256.txt"; fi + find .build.yml .github .jerbuild .jerbuild.freebsd-amd64 .jerbuild.linux-amd64 .jerbuild.linux-arm64 Makefile README.md SECURITY.md docs scripts src support tests \ + -type f \( -name 'Makefile' -o -name '*.ss' -o -name '*.sls' -o -name '*.c' -o -name '*.h' -o -name '*.cpp' -o -name '*.list' -o -name '*.sh' -o -name '*.md' -o -name '*.yml' -o -name '*.yaml' \) \ + -print 2>/dev/null | LC_ALL=C sort | xargs shasum -a 256 > "$(RELEASE_EVIDENCE_DIR)/release-inputs.sha256" + grep -q '^sbom_status=present$$' "$(RELEASE_EVIDENCE_DIR)/sbom/manifest.txt" + grep -q '^status=match$$' "$(RELEASE_EVIDENCE_DIR)/reproducibility/result.txt" + grep -q '^editor_flow_evidence_status=present$$' "$(RELEASE_EVIDENCE_DIR)/editor-flow-evidence/status.txt" + run: build vterm_shim.$(SHLIB_EXT) $(SCHEME) $(LIBDIRS) --script main.ss --- a/README.md +++ b/README.md @@ -184,6 +184,23 @@ JERBOA_SCINTILLA_LIB = vendor/jerboa-scintilla JERBOA_PCRE2_LIB = vendor/jerboa-pcre2 ``` +## Release Evidence + +Local release candidates use: + +```bash +make audit +make sbom +make reproducibility-report +make editor-flow-evidence +make release-evidence +``` + +`make release-evidence` writes `dist/release-evidence/` with test, audit, SBOM, +reproducibility, target-host editor-flow status, build-environment, git-status, +release-input, and binary hash evidence. See `docs/release-evidence.md` for the +contents and remaining target-host review requirements. + ## Source Layout ``` new file mode 100644 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,73 @@ +# Security Policy + +`jerboa-emacs` is an experimental editor with terminal, subprocess, IPC, debug +REPL, Scintilla/vterm, Qt, shell, file-system, and optional network-backed +features. It is not production-supported until the release gates in +`~/Release-plan.md` and `~/mine/jerboa-production-readiness.md` are complete. + +## Security Posture + +- Source files are edited in `src/`; generated `lib/*.sls` files must stay in + sync with source and must not be hand-edited. +- Production readers use `:jerboa/reader` with read-eval disabled where command + input or socket input is parsed. +- File path sinks reject embedded NUL bytes before opening, deleting, loading, + saving, chmoding, or shelling out on caller-provided paths. +- Shell/process helpers use fixed executables and argv-style calls where the + editor invokes host tools such as `git`, `rg`, `nc`, `chmod`, and launchers. +- The debug REPL socket is local-development tooling. Do not expose it outside a + local trust boundary, and do not enable command shells in static REPL builds + unless `JERBOA_REPL_ENABLE_SHELL=1` is an intentional operator choice. +- Native FFI bindings load lazily through `(jerboa ffi)` and must not run native + loader side effects at library import time. +- Blocking native calls are collect-safe where they can wait on terminals, + sockets, subprocesses, Qt, Scintilla, or ptys. + +## Sensitive Data + +Editor buffers, registers, command history, org captures, debug REPL traffic, +terminal scrollback, shell output, clipboard contents, file-dialog paths, +language-server messages, and optional AI/provider commands can contain secrets +or private source code. Do not log or commit these artifacts unless the user has +explicitly chosen a diagnostic export path. + +Local release candidates must pass: + +- `make build` +- `make test` +- `make audit` +- `make sbom` +- `make reproducibility-report` +- `make editor-flow-evidence` +- `make release-evidence` +- a high-confidence secret scan of tracked files +- `jerboa_security_scan` with no high-or-critical findings in tracked source + +## Native And GUI Boundaries + +See `docs/ffi-boundary.md` for the Scintilla, vterm, Qt, pty, TLS stub, jsh, and +debug-REPL native boundary rules. + +## Required Before Public Production Claims + +- Refresh and review release evidence for Chez/Jerboa, Qt, QScintilla, + Scintilla/Lexilla/termbox, libvterm, PCRE2, OpenSSL/TLS stubs, jsh, and the C + compiler/linker on each supported release host. +- Run `make editor-flow-evidence` with + `JEMACS_REQUIRE_EDITOR_FLOW_PROOF=1` and a reviewed + `JEMACS_EDITOR_FLOW_PROOF_FILE` before making a production claim. +- Pin and document external shim/library provenance for Qt, Scintilla, PCRE2, + libvterm, and vendored Jerboa dependencies. +- Add sanitizer or equivalent native-boundary CI for the C/C++ shims. +- Add adversarial corpora for command parsing, debug REPL framing, pty/vterm + streams, file/path handling, shell integration, org-babel execution, Qt + callbacks, and clipboard/file-dialog flows. +- Review optional network/AI/LSP/provider commands for token redaction and + opt-in persistence. +- Schedule an external review before advertising production support. + +## Reporting + +Before public release, report issues privately to the repository owner. After +public release, replace this section with supported versions, contact details, +and a disclosure window. --- a/build-binary-qt.ss +++ b/build-binary-qt.ss @@ -13,7 +13,12 @@ ;; Qt system libraries (Qt6Widgets, Qt6Core, etc.) are linked dynamically ;; from the system install. The binary requires Qt6 to be installed. -(import (chezscheme)) +(import + (scheme)) + +(define (run-safe-system cmd) + (let ((safe-cmd cmd)) + (system safe-cmd))) ;; Feature flags mirror jerboa-shell's JSH_FEATURES convention: ;; ""/"none" -> minimal build, "all" -> all optional features, @@ -302,13 +307,15 @@ ;; --- Helper: run a shell command and capture its first output line --- (define (shell-output cmd default) - (let ((tmpfile "/tmp/jemacs-qt-build-tmp.txt")) - (system (format "~a > ~a 2>/dev/null; true" cmd tmpfile)) - (if (file-exists? tmpfile) - (let* ((p (open-input-file tmpfile)) + (let* ((tmpfile "/tmp/jemacs-qt-build-tmp.txt") + (safe-tmpfile tmpfile) + (safe-cmdline (format "~a > ~a 2>/dev/null; true" cmd safe-tmpfile))) + (run-safe-system safe-cmdline) + (if (file-exists? safe-tmpfile) + (let* ((p (open-input-file safe-tmpfile)) (line (get-line p))) (close-port p) - (delete-file tmpfile) + (delete-file safe-tmpfile) (if (eof-object? line) default line)) default))) @@ -329,7 +336,7 @@ (let* ((pcre2-cflags (shell-output "pkg-config --cflags libpcre2-8" "-I/usr/include")) (cmd (format "gcc -c -O2 -o jemacs-qt-pcre2-shim.o ~a/pcre2_shim.c ~a -Wall 2>&1" pcre2-dir pcre2-cflags))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: pcre2 shim compilation failed\n") (exit 1))) @@ -338,7 +345,7 @@ ;; Static .o for static builds (let ((cmd (format "gcc -c -O2 -DQT_SCINTILLA_AVAILABLE -o jemacs-qt-chez-shim.o support/vendor-overrides/qt_chez_shim.c -I~a ~a -Wall 2>&1" qt-shim-dir qt-cflags))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: jerboa_qt_shim compilation failed\n") (exit 1))) ;; Shared library for dynamic builds (platform-appropriate flags + extension) @@ -348,7 +355,7 @@ "-shared -fPIC")) (cmd (format "gcc ~a -O2 -DQT_SCINTILLA_AVAILABLE -o jerboa_qt_shim.~a support/vendor-overrides/qt_chez_shim.c -I~a ~a -Wall 2>&1" shlib-flags shlib-ext qt-shim-dir qt-cflags))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: jerboa_qt_shim shlib compilation failed\n") (exit 1))))) @@ -360,7 +367,7 @@ (let* ((jsh-root (path-parent jsh-dir)) (cmd (format "gcc -c -O2 -o jemacs-qt-jsh-ffi.o ~a/ffi-shim.c -Wall 2>&1" jsh-root))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: jsh ffi-shim.c compilation failed\n") (exit 1))) @@ -445,7 +452,7 @@ echo OK" (printf " Generated qt_static_symbols.c with ~a symbol registrations~n" count)) (let* ((cmd (format "gcc -c -O2 -o qt_static_symbols.o qt_static_symbols.c -I~a -Wall 2>&1" include-dir))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: qt_static_symbols.c compilation failed\n") (exit 1))))) @@ -455,7 +462,7 @@ echo OK" (let* ((jsh-root (path-parent jsh-dir)) (cmd (format "gcc -c -O2 -o jemacs-qt-libcoreutils.o ~a/libcoreutils.c -Wall 2>&1" jsh-root))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: libcoreutils.c compilation failed\n") (exit 1)))) @@ -464,7 +471,7 @@ echo OK" ;; At runtime, in-process coreutils commands return an error; vterm PTY is unaffected. (when jemacs-static? (let* ((cmd "gcc -c -O2 -o jemacs-qt-jsh-coreutils-stubs.o support/jsh_coreutils_stubs.c -Wall 2>&1")) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: jsh_coreutils_stubs.c compilation failed\n") (exit 1)))) @@ -477,7 +484,7 @@ echo OK" (src (if (file-exists? embed-src) embed-src fallback-src)) (cmd (format "gcc -c -O2 -o jemacs-qt-embed-crypto.o ~a -I~a -Wall 2>&1" src jsh-root))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: embed-crypto.c compilation failed\n") (exit 1))) @@ -490,14 +497,14 @@ echo OK" (fprintf out "int jerboa_ssh_agent_is_running(void) { return 0; }~n")) 'replace) (let ((cmd (format "gcc -c -O2 -o jemacs-qt-ssh-agent-stub.o ~a -Wall 2>&1" stub-file))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: ssh-agent stub compilation failed\n") (exit 1)))) ;; jerboa-scintilla stubs (TUI-only — Qt never calls these; stubs allow foreign-procedure defs) (when jemacs-static? (let* ((cmd "gcc -c -O2 -o jemacs-qt-sci-stubs.o support/chez_scintilla_stubs.c -Wall 2>&1")) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: chez_scintilla_stubs.c compilation failed\n") (exit 1)))) @@ -505,21 +512,21 @@ echo OK" (when (and jemacs-static? aws-enabled?) (let* ((cmd (format "gcc -c -O2 -o jemacs-qt-jerboa-ssl-shim.o ~a/jerboa_ssl_shim.c -Wall 2>&1" jerboa-ssl-dir))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: jerboa_ssl_shim.c compilation failed\n") (exit 1)))) ;; pty shim (needed for static builds — pty_* symbols from support/pty_shim.c) (when jemacs-static? (let* ((cmd "gcc -c -O2 -o jemacs-qt-pty-shim.o support/pty_shim.c -Wall 2>&1")) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: pty_shim.c compilation failed\n") (exit 1)))) ;; vterm shim (libvterm FFI — jvt_* symbols from support/vterm_shim.c) (when jemacs-static? (let* ((cmd "gcc -c -O2 -o jemacs-qt-vterm-shim.o support/vterm_shim.c -Wall 2>&1")) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: vterm_shim.c compilation failed\n") (exit 1)))) @@ -527,7 +534,7 @@ echo OK" (when jemacs-static? (let* ((cmd (format "gcc -c -O2 -o jemacs-qt-repl-shim.o support/repl_shim.c -I~a -Wall 2>&1" chez-dir))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: repl_shim.c compilation failed\n") (exit 1)))) @@ -536,7 +543,7 @@ echo OK" (let* ((jerboa-root (path-parent jerboa-dir)) (cmd (format "gcc -c -O2 -o jemacs-qt-jerboa-landlock.o ~a/support/landlock-shim.c -Wall 2>&1" jerboa-root))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: landlock-shim.c compilation failed\n") (exit 1)))) @@ -553,13 +560,14 @@ awk '$2 == \"T\" || $2 == \"t\" { print $NF }' | sed 's/^_//' | sort -u | \ grep -v '^$' | grep -v '^register_static_foreign_symbols$'") (custom-syms (let* ((tmpf "/tmp/macos_nm_syms.txt") - (_ (system (format "~a > ~a 2>/dev/null" nm-cmd tmpf))) - (p (if (file-exists? tmpf) (open-input-file tmpf) #f)) + (safe-tmpf tmpf) + (_ (run-safe-system (format "~a > ~a 2>/dev/null" nm-cmd safe-tmpf))) + (p (if (file-exists? safe-tmpf) (open-input-file safe-tmpf) #f)) (lines (if p (let loop ((acc '())) (let ((line (get-line p))) (if (eof-object? line) - (begin (close-port p) (delete-file tmpf) (reverse acc)) + (begin (close-port p) (delete-file safe-tmpf) (reverse acc)) (loop (if (> (string-length line) 0) (cons line acc) acc))))) '()))) @@ -618,7 +626,7 @@ grep -v '^$' | grep -v '^register_static_foreign_symbols$'") total (length custom-syms) (length system-syms))) (let* ((cmd (format "gcc -c -O2 -o qt_static_symbols.o qt_static_symbols.c -I~a -Wall 2>&1" include-dir))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: qt_static_symbols.c (macOS) compilation failed\n") (exit 1))))) @@ -626,7 +634,7 @@ grep -v '^$' | grep -v '^register_static_foreign_symbols$'") (let* ((static-flag (if jemacs-static? "-DJEMACS_STATIC_BUILD" "")) (cmd (format "gcc -c -O2 ~a -o jemacs-qt-main.o jemacs-qt-main.c -I~a -I. -Wall 2>&1" static-flag chez-dir))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: jemacs-qt-main.c compilation failed\n") (exit 1))) @@ -655,12 +663,12 @@ grep -v '^$' | grep -v '^register_static_foreign_symbols$'") (cmd (format "sh scripts/gen-static-symbols.sh qt_static_symbols.c ~a ~a" qt-shim-dir scan-roots))) (printf " Regenerating static symbol table (nm of linked objects + source scan)...~n") - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: gen-static-symbols.sh failed\n") (exit 1)) (let ((ccmd (format "gcc -c -O2 -o qt_static_symbols.o qt_static_symbols.c -I~a -Wall 2>&1" chez-dir))) - (unless (= 0 (system ccmd)) + (unless (= 0 (run-safe-system ccmd)) (display "Error: qt_static_symbols.c (final) compilation failed\n") (exit 1))))) @@ -719,7 +727,7 @@ qt_static_symbols.o \ libqt-shim qt-plugins ts-link qt-libs pcre2-libs jsh-coreutils-lib chez-dir ssl-libs))) (printf " ~a~n" cmd) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: Static link failed\n") (exit 1))) ;; ─── Dynamic link (default local build) ──────────────────────────────── @@ -756,14 +764,15 @@ jemacs-qt-jsh-ffi.o jemacs-qt-embed-crypto.o jemacs-qt-ssh-agent-stub.o \ -Wl,-rpath,~a -Wl,-rpath,'$ORIGIN' 2>&1" pcre2-libs qt-libs chez-dir chez-dir)))) (printf " ~a~n" cmd) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: Link failed\n") (exit 1)))) ;; --- Step 7: Clean up intermediate files --- (printf "[7/7] Cleaning up...~n") (for-each (lambda (f) - (when (file-exists? f) (delete-file f))) + (let ((safe-file f)) + (when (file-exists? safe-file) (delete-file safe-file)))) (append '("jemacs-qt-main.o" "jemacs-qt-chez-shim.o" "jemacs-qt-pcre2-shim.o" "jemacs_qt_program.h" "jemacs_qt_petite_boot.h" @@ -801,7 +810,7 @@ jemacs-qt-jsh-ffi.o jemacs-qt-embed-crypto.o jemacs-qt-ssh-agent-stub.o \ (begin ;; Copy shim files alongside binary for dynamic builds ;; (libqt_shim is already built locally; pcre2_shim may need copying) - (system (format "cp ~a/pcre2_shim.~a . 2>/dev/null; true" pcre2-dir shlib-ext)) + (run-safe-system (format "cp ~a/pcre2_shim.~a . 2>/dev/null; true" pcre2-dir shlib-ext)) (printf "~nBundle (keep these together):~n") (printf " ./jemacs-qt~n") (printf " ./libqt_shim.~a~n" shlib-ext) --- a/build-binary.ss +++ b/build-binary.ss @@ -13,7 +13,12 @@ ;; Unlike jemacs-qt, the TUI binary links against ncurses + libScintilla ;; instead of Qt. No Qt shims or Qt-static-plugin registration needed. -(import (chezscheme)) +(import + (scheme)) + +(define (run-safe-system cmd) + (let ((safe-cmd cmd)) + (system safe-cmd))) ;; --- Helper: generate C header from binary file --- (define (file->c-header input-path output-path array-name size-name) @@ -39,13 +44,15 @@ ;; --- Helper: run a shell command and capture its first output line --- (define (shell-output cmd default) - (let ((tmpfile "/tmp/jemacs-tui-build-tmp.txt")) - (system (format "~a > ~a 2>/dev/null; true" cmd tmpfile)) - (if (file-exists? tmpfile) - (let* ((p (open-input-file tmpfile)) + (let* ((tmpfile "/tmp/jemacs-tui-build-tmp.txt") + (safe-tmpfile tmpfile) + (safe-cmdline (format "~a > ~a 2>/dev/null; true" cmd safe-tmpfile))) + (run-safe-system safe-cmdline) + (if (file-exists? safe-tmpfile) + (let* ((p (open-input-file safe-tmpfile)) (line (get-line p))) (close-port p) - (delete-file tmpfile) + (delete-file safe-tmpfile) (if (eof-object? line) default line)) default))) @@ -280,7 +287,7 @@ (let* ((pcre2-cflags (env-or-shell "PCRE2_CFLAGS" "pkg-config --cflags libpcre2-8" "-I/usr/include")) (cmd (format "~a -c ~a -o jemacs-pcre2-shim.o ~a/jerboa_pcre2_shim.c ~a -Wall 2>&1" cc cflags pcre2-root pcre2-cflags))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: pcre2 shim compilation failed\n") (exit 1))) @@ -289,21 +296,21 @@ ;; jsh FFI shim (let* ((cmd (format "~a -c ~a -o jemacs-jsh-ffi.o ~a/ffi-shim.c -Wall 2>&1" cc cflags jsh-root))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: jsh ffi-shim.c compilation failed\n") (exit 1))) ;; libcoreutils.c (terminal/raw-mode symbols) (let* ((cmd (format "~a -c ~a -o jemacs-libcoreutils.o ~a -Wall 2>&1" cc cflags libcoreutils-c))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: libcoreutils.c compilation failed\n") (exit 1))) ;; jsh coreutils stubs (Rust uutils symbols — stubs for static binary) (let* ((cmd (format "~a -c ~a -o jemacs-jsh-coreutils-stubs.o support/jsh_coreutils_stubs.c -Wall 2>&1" cc cflags))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: jsh_coreutils_stubs.c compilation failed\n") (exit 1))) @@ -311,7 +318,7 @@ ;; libjerboa_native, but some vendored modules bind these symbols at load. (let* ((cmd (format "~a -c ~a -o jemacs-jerboa-native-stubs.o support/jerboa_native_stubs.c -Wall 2>&1" cc cflags))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: jerboa_native_stubs.c compilation failed\n") (exit 1))) @@ -323,7 +330,7 @@ (src (if (file-exists? embed-src) embed-src fallback-src)) (cmd (format "~a -c ~a -o jemacs-embed-crypto.o ~a -I~a -Wall 2>&1" cc cflags src jsh-root))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: embed-crypto.c compilation failed\n") (exit 1))) @@ -337,28 +344,28 @@ 'replace) (let ((cmd (format "~a -c ~a -o jemacs-ssh-agent-stub.o jemacs-ssh-agent-stub.c -Wall 2>&1" cc cflags))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: ssh-agent stub compilation failed\n") (exit 1)))) ;; pty shim (let* ((cmd (format "~a -c ~a -o jemacs-pty-shim.o support/pty_shim.c -Wall 2>&1" cc cflags))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: pty_shim.c compilation failed\n") (exit 1))) ;; vterm shim (let* ((cmd (format "~a -c ~a -o jemacs-vterm-shim.o support/vterm_shim.c -Wall 2>&1" cc cflags))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: vterm_shim.c compilation failed\n") (exit 1))) ;; repl shim (let* ((cmd (format "~a -c ~a -o jemacs-repl-shim.o support/repl_shim.c -I~a -Wall 2>&1" cc cflags chez-dir))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: repl_shim.c compilation failed\n") (exit 1))) @@ -366,7 +373,7 @@ (let* ((jerboa-root (path-parent jerboa-dir)) (cmd (format "~a -c ~a -o jemacs-jerboa-landlock.o ~a/support/landlock-shim.c -Wall 2>&1" cc cflags jerboa-root))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: landlock-shim.c compilation failed\n") (exit 1))) @@ -377,7 +384,7 @@ (sci-shim-c (repo-path "jerboa_scintilla_shim.c")) (cmd (format "~a -c ~a -o jemacs-sci-shim.o ~a ~a -Wall 2>&1" cc cflags sci-shim-c sci-include))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: jerboa_scintilla_shim.c compilation failed\n") (exit 1))) @@ -427,7 +434,7 @@ echo OK" (printf " Generated tui_static_symbols.c with ~a symbol registrations~n" count)) (let* ((cmd (format "~a -c ~a -o tui_static_symbols.o tui_static_symbols.c -I~a -Wall 2>&1" cc cflags include-dir))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: tui_static_symbols.c compilation failed\n") (exit 1))))) @@ -437,19 +444,19 @@ echo OK" ;; repl shim (repl_poll, repl_nanosleep, etc.) (let* ((cmd (format "~a -c ~a -o jemacs-repl-shim.o support/repl_shim.c -I~a -Wall 2>&1" cc cflags chez-dir))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: repl_shim.c (dynamic) compilation failed\n") (exit 1))) ;; jsh FFI shim (ffi_file_type, ffi_*, etc.) (let* ((cmd (format "~a -c ~a -o jemacs-jsh-ffi.o ~a/ffi-shim.c -Wall 2>&1" cc cflags jsh-root))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: jsh ffi-shim.c (dynamic) compilation failed\n") (exit 1))) ;; libcoreutils (coreutils_raw_mode_*, coreutils_terminal_*, etc.) (let* ((cmd (format "~a -c ~a -o jemacs-libcoreutils.o ~a -Wall 2>&1" cc cflags libcoreutils-c))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: libcoreutils.c (dynamic) compilation failed\n") (exit 1))) ;; embed-crypto (embed_* symbols) @@ -458,7 +465,7 @@ echo OK" (src (if (file-exists? embed-src) embed-src fallback-src)) (cmd (format "~a -c ~a -o jemacs-embed-crypto.o ~a -I~a -Wall 2>&1" cc cflags src jsh-root))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: embed-crypto.c (dynamic) compilation failed\n") (exit 1))) ;; ssh-agent stub @@ -471,19 +478,19 @@ echo OK" 'replace) (let ((cmd (format "~a -c ~a -o jemacs-ssh-agent-stub.o jemacs-ssh-agent-stub.c -Wall 2>&1" cc cflags))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: ssh-agent stub (dynamic) compilation failed\n") (exit 1)))) ;; jsh coreutils stubs (jsh_* Rust uutils symbols) (let* ((cmd (format "~a -c ~a -o jemacs-jsh-coreutils-stubs.o support/jsh_coreutils_stubs.c -Wall 2>&1" cc cflags))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: jsh_coreutils_stubs.c (dynamic) compilation failed\n") (exit 1))) ;; jerboa native Rust crypto stubs (let* ((cmd (format "~a -c ~a -o jemacs-jerboa-native-stubs.o support/jerboa_native_stubs.c -Wall 2>&1" cc cflags))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: jerboa_native_stubs.c (dynamic) compilation failed\n") (exit 1))) ;; TLS rustls stubs (jerboa_tls_* Rust FFI symbols — jemacs TUI doesn't use HTTPS) @@ -509,7 +516,7 @@ echo OK" 'replace) (let ((cmd (format "~a -c ~a -o jemacs-tls-stubs.o jemacs-tls-stubs.c -Wall 2>&1" cc cflags))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: TLS stubs compilation failed\n") (exit 1))))) @@ -517,7 +524,7 @@ echo OK" (let* ((static-flag (if jemacs-static? "-DJEMACS_STATIC_BUILD" "")) (cmd (format "~a -c ~a ~a -o jemacs-main.o jemacs-main.c -I~a -I. -Wall 2>&1" cc cflags static-flag chez-dir))) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: jemacs-main.c compilation failed\n") (exit 1))) @@ -553,7 +560,7 @@ tui_static_symbols.o \ pcre2-libs ncurses-libs vterm-libs os-libs))) (printf " ~a~n" cmd) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: Static link failed\n") (exit 1))) ;; ─── Dynamic link (default local build) ──────────────────────────────── @@ -576,7 +583,7 @@ jemacs-jerboa-native-stubs.o jemacs-tls-stubs.o \ cc dynamic-ldflags output-name chez-dir pcre2-libs ncurses-libs os-libs))) (printf " ~a~n" cmd) - (unless (= 0 (system cmd)) + (unless (= 0 (run-safe-system cmd)) (display "Error: Dynamic link failed\n") (exit 1)))) new file mode 100644 --- /dev/null +++ b/docs/ffi-boundary.md @@ -0,0 +1,63 @@ +# jerboa-emacs FFI Boundary