Update Jerboa runtime and guidance state
ober
fe5bef605aa7bba5957687d521461982e233a7cb
--- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,6 +42,14 @@ jobs: ${{ env.IMAGE }}:ci \ make build SCHEME=scheme + - name: Run production security audit + run: | + docker run --rm \ + -v "${{ github.workspace }}:/workspace" -w /workspace \ + -e JERBOA=/workspace/lib -e JERBOA_HOME=/workspace \ + ${{ env.IMAGE }}:ci \ + make audit SCHEME=scheme + - name: Run feature tests (informational) continue-on-error: true timeout-minutes: 10 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/.jerboa-system +++ b/.jerboa-system @@ -104,12 +104,14 @@ (tcb ("Chez Scheme" "Jerboa stdlib" "jerboa-native-rs" "support/multicall-main.c" "support/jerboa-embed.c" "support/jerboa-embed.h" + "support/hardening-report.h" "support/landlock-shim.c"))) (native-boundary (rust ("jerboa-native-rs")) (c-shims ("support/multicall-main.c" "support/jerboa-embed.c" - "support/jerboa-embed.h" "support/landlock-shim.c")) + "support/jerboa-embed.h" "support/hardening-report.h" + "support/landlock-shim.c")) (ffi-wrapper-policy explicit-safe-wrapper) (fuzz planned)) --- a/Dockerfile +++ b/Dockerfile @@ -54,7 +54,8 @@ RUN printf '#!/bin/sh\nexec /usr/bin/g++ -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 " RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ sh -s -- -y --default-toolchain stable --profile minimal && \ . /root/.cargo/env && \ - rustup target add x86_64-unknown-linux-musl + rustup target add x86_64-unknown-linux-musl && \ + cargo install cargo-audit --locked ENV PATH="/root/.cargo/bin:${PATH}" ENV RUSTUP_HOME="/root/.rustup" 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 @@ -57,7 +57,7 @@ TYPED_LLVMIR_PARITY_FN ?= sample_typed_llvmir_smoke::main LLVM_BIN ?= $(shell if command -v llvm-as >/dev/null 2>&1; then dirname "$$(command -v llvm-as)"; elif [ -x /opt/homebrew/opt/llvm/bin/llvm-as ]; then echo /opt/homebrew/opt/llvm/bin; elif [ -x /usr/local/opt/llvm/bin/llvm-as ]; then echo /usr/local/opt/llvm/bin; fi) TYPED_WRAPPER_DIR ?= build/typed/jerboa -.PHONY: help chez chez-cross build binary binary-typed binary-typed-smoke binary-cross native-cross pure-audit typecheck typed-rust typed-llvmir typed-llvmir-check typed-llvmir-smoke typed-llvmir-parity typed-wrappers typed-build typed-wrapper-smoke typed-split-tree-smoke typed-test typed-clean test test-known-flaky test-reader test-core test-runtime test-try-debug test-stdlib test-ffi test-modules test-expanded test-contract test-ergo test-sqlite-robustness test-limits-primitives test-typed-core test-typed-parser test-typed-checker test-typed-rust test-typed-llvmir test-typed-wrappers test-pure-audit test-features test-wrappers test-phase4a test-phase4b test-phase4c test-phase4d test-phase4e test-phase4f test-phase5 test-phase5e test-phase6 test-phase7 test-phase8 test-functional test-repl test-security test-security-profile test-native test-gaps native clean-native audit-native clean security security-production security-profile security-hardware-smoke fuzz fuzz-smoke fuzz-deep fuzz-reader-fuzz fuzz-json-fuzz fuzz-http2-fuzz fuzz-dns-fuzz fuzz-pregexp-fuzz fuzz-csv-fuzz fuzz-base64-fuzz fuzz-hex-fuzz fuzz-uri-fuzz fuzz-format-fuzz fuzz-router-fuzz fuzz-sandbox-fuzz test-rawstring test-regex test-rx test-peg test-regex-all check-docs check-docs-strict data-check docker-build docker-push +.PHONY: help chez chez-cross build binary binary-typed binary-typed-smoke binary-cross native-cross pure-audit typecheck typed-rust typed-llvmir typed-llvmir-check typed-llvmir-smoke typed-llvmir-parity typed-wrappers typed-build typed-wrapper-smoke typed-split-tree-smoke typed-test typed-clean test test-known-flaky test-reader test-core test-runtime test-try-debug test-stdlib test-ffi test-modules test-expanded test-contract test-ergo test-sqlite-robustness test-limits-primitives test-typed-core test-typed-parser test-typed-checker test-typed-rust test-typed-llvmir test-typed-wrappers test-pure-audit test-features test-wrappers test-phase4a test-phase4b test-phase4c test-phase4d test-phase4e test-phase4f test-phase5 test-phase5e test-phase6 test-phase7 test-phase8 test-functional test-repl test-security test-security-profile test-native test-gaps native clean-native audit audit-native security-audit clean security security-production security-profile security-hardware-smoke fuzz fuzz-smoke fuzz-deep fuzz-reader-fuzz fuzz-json-fuzz fuzz-http2-fuzz fuzz-dns-fuzz fuzz-pregexp-fuzz fuzz-csv-fuzz fuzz-base64-fuzz fuzz-hex-fuzz fuzz-uri-fuzz fuzz-format-fuzz fuzz-router-fuzz fuzz-sandbox-fuzz test-rawstring test-regex test-rx test-peg test-regex-all check-docs check-docs-strict data-check docker-build docker-push .PHONY: check-cross-tools docker fuzz-websocket-fuzz jlsp jlsp-freebsd-amd64 \ jlsp-install jlsp-linux-amd64 jlsp-portable jmcp-freebsd-amd64 \ jmcp-freebsd-arm64 jmcp-linux-amd64 jmcp-linux-arm64 \ @@ -113,6 +113,7 @@ help: @echo " native Build Rust native library" @echo " clean Remove compiled .so and .wpo artifacts" @echo " clean-native Remove Rust build artifacts" + @echo " audit Run production security/release gate checks" @echo " audit-native Run cargo audit on Rust native library" @echo " pure-audit Scan jerboa-* repos for non-pure runtime surfaces" @echo "" @@ -281,7 +282,7 @@ jerbuild-smoke: jerbuild # dist/jerbuild <src> <lib> # transpile; dist/jerbuild binary E.ss OUT .PHONY: jerboa jerboa-smoke jerboa: chez build mcp-check lsp-gen - $(SCHEME) --script support/build-jerboa-multicall.ss + $(SCHEME) --libdirs $(LIBDIRS) --script support/build-jerboa-multicall.ss .PHONY: system-check system-check: @@ -700,7 +701,7 @@ mcp-check: @$(MAKE) data-check data-check: - @$(SCHEME) --script support/data-check.ss data + @$(SCHEME) --libdirs $(LIBDIRS) --script support/data-check.ss data mcp: chez mcp-check @JERBOA_HOME=$(JERBOA_HOME) JERBOA_MCP_SCHEME_PATH=$(SCHEME) \ @@ -814,7 +815,7 @@ api-signatures: chez # Regenerate the completion tables from the live API map (data/api-signatures.sexp). lsp-gen: chez - @JERBOA_HOME=$(JERBOA_HOME) $(SCHEME) --script $(LSP_GEN) + @JERBOA_HOME=$(JERBOA_HOME) $(SCHEME) --libdirs $(LIBDIRS) --script $(LSP_GEN) # Compile-check the whole server from source (imports the full cascade). lsp: chez lsp-gen @@ -844,7 +845,7 @@ lsp-test-e2e: chez lsp-gen jlsp: chez build lsp-gen @echo "=== Building native ./jlsp (host) ===" @JERBOA_HOME=$(JERBOA_HOME) JERBOA_LSP_DIR=$(JERBOA_HOME) \ - $(SCHEME) -q --libdirs $(JERBOA_HOME) --script lsp/build-binary.ss + $(SCHEME) -q --libdirs '$(JERBOA_HOME):$(LIBDIRS)' --script lsp/build-binary.ss jlsp-install: jlsp @mkdir -p $(HOME)/.local/bin @@ -1508,7 +1509,21 @@ test-native: native @$(NATIVE_LD_VAR)=lib $(SCHEME) --libdirs $(LIBDIRS) --script tests/test-native-rust-week5-6.ss audit-native: - cd $(RUST_NATIVE_DIR) && cargo audit + @set -eu; \ + if command -v cargo-audit >/dev/null 2>&1; then \ + cargo_audit=cargo-audit; \ + elif [ -x "$$HOME/.cargo/bin/cargo-audit" ]; then \ + cargo_audit="$$HOME/.cargo/bin/cargo-audit"; \ + else \ + echo "cargo-audit is required for Rust advisory checks" >&2; \ + exit 1; \ + fi; \ + cd $(RUST_NATIVE_DIR) && "$$cargo_audit" audit -D warnings + +security-audit: + @tools/security-audit.sh + +audit: security-audit audit-native data-check security-production unification-check test-gaps: $(SCHEME) --libdirs $(LIBDIRS) --script tests/test-gaps.ss --- a/README.md +++ b/README.md @@ -211,6 +211,7 @@ Common local targets: ```bash make build # build core Jerboa libraries make binary # native binary build for macOS/FreeBSD/other local hosts +make audit # production security and release-readiness gate make test # core test suite make test-features # feature-phase tests make test-native # Rust native backend tests @@ -275,6 +276,8 @@ Start with these docs: [`docs/core-async.md`](docs/core-async.md) for concurrency. - [`docs/security-reference.md`](docs/security-reference.md) and [`docs/safety-guide.md`](docs/safety-guide.md) for hardening. +- [`docs/release-security.md`](docs/release-security.md) for production + release gates and threat model. - [`docs/native-rust.md`](docs/native-rust.md), [`docs/ffi.md`](docs/ffi.md), [`docs/wasm.md`](docs/wasm.md), and [`docs/slang.md`](docs/slang.md) for native and WASM integration. new file mode 100644 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,48 @@ +# Security Policy + +This repository is the core Jerboa runtime, standard library, build system, +MCP server, LSP server, package tooling, native Rust backend, and multicall +binary source. Treat it as security-sensitive even when a change looks like +library or developer tooling work. + +Production claims require the gates in [`docs/release-security.md`](docs/release-security.md) +and the cross-repository tracker at `~/mine/jerboa-production-readiness.md`. + +## Supported Status + +No public production-support commitment exists yet. A production release must be +cut from a clean checkout after: + +- `make audit`, `make build`, and `make test` pass. +- Release artifacts additionally pass `make jerboa`, + `make unification-release-check`, `make jerboa-smoke`, and + `make security-hardware-smoke`. +- RustSec, secret scanning, `.jerboa-system` manifest checks, generated + artifact policy, native boundary checks, and security profile tests are clean. +- FFI, filesystem, network, shell, generated-artifact, and credential-handling + behavior is documented or explicitly accepted as release risk. + +## Security Expectations + +- Do not commit secrets, private keys, tokens, production `.env` files, + operational hostnames, user/customer data, or private infrastructure details. +- Keep vendored Chez license and notice files with source and binary releases: + `LICENSE`, `LICENSE-CHEZ`, `NOTICE`, `vendor/ChezScheme/LICENSE`, and + `vendor/ChezScheme/NOTICE`. +- Use the guarded Jerboa reader for untrusted S-expressions and tool input. +- Use `safe-*` taint wrappers around dangerous file, shell, and delete sinks + when values are not compile-time constants. +- Keep blocking FFI calls collect-safe, retry interrupted POSIX I/O, and declare + native Rust/C/FFI boundaries in `.jerboa-system`. +- Prefer Rust for byte-level parsing, crypto-adjacent integration, TLS, and + sandbox setup. Scheme should hold policy, orchestration, tests, and high-level + behavior. +- Daemon/service exposure and hostile parsers require threat models, fuzz + harnesses, regression corpora, resource limits, and sandboxing notes before + production use. + +## Reporting + +Before public release, report issues privately to the repository owner. After +public release, replace this section with a dedicated advisory contact, +supported versions, and disclosure window. --- a/benchmarks/bench-ac-alloc.ss +++ b/benchmarks/bench-ac-alloc.ss @@ -24,7 +24,7 @@ (optimize-level 3) (compile-imported-libraries #t) -(import (chezscheme) +(import (scheme) (std text aho-corasick) (std runtime gc)) --- a/benchmarks/bench-core.ss +++ b/benchmarks/bench-core.ss @@ -5,7 +5,7 @@ ;;; ;;; Run: bin/jerboa run benchmarks/bench-core.ss -(import (except (chezscheme) +(import (except (scheme) make-hash-table hash-table? sort sort! format printf fprintf iota 1+ 1- --- a/benchmarks/bench-defstruct.ss +++ b/benchmarks/bench-defstruct.ss @@ -7,7 +7,7 @@ ;;; inlining the accessor, defstruct-acc will be measurably slower ;;; than the raw DRT path under unsafe mode (o=3). -(import (except (chezscheme) +(import (except (scheme) make-hash-table hash-table? sort sort! format printf fprintf iota 1+ 1- path-extension path-absolute? with-input-from-string with-output-to-string) --- a/benchmarks/bench-for-or-and.ss +++ b/benchmarks/bench-for-or-and.ss @@ -3,7 +3,7 @@ ;;; introduced in Phase 19. The fused path avoids the intermediate ;;; list produced by the generic multi-clause machinery. -(import (except (chezscheme) +(import (except (scheme) make-hash-table hash-table? sort sort! format printf fprintf iota 1+ 1- path-extension path-absolute? with-input-from-string with-output-to-string) --- a/benchmarks/bench-gc-tuning.ss +++ b/benchmarks/bench-gc-tuning.ss @@ -20,7 +20,7 @@ (guard (_ [#t #f]) (load-shared-object "libSystem.dylib") #t) (guard (_ [#t #f]) (load-shared-object "libSystem.B.dylib") #t)) -(import (chezscheme) +(import (scheme) (std runtime gc)) ;; ---- Synthetic scanner workload ---- --- a/benchmarks/bench-kwargs.ss +++ b/benchmarks/bench-kwargs.ss @@ -3,7 +3,7 @@ ;;; vs equivalent positional-argument calls, after Phase 6's single-pass ;;; kwarg extractor (jerboa 19e5a5b). -(import (except (chezscheme) +(import (except (scheme) make-hash-table hash-table? sort sort! format printf fprintf iota 1+ 1- path-extension path-absolute? with-input-from-string with-output-to-string) --- a/benchmarks/bench-method-dispatch.ss +++ b/benchmarks/bench-method-dispatch.ss @@ -6,7 +6,7 @@ ;;; rtd -> method) plus the record-rtd call. A per-callsite PIC ;;; would bypass both hashtable lookups on a cache hit. -(import (except (chezscheme) +(import (except (scheme) make-hash-table hash-table? sort sort! format printf fprintf iota 1+ 1- path-extension path-absolute? with-input-from-string with-output-to-string) --- a/benchmarks/bench-mmap.ss +++ b/benchmarks/bench-mmap.ss @@ -23,7 +23,7 @@ (guard (_ [#t #f]) (load-shared-object "libSystem.dylib") #t) (guard (_ [#t #f]) (load-shared-object "libSystem.B.dylib") #t)) -(import (chezscheme) +(import (scheme) (std mmap) (std runtime gc)) --- a/benchmarks/bench-string-append.ss +++ b/benchmarks/bench-string-append.ss @@ -5,7 +5,7 @@ ;;; fold, consecutive literal arguments collapse into one. The ;;; per-call allocator cost should scale linearly with arg count. -(import (except (chezscheme) +(import (except (scheme) make-hash-table hash-table? sort sort! format printf fprintf iota 1+ 1- path-extension path-absolute? with-input-from-string with-output-to-string) --- a/data/anti-patterns.sexp +++ b/data/anti-patterns.sexp @@ -882,4 +882,437 @@ ("tools" "jerboa_request_advisor" "verify" - "jerboa_anti_pattern_lookup"))) + "jerboa_anti_pattern_lookup")) + (("advice" + . + "Convert strings at the Scheme boundary to NUL-terminated UTF-8 bytevectors, change the FFI parameter type to u8*, and pass the bytevector to the collect-safe foreign call. Keep C code treating the pointer as const char* only for the duration of the call.") + ("avoid" + . + "Do not add __collect_safe to a foreign-procedure that still declares string parameters. Chez rejects string arguments for collect-safe foreign calls at runtime.") + ("id" . "collect-safe-string-ffi-args") + ("kinds" "ffi" "security") + ("pattern" + . + "foreign-procedure\\s+__collect_safe[^\\n]*\\bstring\\b") + ("severity" . "high") + ("tags" "ffi" "collect-safe" "foreign-procedure" "strings" + "bytevector" "chez") + ("title" + . + "Do not use __collect_safe with string FFI arguments") + ("tools" + "jerboa_security_scan" + "jerboa_run_tests" + "jerboa_failure_advisor")) + (("advice" + . + "Use suppressions as documented risk acceptances only. Fix production-source findings in code when feasible, then classify any remaining scanner output by path class such as runtime, build, test, generated, or vendor in the release tracker.") + ("avoid" + . + "Do not assume .jerboa/security.json suppressions or inline suppression comments remove findings from jerboa_security_scan output.") + ("id" . "security-profile-suppressions-not-filters") + ("kinds" "security" "docs") + ("pattern" + . + "\\.jerboa/security\\.json.*suppressions|jerboa-security: suppress") + ("severity" . "medium") + ("tags" "security-scan" "suppressions" "profile" "findings" + "audit") + ("title" + . + "Treat Security Profile Suppressions As Audit Notes") + ("tools" + "jerboa_security_scan" + "jerboa_security_profile" + "jerboa_request_advisor" + "rg")) + (("advice" + . + "Keep the real validation in code, but make the sink line visibly consume a checked/safe value, such as a validated safe-path variable or safe-* wrapper. Pair that with focused tests for rejected tainted or non-allowlisted inputs.") + ("avoid" + . + "Do not hide the only taint/path validation inside a helper while leaving a dangerous sink line that still looks raw to the line-oriented security scanner.") + ("id" . "hidden-taint-check-helper-invisible-to-scanner") + ("kinds" "security" "module") + ("pattern" + . + "\\((?:system|open-output-file|open-input-file|delete-file)\\s+[^\\n]*(?!check-untainted|safe-)") + ("severity" . "medium") + ("tags" "taint" "scanner" "safe-wrapper" "file-io" + "line-oriented") + ("title" . "Hidden Taint Checks Invisible To Line Scanner") + ("tools" + "jerboa_security_scan" + "jerboa_howto" + "jerboa_verify" + "jerboa_run_tests")) + (("advice" + . + "Generate FFI symbol lists from defined symbols instead: prefer `llvm-nm` or `nm -g --defined-only`, fall back to parsing `nm -g` type columns for defined text/data symbols, and then filter bare symbol names with the expected prefix such as `jerboa_`. Verify with the full two-pass `make binary`/WPO path, not only the first build pass.") + ("avoid" + . + "Do not generate Jerboa/Chez FFI registration lists with macOS `nm -U` or `nm -gjU`; on Darwin, `-U` selects undefined symbols, so the list can omit the defined native `jerboa_*` exports needed by the second binary link pass.") + ("id" . "macos-nm-u-undefined-ffi-symbols") + ("kinds" "ffi" "security" "debug-error") + ("pattern" . "nm\\s+-[A-Za-z]*U[A-Za-z]*.*jerboa_") + ("severity" . "medium") + ("tags" "ffi" "nm" "macos" "jerbuild" "static-binary" + "symbols") + ("title" . "Do Not Use macOS nm -U For Defined FFI Symbols") + ("tools" + "jerboa_make" + "jerboa_failure_advisor" + "jerboa_security_scan" + "rg")) + (("advice" + . + "Acquire into a mutable #f handle slot under a guard/dynamic cleanup block. Close the handle in the exception path, build the owning transport object only after setup succeeds, then clear the local slot before returning so ownership transfer is explicit. Re-run jerboa_security_scan on the file and the real project tests.") + ("avoid" + . + "Do not acquire sockets, TLS handles, file descriptors, or other closeable resources directly in a let binding and then return the handle/transport to the caller. Jerboa's security scanner treats that shape as resource-no-cleanup, and it can also miss exceptions between acquisition and ownership transfer.") + ("id" . "resource-acquisition-let-returned-handle") + ("kinds" "security" "ffi" "network") + ("pattern" + . + "\\(let \\(\\[[^\\]]+ \\((tcp-connect|ssl-connect|open-file|open-.*port)") + ("severity" . "medium") + ("tags" "resource-cleanup" "socket" "tls" "scanner" + "ownership") + ("title" + . + "Avoid Let-Bound Resource Acquisition When Returning Ownership") + ("tools" + "jerboa_security_scan" + "jerboa_verify" + "jerboa_run_tests")) + (("advice" + . + "Import `(jerboa ffi)` or narrow `(only (jerboa ffi) load-shared-object*)` plus the existing Jerboa prelude FFI macros. Use `c-lambda` inside guarded/lazy binding setup instead of top-level `foreign-procedure`, and call `load-shared-object*` only from an availability check or native-call setup path. Verify with `jerboa_verify`, the real Makefile test, and `jerboa_security_scan`.") + ("avoid" + . + "Do not keep `(chezscheme)` imports in user libraries only to access `foreign-procedure` or `load-shared-object`. The Jerboa scanner flags the import as a capability bypass, and top-level dynamic loading can also create static-binary crashes.") + ("id" . "direct-chez-ffi-import-in-user-library") + ("kinds" "ffi" "security" "module") + ("pattern" + . + "\\(import[^)]*\\(chezscheme\\).*foreign-procedure|load-shared-object") + ("severity" . "high") + ("tags" "chezscheme" "ffi" "load-shared-object" "c-lambda" + "scanner" "static-binary") + ("title" + . + "Avoid Direct Chez Imports For FFI In User Libraries") + ("tools" + "jerboa_verify" + "jerboa_security_scan" + "jerboa_run_tests")) + (("advice" + . + "Prefer explicit statement finalization before close. For safe close wrappers that finalize tracked statements internally, keep `sqlite-finalize` later on the same source line or use an internal diagnostic label that does not contain `sqlite-close`; then rerun `jerboa_security_scan` and the real SQLite tests.") + ("avoid" + . + "Do not leave public exports, diagnostics, or close tests with a bare `sqlite-close` token on a line that lacks explicit `sqlite-finalize` context when running the Jerboa scanner.") + ("id" . "sqlite-close-line-rule-without-finalize-context") + ("kinds" "security" "ffi" "test") + ("pattern" . "sqlite-close(?!.*sqlite-finalize)") + ("severity" . "low") + ("tags" "sqlite" "security-scan" "finalize" "false-positive" + "ffi") + ("title" + . + "SQLite Close Scanner Needs Same-Line Finalize Context") + ("tools" + "jerboa_security_scan" + "jerboa_verify" + "jerboa_run_tests")) + (("advice" + . + "Collect parser results into `Result<Vec<_>, _>` or use `PemObject::from_pem_*` and fail closed on any parse error. Check that the resulting certificate/key list is non-empty, then rerun cargo tests and `cargo-audit -D warnings`.") + ("avoid" + . + "Do not parse certificate chains or private-key material with `filter_map(|r| r.ok())`; that silently discards malformed PEM sections and can let bad input pass as a truncated chain.") + ("id" . "pem-filter-map-drops-parse-errors") + ("kinds" "security" "ffi") + ("pattern" + . + "rustls(_pki_types|_pemfile).*\\.filter_map\\(\\|r\\| r\\.ok\\(\\)\\)|filter_map\\(\\|r\\| r\\.ok\\(\\)\\).*certs") + ("severity" . "high") + ("tags" "pem" "tls" "rust" "certificates" "ffi" "audit") + ("title" . "Do Not Drop PEM Parse Errors With filter_map") + ("tools" "rg" "jerboa_security_scan" "jerboa_run_tests")) + (("advice" + . + "Use a builder subprocess helper that does not transitively import target program libraries. Clean stale generated .so/.wpo before snapshotting, compile source-only libraries in dependency order, and snapshot after cleanup so failed builds cannot poison later WPO runs. If a source-only dependency lacks .wpo, precompile a matching .so and let compile-whole-program bundle it.") + ("avoid" + . + "Do not import a helper library in a build script when the same run deletes, recompiles, or bundles that library as part of compile-program/compile-whole-program. Also avoid compiling dependent source-only libraries before their source-only dependencies, then overwriting the dependency .so later in the same run.") + ("id" . "wpo-builder-imports-recompiled-lib") + ("kinds" "jerboa" "build" "wpo" "security-release") + ("pattern" + . + "compile-whole-program.*does not define expected compilation instance|source-only stdlib|std misc channel|std os aproc") + ("severity" . "high") + ("tags" "wpo" "compile-whole-program" "generated-artifacts" + "source-only-stdlib" "release-evidence") + ("title" + . + "WPO builder imports or overwrites libraries in its program graph") + ("tools" + "jerboa_verify" + "jerboa_compile_check" + "jerboa_security_scan")) + (("advice" + . + "For portable project tests, run scripts with `jerbuild exec --libdirs <project>:<jerboa-home>/lib <script.ss>`. Fall back to an adjacent checkout's `.chez/bin/scheme` only when no usable jerbuild is available, and make CI install jerbuild before invoking make.") + ("avoid" + . + "Do not assume a release jerbuild --jerboa-home directory contains .chez/bin/scheme or has the same layout as a developer checkout.") + ("id" . "jerbuild-home-as-chez-checkout") + ("kinds" "all" "test" "security") + ("pattern" + . + "jerbuild --jerboa-home.*\\.chez/bin/scheme|\\$\\(JH\\)/\\.chez/bin/scheme") + ("severity" . "medium") + ("tags" "jerbuild" "makefile" "release-toolchain" "tests" + "libdirs" "ci") + ("title" + . + "Treating jerbuild --jerboa-home as a Chez Checkout") + ("tools" "jerboa_howto" "jerboa_verify" "jerboa_make")) + (("advice" + . + "Use `(except (jerboa prelude) tcp-connect tcp-listen tcp-accept tcp-close)` when a module intentionally imports `(std net tcp)` for the concrete TCP API. Re-run the exact import/build command that failed, not just unrelated tests.") + ("avoid" + . + "Do not import `(jerboa prelude)` and `(std net tcp)` together without excluding the prelude TCP helpers. The prelude exports safe TCP aliases with the same names as `(std net tcp)`, which can compile-fail with duplicate definitions such as tcp-connect.") + ("id" . "prelude-std-net-tcp-import-collision") + ("kinds" "module" "debug-error") + ("pattern" + . + "\\(jerboa prelude\\).*\\(std net tcp\\)|multiple definitions for tcp-connect") + ("severity" . "medium") + ("tags" "import" "tcp" "prelude" "std-net" + "duplicate-definition") + ("title" + . + "Avoid Prelude TCP Name Collisions With std net tcp") + ("tools" + "jerboa_module_exports" + "jerboa_compile_check" + "jerboa_verify" + "jerboa_make")) + (("advice" + . + "Read the length into a scalar, validate type/range/max immediately, then allocate. Add corruption tests for oversized/truncated payloads and re-run both the focused decoder tests and the security scanner.") + ("avoid" + . + "Do not read a count or length from serialized/untrusted input and allocate a vector, bytevector, fxvector, or flvector before validating that count against a sane maximum.") + ("id" . "decode-length-validated-after-allocation") + ("kinds" "security" "module" "test") + ("pattern" + . + "make-(vector|bytevector|fxvector|flvector)\\s+[^\\n]*\\n[^\\n]*(unless|when).*max|\\[n \\([^\\)]*read[^\\)]*\\)\\][\\s\\S]{0,200}make-(vector|bytevector|fxvector|flvector)") + ("severity" . "high") + ("tags" "decoder" "serialization" "allocation" "corruption" + "bounds") + ("title" . "Validate Serialized Lengths Before Allocation") + ("tools" + "jerboa_security_scan" + "jerboa_verify" + "jerboa_run_tests" + "jerboa_failure_advisor")) + (("advice" + . + "During development, pair tracked-file gates with `rg` or `jerboa_security_scan` over the working tree. After adding new files, confirm they are tracked or explicitly included before treating a git-grep gate as release evidence.") + ("avoid" + . + "Do not rely only on `git grep` in a local release/security gate while new hardening scripts, docs, or source files are still untracked.") + ("id" . "git-grep-security-gate-ignores-untracked") + ("kinds" "security" "docs" "test") + ("pattern" + . + "git grep .*security|secret|system|foreign|read") + ("severity" . "medium") + ("tags" "git-grep" "security-gate" "untracked-files" + "release-evidence" "secret-scan") + ("title" . "Git Grep Security Gates Miss Untracked Files") + ("tools" "rg" "jerboa_security_scan" "git status --short")) + (("advice" + . + "For a known current password, use the OATH access change flow: select OATH, validate with the current passphrase if the SELECT response includes a challenge, then set the new password with oath-set-code. Only discuss reset when the password is forgotten and the user accepts deleting all OATH accounts.") + ("avoid" + . + "Do not suggest or call OATH reset when the user wants to change a known OATH password. Reset wipes all OATH credentials/accounts.") + ("id" . "yubikey-oath-change-not-reset") + ("kinds" "all" "security" "docs") + ("pattern" . "oath reset|ykman oath reset|oath-reset") + ("severity" . "high") + ("tags" "yubikey" "oath" "password" "reset" "credentials") + ("title" . "Do Not Reset OATH For Password Changes") + ("tools" + "jerboa_howto" + "jerboa_function_signature" + "jerboa_security_scan")) + (("advice" + . + "Make the security target depend on a clean-generated step, or remove generated outputs before source/security evidence collection and rebuild only for binary smoke/hash steps. If a previously tracked generated file is being deleted, allow the local deletion state but keep the gate rejecting present tracked artifacts in clean checkouts.") + ("avoid" + . + "Do not run a generated-artifact rejection gate after a build target without first cleaning the generated outputs, especially when removing a previously tracked binary from a repo.") + ("id" . "generated-artifact-gate-after-build") + ("kinds" "security" "test" "all") + ("pattern" + . + "security.*generated artifact|tracked generated artifact|make verify.*builds.*binary|release-evidence.*security.*binary") + ("severity" . "medium") + ("tags" "generated-artifacts" "security-gate" + "release-evidence" "git-status" "make-verify" + "tracked-binary") + ("title" + . + "Generated Artifact Gates Must Start From Clean Outputs") + ("tools" + "git status --short --ignored" + "make security" + "make release-evidence" + "jerboa_security_scan")) + (("advice" + . + "Use the vendored module API directly, confirm exports/signatures with Jerboa MCP tools, add focused tests in the library repo when bindings are involved, and smoke-test with PATH stripped so accidental CLI dependencies fail visibly.") + ("avoid" + . + "Do not implement Jerboa functionality by shelling out to a system CLI when the project vendors a direct Jerboa library for the same domain. This adds hidden runtime dependencies, bypasses library tests, and can mask broken bindings.") + ("id" . "external-cli-fallback-for-vendored-jerboa-api") + ("kinds" "module" "script" "security") + ("pattern" + . + "ykman|run-cmd.*(ykman|oath)|shell-join.*ykman") + ("severity" . "high") + ("tags" "shellout" "vendored-api" "yubikey" "ykman" + "run-cmd" "ffi") + ("title" + . + "Do Not Shell Out When a Vendored Jerboa API Exists") + ("tools" "jerboa_howto" "jerboa_module_exports" + "jerboa_function_signature" "jerboa_verify" + "jerboa_security_scan" "jerboa_make")) + (("advice" + . + "Keep the scanner-safe `(scheme)` import, but preserve the conflict exclusions: use `(except (scheme) make-hash-table hash-table? sort sort! printf fprintf path-extension path-absolute? with-input-from-string with-output-to-string iota 1+ 1- partition make-date make-time)` before importing `(jerboa prelude)`. Re-run the exact WPO/build target that failed.") + ("avoid" + . + "Do not mechanically replace broad `(except (chezscheme) ...)` imports with bare `(scheme)` in modules that also import `(jerboa prelude)`. The build can fail with duplicate definitions such as `path-extension` and related prelude exports.") + ("id" . "bare-scheme-prelude-import-collision") + ("kinds" "module" "security") + ("pattern" + . + "\\(import \\(scheme\\)\\s+\\(except \\(jerboa prelude\\)") + ("severity" . "medium") + ("tags" "imports" "scheme" "prelude" "duplicate-definitions" + "chezscheme") + ("title" + . + "Avoid Bare Scheme Imports Beside Jerboa Prelude") + ("tools" + "jerboa_compile_check" + "jerboa_verify" + "jerboa_make" + "jerboa_security_scan")) + (("advice" + . + "For scanner/parser repos, capability checks should target runtime source primitives and build files, not arbitrary test fixtures or sample code strings. Keep fixture corpora searchable, but scope runtime policy checks to Scheme primitives such as open-process-ports, raw system calls, direct FFI, or concrete network APIs in source directories.") + ("avoid" + . + "Using broad grep patterns for words like process, socket, http-client, curl, or nc across tests and fixtures in scanner/parser repositories.") + ("id" . "scanner-repo-security-grep-fixture-false-positive") + ("kinds" "security" "jerboa" "parser" "scanner") + ("pattern" . "") ("severity" . "medium") + ("tags" + "security-check" + "false-positive" + "fixtures" + "semgrep") + ("title" + . + "Do not scan fixture text as runtime capabilities in scanner repositories") + ("tools" "jerboa_security_scan" "jerboa_failure_advisor")) + (("advice" + . + "Use existing Jerboa crypto APIs or Rust-backed native crypto libraries. If an algorithm is missing, add a small Rust native export and bind that, or use a Chez/Jerboa built-in primitive when available. Tests must fail when required crypto is unavailable; never silently skip OATH/password crypto tests.") + ("avoid" + . + "Do not implement Jerboa crypto by dlopening OpenSSL/CommonCrypto or binding ad hoc C crypto symbols from Scheme. This creates fragile platform behavior and hidden runtime dependencies, and it violates the project preference for Jerboa/Rust crypto.") + ("id" . "openssl-dlopen-for-jerboa-crypto") + ("kinds" "security" "ffi" "module" "debug-error") + ("pattern" + . + "load-shared-object\\*?.*(libcrypto|openssl|CommonCrypto)|c-lambda.*(HMAC|EVP_|SHA[0-9])") + ("severity" . "high") + ("tags" "crypto" "openssl" "dlopen" "rust-native" + "jerboa-crypto" "ffi") + ("title" . "Do Not dlopen OpenSSL For Jerboa Crypto") + ("tools" "jerboa_howto" "jerboa_module_exports" + "jerboa_function_signature" "jerboa_security_scan" + "jerboa_run_tests")) + (("advice" + . + "Treat std sandbox modules as non-enforcing unless the scanner and runtime evidence prove otherwise. Either remove the dependency and state that kernel sandboxing is not claimed, or implement a verified native enforcement layer and add target-specific tests/evidence. Rerun jerboa_security_scan at medium severity or stricter.") + ("avoid" + . + "Do not import Jerboa std seccomp/Landlock stubs and document them as real production sandbox enforcement without proof that they enforce kernel policy on the target platform.") + ("id" . "std-security-stub-production-claim") + ("kinds" "security" "ffi" "docs") + ("pattern" + . + "std security (seccomp|landlock)|seccomp-.*install|landlock-.*install") + ("severity" . "high") + ("tags" "seccomp" "landlock" "sandbox" "stubs" "production" + "scanner") + ("title" + . + "Do Not Claim Std Sandbox Stubs As Production Enforcement") + ("tools" + "jerboa_security_scan" + "rg" + "jerboa_howto" + "make verify")) + (("advice" + . + "Use portable entry-script forms such as `define` and explicit helper functions for data literals, or import only the exact symbols needed. If a broad module import causes multiple-definition errors in `jerboa-entry.ss`, narrow it with `(only (module) symbol ...)`. Always run the actual `make binary` or WPO target after editing entry scripts.") + ("avoid" + . + "Do not assume top-level WPO entry scripts have the same shorthand bindings as Jerboa library modules or REPL-oriented scripts. In particular, avoid adding `def`, `hash`, or broad utility imports to an entry script and treating `make test` as enough evidence.") + ("id" . "wpo-entry-script-shorthand-bindings") + ("kinds" "script" "debug-error") + ("pattern" + . + "attempt to reference unbound identifier (def|hash)|multiple definitions .* in body .*jerboa-entry\\.ss") + ("severity" . "medium") + ("tags" "jerbuild" "wpo" "entry-script" "binary" "imports" + "shorthand") + ("title" + . + "Do Not Rely On Shorthand Bindings In WPO Entry Scripts") + ("tools" + "jerboa_make" + "jerboa_module_exports" + "jerboa_verify" + "jerboa_failure_advisor")) + (("advice" + . + "Use an escaped dot symbol in Lisp query forms, for example `(list \\. (symbol) @face (:match ...))`, or use a query string. Always run `emacs --batch -Q -L . -f batch-byte-compile <mode>.el` after editing Tree-sitter font-lock queries.") + ("avoid" + . + "Do not write Tree-sitter query anchors as raw dotted syntax inside quoted Emacs Lisp queries when additional query elements follow, for example `(list . (symbol) @face (:match ...))`. Emacs reads this as invalid dotted-list syntax and byte compilation fails before the mode can load.") + ("id" . "emacs-treesit-query-anchor-dot-read-syntax") + ("kinds" "docs" "test" "security") + ("pattern" + . + "\\(list \\. \\(symbol\\) @font-lock|Invalid read syntax: \"expected \\)\"") + ("severity" . "medium") + ("tags" "emacs" "treesit" "tree-sitter" "font-lock" + "byte-compile" "query") + ("title" + . + "Escape Tree-sitter Query Anchor Dots In Emacs Lisp") + ("tools" "rg" "emacs --batch" "make test" "make verify"))) --- a/data/cookbooks.sexp +++ b/data/cookbooks.sexp @@ -6229,4 +6229,62 @@ "strings") ("title" . - "Encode embedded Chez boot and WPO blobs in generated C launchers"))) + "Encode embedded Chez boot and WPO blobs in generated C launchers")) + (("code" + . + "(library (example bounded-rule-reader)\n (export load-rule-forms)\n (import (scheme)\n (only (jerboa reader)\n jerboa-read\n *max-read-depth* *max-list-length*\n *max-string-length* *max-symbol-length*)\n (only (std security taint) check-untainted!))\n\n (def +max-rule-file-forms+ 10000)\n\n (def (load-rule-forms path)\n (check-untainted! path 'load-rule-forms)\n (call-with-input-file path\n (lambda (p)\n (parameterize ([*max-read-depth* 64]\n [*max-list-length* 100000]\n [*max-string-length* (* 1024 1024)]\n [*max-symbol-length* 256])\n (let loop ([forms '()] [count 0])\n (let ([form (jerboa-read p)])\n (cond\n [(eof-object? form) (reverse forms)]\n [(>= count +max-rule-file-forms+)\n (error 'load-rule-forms \"too many forms\" path)]\n [(and (pair? form) (eq? (car form) 'rule))\n (loop (cons form forms) (fx+ count 1))]\n [else\n (error 'load-rule-forms \"expected (rule ...) form\" form)]))))))))") ("id" . "bounded-jerboa-reader-for-rule-files") + ("imports" + "(only (jerboa reader) jerboa-read *max-read-depth* *max-list-length* *max-string-length* *max-symbol-length*)" + "(only (std security taint) check-untainted!)") + ("notes" + . + "Use `jerboa-read`, not raw Chez `read`, for user-supplied rule/config files. Set reader limits with `parameterize` around the entire file read, add a top-level form cap, and validate the expected form shape before constructing runtime objects. Still run the real project tests and `jerboa_security_scan` because the safe reader only handles syntax/resource bounds; semantic validation is caller-specific.") + ("tags" "reader" "security" "jerboa-read" "untrusted-input" + "rule-loader" "taint") + ("title" + . + "Use bounded jerboa-read for untrusted rule files")) + (("code" + . + "(import (jerboa prelude)\n (yubikey transport connection)\n (yubikey oath))\n\n(def (change-oath-password current-pass new-pass)\n (yk-with-connection\n (lambda (conn)\n (let ([info (oath-select conn)])\n ;; A challenge in the SELECT response means the OATH applet is locked.\n ;; Validate with the current password before installing the new one.\n (when (oath-info-challenge info)\n (oath-validate conn current-pass info))\n (oath-set-code conn new-pass info)\n 'changed))))\n\n;; Example:\n;; (change-oath-password \"old password\" \"new password\")") ("id" . "yubikey-oath-password-change") + ("imports" + "(jerboa prelude)" + "(yubikey transport connection)" + "(yubikey oath)") + ("notes" + . + "Use this for in-place OATH password changes. Do not call oath-reset for a password change; oath-reset wipes all OATH credentials. If the SELECT response has no challenge, no current password is set and oath-set-code can set the new password directly. In interactive jsh code, collect current/new passwords with ffi-embed-read-passphrase so they are not echoed or saved in argv/history.") + ("tags" "yubikey" "oath" "password" "oath-set-code" + "oath-validate" "yk-with-connection") + ("title" + . + "Change a YubiKey OATH Password Without Resetting Accounts")) + (("code" + . + "(import (only-in :std/sugar with-catch)\n (only (jerboa ffi) load-shared-object*))\n\n(define *ffi-loaded?* #f)\n\n(define (ensure-ffi!)\n (unless *ffi-loaded?*\n (set! *ffi-loaded?* #t)\n ;; Static jerbuild binaries should register symbols from C main via\n ;; Sforeign_symbol. load-shared-object* #f is only a guarded fallback for\n ;; dynamic/dev tools that resolve symbols from the current process.\n (with-catch\n (lambda (e) (void))\n (lambda () (load-shared-object* #f))))\n #t)\n\n(define-syntax define-lazy-foreign\n (syntax-rules (__collect_safe)\n ((_ name __collect_safe c-name (arg-type ...) ret-type)\n (define name\n (let ((proc #f))\n (lambda args\n (unless proc\n (ensure-ffi!)\n (set! proc (foreign-procedure __collect_safe c-name (arg-type ...) ret-type)))\n (apply proc args)))))\n ((_ name c-name (arg-type ...) ret-type)\n (define name\n (let ((proc #f))\n (lambda args\n (unless proc\n (ensure-ffi!)\n (set! proc (foreign-procedure c-name (arg-type ...) ret-type)))\n (apply proc args)))))))\n\n;; Example:\n(define-lazy-foreign c-terminal-width \"my_terminal_width\" (int) int)\n(define-lazy-foreign c-read-fd __collect_safe \"my_read_fd\" (int u8* size_t) ssize_t)") ("id" . "lazy-static-foreign-wrapper") + ("imports"