Security hardening and release readiness
ober
7937f014e29bbc4aa3bbd16917b38721ba4d939f
new file mode 100644 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,75 @@ +name: CI + +on: + push: + branches: [main, master] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +env: + JERBOA_VERSION: v0.2.3 + JERBUILD: ${{ github.workspace }}/.jerboa/bin/jerbuild + JERBOA_AWS_REPO: https://git.sr.ht/~lisp/jerboa-aws + JERBOA_PROTON_BRIDGE_REPO: https://git.sr.ht/~lisp/jerboa-proton-bridge + JERBOA_YUBIKEY_REPO: https://git.sr.ht/~lisp/jerboa-yubikey + JERBOA_PGP_REPO: https://git.sr.ht/~lisp/jerboa-pgp + JERBOA_CRYPTO_REPO: https://git.sr.ht/~lisp/jerboa-crypto + JERBOA_FUSE_REPO: https://git.sr.ht/~lisp/jerboa-fuse + JERBOA_MAIL_REPO: https://git.sr.ht/~lisp/jerboa-mail + JERBOA_HTTPS_REPO: https://git.sr.ht/~lisp/jerboa-https + JERBOA_SSL_REPO: https://git.sr.ht/~lisp/jerboa-ssl + JERBOA_AWS_DIR: ${{ github.workspace }}/.deps/jerboa-aws + JERBOA_PROTON_BRIDGE_DIR: ${{ github.workspace }}/.deps/jerboa-proton-bridge + JERBOA_YUBIKEY_DIR: ${{ github.workspace }}/.deps/jerboa-yubikey + JERBOA_PGP_DIR: ${{ github.workspace }}/.deps/jerboa-pgp + JERBOA_CRYPTO_DIR: ${{ github.workspace }}/.deps/jerboa-crypto + JERBOA_FUSE_DIR: ${{ github.workspace }}/.deps/jerboa-fuse + JERBOA_MAIL_DIR: ${{ github.workspace }}/.deps/jerboa-mail + JERBOA_HTTPS_DIR: ${{ github.workspace }}/.deps/jerboa-https + JERBOA_SSL_DIR: ${{ github.workspace }}/.deps/jerboa-ssl + +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 git curl ca-certificates libssl-dev pkg-config + command -v cargo + cargo --version + + - name: Install Jerboa toolchain + run: sh support/ensure-jerboa.sh "$JERBOA_VERSION" .jerboa/bin + + - name: Fetch dependencies + run: | + set -eu + mkdir -p .deps + git clone --depth 1 "$JERBOA_AWS_REPO" "$JERBOA_AWS_DIR" + git clone --depth 1 "$JERBOA_PROTON_BRIDGE_REPO" "$JERBOA_PROTON_BRIDGE_DIR" + git clone --depth 1 "$JERBOA_YUBIKEY_REPO" "$JERBOA_YUBIKEY_DIR" + git clone --depth 1 "$JERBOA_PGP_REPO" "$JERBOA_PGP_DIR" + git clone --depth 1 "$JERBOA_CRYPTO_REPO" "$JERBOA_CRYPTO_DIR" + git clone --depth 1 "$JERBOA_FUSE_REPO" "$JERBOA_FUSE_DIR" + git clone --depth 1 "$JERBOA_MAIL_REPO" "$JERBOA_MAIL_DIR" + git clone --depth 1 "$JERBOA_HTTPS_REPO" "$JERBOA_HTTPS_DIR" + git clone --depth 1 "$JERBOA_SSL_REPO" "$JERBOA_SSL_DIR" + + - name: Test + run: make test + + - name: CLI doctor smoke + run: make run ARGS='doctor' + + - name: Install cargo-audit + run: cargo install cargo-audit --locked + + - name: Release evidence + run: make release-evidence 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 @@ -1,6 +1,10 @@ *.so *.dylib *.o +.deps/ +.jerboa/* +!.jerboa/ +!.jerboa/security.json cache/ tmp/ mount/ new file mode 100644 --- /dev/null +++ b/.jerboa/security.json @@ -0,0 +1,48 @@ +{ + "version": 1, + "repo": "jerboa-drive", + "extends": ["jerboa:cli", "jerboa:crypto", "jerboa:ffi", "jerboa:network", "jerboa:credentialed-client", "jerboa:storage"], + "paths": { + "production": ["*.ss", "*.sls", "lib/**/*.ss", "lib/**/*.sls", "protonstorage/**/*.ss", "support/**/*.ss", "Makefile"], + "tests": ["test/**", "tests/**", "**/*-test.ss", "fixtures/**"], + "generated": ["build/**", "dist/**", "target/**", "*.so", "*.dylib", "*.wpo", "*-main.c"], + "vendor": ["vendor/**", "third_party/**"], + "docs": ["README.md", "docs/**", "*.md"] + }, + "policy": { + "failOn": ["critical", "high"], + "imports": { "directChezscheme": "allow-in-ffi-boundaries" }, + "ffi": { "allowed": true, "requireDynamicWindCleanup": true }, + "process": { "shellInterpolation": "deny" }, + "network": { "requireTimeouts": true, "rawRemoteOutputEvidence": "deny" }, + "eval": { "stringEval": "deny", "bareRead": "deny", "allowReadEval": false }, + "crypto": { "forbidSecretLogging": true, "requireRustSecCleanBackends": true }, + "credentials": { + "plaintextCache": "deny", + "rawCredentialEvidence": "deny", + "vaultPasswordStorage": "deny" + }, + "storage": { + "remotePlaintextFilenames": "deny", + "remotePlaintextBodies": "deny", + "fuseMountEvidence": "target-required" + }, + "targetEvidence": { + "storageProof": "fail-closed", + "requiredMarkers": [ + "proton_live_drive_status=target-evidence-recorded", + "s3_encrypted_roundtrip_status=target-evidence-recorded", + "fuse_mount_smoke_status=target-evidence-recorded", + "credential_redaction_status=target-evidence-recorded", + "no_plaintext_remote_state_status=target-evidence-recorded", + "vault_permissions_status=target-evidence-recorded", + "proton_crypto_disabled_status=confirmed", + "target_platform_smoke_status=target-evidence-recorded", + "production_storage_review_status=current-run-recorded", + "raw_remote_output_status=not-preserved", + "sensitive_artifact_policy=no-proton-passwords-tokens-s3-keys-vault-passwords-piv-pins-plaintext-files-or-raw-account-output-preserved" + ] + } + }, + "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,23 +1,54 @@ -JERBOA_HOME ?= $(realpath $(CURDIR)/../jerboa) -SCHEME ?= $(JERBOA_HOME)/.chez/bin/scheme -JERBOA_PROTON_BRIDGE_DIR ?= $(realpath $(CURDIR)/../jerboa-proton-bridge) -JERBOA_HTTPS_DIR ?= $(realpath $(CURDIR)/../jerboa-https) -JERBOA_SSL_DIR ?= $(realpath $(CURDIR)/../jerboa-ssl) -JERBOA_YUBIKEY_DIR ?= $(realpath $(CURDIR)/../jerboa-yubikey) -JERBOA_PGP_DIR ?= $(realpath $(CURDIR)/../jerboa-pgp) -JERBOA_CRYPTO_DIR ?= $(realpath $(CURDIR)/../jerboa-crypto) -JERBOA_FUSE_DIR ?= $(realpath $(CURDIR)/../jerboa-fuse) -JERBOA_MAIL_DIR ?= $(realpath $(CURDIR)/../jerboa-mail) -JERBOA_AWS_DIR ?= $(realpath $(CURDIR)/../jerboa-aws) -LIBDIRS := $(CURDIR):$(JERBOA_AWS_DIR)/lib:$(JERBOA_PROTON_BRIDGE_DIR):$(JERBOA_YUBIKEY_DIR):$(JERBOA_YUBIKEY_DIR)/lib:$(JERBOA_PGP_DIR):$(JERBOA_CRYPTO_DIR)/lib:$(JERBOA_FUSE_DIR)/lib:$(JERBOA_MAIL_DIR):$(JERBOA_HTTPS_DIR)/lib:$(JERBOA_SSL_DIR)/lib:$(JERBOA_HOME)/lib -XC_LIBDIRS := $(LIBDIRS):$(JERBOA_HOME)/.chez-cross-ta6le/lib:$(JERBOA_HOME)/.chez-cross-tarm64le/lib:$(JERBOA_HOME)/.chez-cross-ta6fb/lib -JERBOA_NATIVE_DIR ?= $(JERBOA_HOME)/jerboa-native-rs/target/release -RUN_ENV := JERBOA_HOME=$(JERBOA_HOME) \ +JERBOA_VERSION ?= v0.2.3 +JERBOA_TOOL_DIR ?= $(CURDIR)/.jerboa/bin +JERBUILD ?= $(shell if [ -x "$(CURDIR)/jerbuild" ]; then echo "$(CURDIR)/jerbuild"; \ + elif [ -x "$(JERBOA_TOOL_DIR)/jerbuild" ]; then echo "$(JERBOA_TOOL_DIR)/jerbuild"; \ + elif [ -x "$(CURDIR)/../jerboa/dist/jerbuild" ]; then echo "$(CURDIR)/../jerboa/dist/jerbuild"; \ + elif [ -x "$(CURDIR)/../jerboa/jerbuild" ]; then echo "$(CURDIR)/../jerboa/jerbuild"; \ + elif command -v jerbuild >/dev/null 2>&1; then command -v jerbuild; \ + else echo "$(JERBOA_TOOL_DIR)/jerbuild"; fi) +JH = $(shell "$(JERBUILD)" --jerboa-home 2>/dev/null) +JERBOA_HOME ?= $(JH) + +DEPS_DIR ?= $(CURDIR)/.deps +LOCAL_PROTON_BRIDGE_DIR := $(CURDIR)/../jerboa-proton-bridge +LOCAL_HTTPS_DIR := $(CURDIR)/../jerboa-https +LOCAL_SSL_DIR := $(CURDIR)/../jerboa-ssl +LOCAL_YUBIKEY_DIR := $(CURDIR)/../jerboa-yubikey +LOCAL_PGP_DIR := $(CURDIR)/../jerboa-pgp +LOCAL_CRYPTO_DIR := $(CURDIR)/../jerboa-crypto +LOCAL_FUSE_DIR := $(CURDIR)/../jerboa-fuse +LOCAL_MAIL_DIR := $(CURDIR)/../jerboa-mail +LOCAL_AWS_DIR := $(CURDIR)/../jerboa-aws +JERBOA_PROTON_BRIDGE_DIR ?= $(if $(wildcard $(LOCAL_PROTON_BRIDGE_DIR)/.),$(realpath $(LOCAL_PROTON_BRIDGE_DIR)),$(DEPS_DIR)/jerboa-proton-bridge) +JERBOA_HTTPS_DIR ?= $(if $(wildcard $(LOCAL_HTTPS_DIR)/.),$(realpath $(LOCAL_HTTPS_DIR)),$(DEPS_DIR)/jerboa-https) +JERBOA_SSL_DIR ?= $(if $(wildcard $(LOCAL_SSL_DIR)/.),$(realpath $(LOCAL_SSL_DIR)),$(DEPS_DIR)/jerboa-ssl) +JERBOA_YUBIKEY_DIR ?= $(if $(wildcard $(LOCAL_YUBIKEY_DIR)/.),$(realpath $(LOCAL_YUBIKEY_DIR)),$(DEPS_DIR)/jerboa-yubikey) +JERBOA_PGP_DIR ?= $(if $(wildcard $(LOCAL_PGP_DIR)/.),$(realpath $(LOCAL_PGP_DIR)),$(DEPS_DIR)/jerboa-pgp) +JERBOA_CRYPTO_DIR ?= $(if $(wildcard $(LOCAL_CRYPTO_DIR)/.),$(realpath $(LOCAL_CRYPTO_DIR)),$(DEPS_DIR)/jerboa-crypto) +JERBOA_FUSE_DIR ?= $(if $(wildcard $(LOCAL_FUSE_DIR)/.),$(realpath $(LOCAL_FUSE_DIR)),$(DEPS_DIR)/jerboa-fuse) +JERBOA_MAIL_DIR ?= $(if $(wildcard $(LOCAL_MAIL_DIR)/.),$(realpath $(LOCAL_MAIL_DIR)),$(DEPS_DIR)/jerboa-mail) +JERBOA_AWS_DIR ?= $(if $(wildcard $(LOCAL_AWS_DIR)/.),$(realpath $(LOCAL_AWS_DIR)),$(DEPS_DIR)/jerboa-aws) +LIBDIRS = $(CURDIR):$(JERBOA_AWS_DIR)/lib:$(JERBOA_PROTON_BRIDGE_DIR):$(JERBOA_YUBIKEY_DIR):$(JERBOA_YUBIKEY_DIR)/lib:$(JERBOA_PGP_DIR):$(JERBOA_CRYPTO_DIR)/lib:$(JERBOA_FUSE_DIR)/lib:$(JERBOA_MAIL_DIR):$(JERBOA_HTTPS_DIR)/lib:$(JERBOA_SSL_DIR)/lib:$(JH)/lib +XC_LIBDIRS = $(LIBDIRS):$(JH)/.chez-cross-ta6le/lib:$(JH)/.chez-cross-tarm64le/lib:$(JH)/.chez-cross-ta6fb/lib +JERBOA_NATIVE_DIR ?= $(JH)/jerboa-native-rs/target/release +PROTON_BRIDGE_NATIVE_DIR := $(JERBOA_PROTON_BRIDGE_DIR)/proton-bridge-native/target/release +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Darwin) + LD_VAR = DYLD_LIBRARY_PATH + SO_EXT = .dylib + FUSE_SO_EXT = .so +else + LD_VAR = LD_LIBRARY_PATH + SO_EXT = .so + FUSE_SO_EXT = .so +endif +RUN_ENV := JERBOA_HOME=$(JH) \ JERBOA_SSL_LIB=$(JERBOA_SSL_DIR) \ JERBOA_CRYPTO_LIB=$(JERBOA_CRYPTO_DIR) \ - DYLD_LIBRARY_PATH=$(JERBOA_NATIVE_DIR):$(JERBOA_CRYPTO_DIR):$(JERBOA_FUSE_DIR):$(JERBOA_PROTON_BRIDGE_DIR)/proton-bridge-native/target/release:$${DYLD_LIBRARY_PATH:-} \ + JERBOA_FUSE_LIB=$(JERBOA_FUSE_DIR) \ + DYLD_LIBRARY_PATH=$(JERBOA_NATIVE_DIR):$(JERBOA_CRYPTO_DIR):$(JERBOA_FUSE_DIR):$(PROTON_BRIDGE_NATIVE_DIR):$${DYLD_LIBRARY_PATH:-} \ DYLD_FALLBACK_LIBRARY_PATH=$(JERBOA_NATIVE_DIR):$${DYLD_FALLBACK_LIBRARY_PATH:-} \ - LD_LIBRARY_PATH=$(JERBOA_NATIVE_DIR):$(JERBOA_CRYPTO_DIR):$(JERBOA_FUSE_DIR):$(JERBOA_PROTON_BRIDGE_DIR)/proton-bridge-native/target/release:$${LD_LIBRARY_PATH:-} + LD_LIBRARY_PATH=$(JERBOA_NATIVE_DIR):$(JERBOA_CRYPTO_DIR):$(JERBOA_FUSE_DIR):$(PROTON_BRIDGE_NATIVE_DIR):$${LD_LIBRARY_PATH:-} BINARY_OUTPUT ?= protonstorage-bin LINUX_AMD64_CC ?= x86_64-linux-musl-gcc LINUX_ARM64_CC ?= aarch64-linux-musl-gcc @@ -28,8 +59,11 @@ BUNDLE_OUTPUT ?= dist/jerboa-drive-$(HOST_OS)-$(HOST_ARCH) INSTALL_PREFIX ?= $(HOME)/.local INSTALL_BINDIR ?= $(INSTALL_PREFIX)/bin INSTALL_OPTDIR ?= $(INSTALL_PREFIX)/opt/jerboa-drive +DIST_DIR ?= $(CURDIR)/dist +RELEASE_EVIDENCE_DIR ?= $(DIST_DIR)/release-evidence +TARGET_EVIDENCE_DIR ?= $(DIST_DIR)/target-evidence -.PHONY: help run test integration s3-integration doctor binary bundle install linux linux-amd64 linux-arm64 freebsd freebsd-amd64 clean +.PHONY: help ensure-jerboa-tools ensure-deps deps run test integration s3-integration doctor binary binary-doctor-smoke audit verify security dependency-evidence sbom reproducibility-report target-evidence release-evidence bundle install linux linux-amd64 linux-arm64 freebsd freebsd-amd64 clean .DEFAULT_GOAL := help help: @@ -39,6 +73,8 @@ help: @echo " make run ARGS='help' Run the CLI" @echo " make doctor Check native Jerboa implementation status" @echo " make test Run smoke tests" + @echo " make target-evidence Write target Drive/S3/FUSE proof status" + @echo " make release-evidence Write test/audit/SBOM/reproducibility evidence" @echo " make integration Run opt-in live Drive test when enabled" @echo " make s3-integration Run opt-in live encrypted S3 test when enabled" @echo " make clean Remove generated local artifacts" @@ -53,8 +89,7 @@ help: @echo " make freebsd-amd64 Cross-build ./protonstorage-freebsd-amd64" @echo "" @echo "Environment:" - @echo " JERBOA_HOME = $(JERBOA_HOME)" - @echo " SCHEME = $(SCHEME)" + @echo " JERBUILD = $(JERBUILD)" @echo " JERBOA_PROTON_BRIDGE_DIR = $(JERBOA_PROTON_BRIDGE_DIR)" @echo " JERBOA_YUBIKEY_DIR = $(JERBOA_YUBIKEY_DIR)" @echo " JERBOA_PGP_DIR = $(JERBOA_PGP_DIR)" @@ -67,29 +102,154 @@ help: @echo " JERBOA_NATIVE_DIR = $(JERBOA_NATIVE_DIR)" @echo " INSTALL_PREFIX = $(INSTALL_PREFIX)" -run: +ensure-jerboa-tools: + @if "$(JERBUILD)" --jerboa-home >/dev/null 2>&1; then \ + echo "=== Using Jerboa toolchain: $(JERBUILD) ==="; \ + elif [ -x "$(JERBOA_TOOL_DIR)/jerbuild" ] && "$(JERBOA_TOOL_DIR)/jerbuild" --jerboa-home >/dev/null 2>&1; then \ + echo "=== Using downloaded Jerboa toolchain: $(JERBOA_TOOL_DIR) ==="; \ + elif command -v jerbuild >/dev/null 2>&1 && jerbuild --jerboa-home >/dev/null 2>&1; then \ + echo "=== Using Jerboa toolchain from PATH: $$(command -v jerbuild) ==="; \ + else \ + echo "=== Fetching Jerboa $(JERBOA_VERSION) release tools into $(JERBOA_TOOL_DIR) ==="; \ + sh support/ensure-jerboa.sh "$(JERBOA_VERSION)" "$(JERBOA_TOOL_DIR)"; \ + fi + @"$(JERBUILD)" --jerboa-home >/dev/null || { \ + echo "ERROR: Jerboa toolchain is unavailable; set JERBUILD=/path/to/jerbuild or JERBOA_VERSION=<tag>" >&2; \ + exit 1; \ + } + +ensure-deps: + @test -d "$(JERBOA_PROTON_BRIDGE_DIR)" || { echo "ERROR: JERBOA_PROTON_BRIDGE_DIR missing: $(JERBOA_PROTON_BRIDGE_DIR)" >&2; exit 1; } + @test -d "$(JERBOA_AWS_DIR)" || { echo "ERROR: JERBOA_AWS_DIR missing: $(JERBOA_AWS_DIR)" >&2; exit 1; } + @test -d "$(JERBOA_YUBIKEY_DIR)" || { echo "ERROR: JERBOA_YUBIKEY_DIR missing: $(JERBOA_YUBIKEY_DIR)" >&2; exit 1; } + @test -d "$(JERBOA_PGP_DIR)" || { echo "ERROR: JERBOA_PGP_DIR missing: $(JERBOA_PGP_DIR)" >&2; exit 1; } + @test -d "$(JERBOA_CRYPTO_DIR)" || { echo "ERROR: JERBOA_CRYPTO_DIR missing: $(JERBOA_CRYPTO_DIR)" >&2; exit 1; } + @test -d "$(JERBOA_FUSE_DIR)" || { echo "ERROR: JERBOA_FUSE_DIR missing: $(JERBOA_FUSE_DIR)" >&2; exit 1; } + @test -d "$(JERBOA_MAIL_DIR)" || { echo "ERROR: JERBOA_MAIL_DIR missing: $(JERBOA_MAIL_DIR)" >&2; exit 1; } + @test -d "$(JERBOA_HTTPS_DIR)" || { echo "ERROR: JERBOA_HTTPS_DIR missing: $(JERBOA_HTTPS_DIR)" >&2; exit 1; } + @test -d "$(JERBOA_SSL_DIR)" || { echo "ERROR: JERBOA_SSL_DIR missing: $(JERBOA_SSL_DIR)" >&2; exit 1; } + +deps: ensure-jerboa-tools ensure-deps + @if [ ! -f "$(JERBOA_SSL_DIR)/lib/jerboa-ssl.sls" ] || [ ! -f "$(JERBOA_SSL_DIR)/jerboa_ssl_shim$(SO_EXT)" ]; then \ + $(MAKE) -C "$(JERBOA_SSL_DIR)" JERBUILD="$(JERBUILD)" build; \ + fi + @if [ ! -f "$(JERBOA_HTTPS_DIR)/lib/jerboa-https.sls" ]; then \ + $(MAKE) -C "$(JERBOA_HTTPS_DIR)" JERBUILD="$(JERBUILD)" SSL_DIR="$(JERBOA_SSL_DIR)" build; \ + fi + @if [ ! -f "$(JERBOA_CRYPTO_DIR)/lib/jerboa-crypto.sls" ] || [ ! -f "$(JERBOA_CRYPTO_DIR)/jerboa_crypto_shim.so" ]; then \ + $(MAKE) -C "$(JERBOA_CRYPTO_DIR)" JERBUILD="$(JERBUILD)" build; \ + fi + @if [ ! -f "$(JERBOA_FUSE_DIR)/lib/jerboa-fuse.sls" ] || [ ! -f "$(JERBOA_FUSE_DIR)/libjerboa_fuse_mount$(FUSE_SO_EXT)" ]; then \ + $(MAKE) -C "$(JERBOA_FUSE_DIR)" JERBUILD="$(JERBUILD)" build; \ + fi + @if [ ! -f "$(PROTON_BRIDGE_NATIVE_DIR)/libproton_bridge_native$(SO_EXT)" ]; then \ + $(MAKE) -C "$(JERBOA_PROTON_BRIDGE_DIR)" JERBUILD="$(JERBUILD)" native; \ + fi + +run: deps $(RUN_ENV) \ - $(SCHEME) -q --libdirs $(LIBDIRS) --script main.ss -- $(ARGS) + "$(JERBUILD)" exec --libdirs "$(LIBDIRS)" main.ss -- $(ARGS) doctor: $(MAKE) run ARGS='doctor' -test: +test: deps $(RUN_ENV) \ - $(SCHEME) -q --libdirs $(LIBDIRS) --script test/test-all.ss + "$(JERBUILD)" exec --libdirs "$(LIBDIRS)" test/test-all.ss -integration: +integration: deps $(RUN_ENV) \ - $(SCHEME) -q --libdirs $(LIBDIRS) --script test/integration-drive.ss + "$(JERBUILD)" exec --libdirs "$(LIBDIRS)" test/integration-drive.ss -s3-integration: +s3-integration: deps $(RUN_ENV) \ - $(SCHEME) -q --libdirs $(LIBDIRS) --script test/integration-s3.ss + "$(JERBUILD)" exec --libdirs "$(LIBDIRS)" test/integration-s3.ss -binary: - $(RUN_ENV) BINARY_LIBDIRS='$(LIBDIRS)' \ - JERBOA_CHEZ_PREFIX=$(JERBOA_HOME)/.chez \ - bash $(JERBOA_HOME)/support/build-binary.sh support/binary-entry.ss $(BINARY_OUTPUT) +binary: deps + $(RUN_ENV) "$(JERBUILD)" binary --libdirs "$(LIBDIRS)" support/binary-entry.ss $(BINARY_OUTPUT) + +binary-doctor-smoke: binary + $(RUN_ENV) ./$(BINARY_OUTPUT) doctor + +audit: deps + $(MAKE) -C "$(JERBOA_PROTON_BRIDGE_DIR)" JERBUILD="$(JERBUILD)" audit + +verify: security test doctor audit + +security: + @set -eu; \ + pattern="(BEGIN (RSA|OPENSSH|EC|DSA|PRIVATE) KEY|ghp_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,}|glpat-[A-Za-z0-9_-]{20,}|gldt-[A-Za-z0-9_-]{20,}|glrt-[A-Za-z0-9_-]{20,}|Authorization['\\\"]?[[:space:]]*[:=][[:space:]]*['\\\"]?Bearer[[:space:]]+[A-Za-z0-9_-]{20,}\\.[A-Za-z0-9_-]{20,}\\.[A-Za-z0-9_-]{20,}|PROTONDRIVE_(USERNAME|PASSWORD|VAULT_PASSWORD|ACCESS_TOKEN|REFRESH_TOKEN)=['\\\"]?[A-Za-z0-9_./+=:-]{24,}|JDRIVE_(S3_TEST_SECRET|S3_TEST_ACCESS|VAULT_PASSWORD)=['\\\"]?[A-Za-z0-9_./+=:-]{24,}|AKIA[0-9A-Z]{16}|ASIA[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; \ + echo "secret_scan_status=pass" + +dependency-evidence: deps + @set -eu; \ + status=0; \ + for item in \ + "jerboa-proton-bridge:$(JERBOA_PROTON_BRIDGE_DIR)" \ + "jerboa-yubikey:$(JERBOA_YUBIKEY_DIR)" \ + "jerboa-aws:$(JERBOA_AWS_DIR)" \ + "jerboa-pgp:$(JERBOA_PGP_DIR)" \ + "jerboa-crypto:$(JERBOA_CRYPTO_DIR)" \ + "jerboa-fuse:$(JERBOA_FUSE_DIR)" \ + "jerboa-mail:$(JERBOA_MAIL_DIR)" \ + "jerboa-https:$(JERBOA_HTTPS_DIR)" \ + "jerboa-ssl:$(JERBOA_SSL_DIR)"; do \ + label=$${item%%:*}; \ + dir=$${item#*:}; \ + echo "==> $$label dependency evidence"; \ + if awk 'BEGIN { found=0 } /^[^#[:space:]][^:]*:/ { split($$0, a, ":"); n=split(a[1], t, /[[:space:]]+/); for (i=1; i<=n; i++) if (t[i] == "release-evidence") found=1 } END { exit(found ? 0 : 1) }' "$$dir/Makefile"; then \ + env -u DIST_DIR -u RELEASE_EVIDENCE_DIR -u SBOM_DIR -u REPRO_DIR \ + $(MAKE) -C "$$dir" JERBUILD="$(JERBUILD)" release-evidence || status=1; \ + else \ + echo "$$label release-evidence target missing"; \ + status=1; \ + fi; \ + done; \ + exit "$$status" + +sbom: deps + @REPO_ROOT="$(CURDIR)" \ + DIST_DIR="$(DIST_DIR)" \ + JERBUILD="$(JERBUILD)" \ + BINARY_OUTPUT="$(BINARY_OUTPUT)" \ + JERBOA_PROTON_BRIDGE_DIR="$(JERBOA_PROTON_BRIDGE_DIR)" \ + JERBOA_YUBIKEY_DIR="$(JERBOA_YUBIKEY_DIR)" \ + JERBOA_AWS_DIR="$(JERBOA_AWS_DIR)" \ + JERBOA_PGP_DIR="$(JERBOA_PGP_DIR)" \ + JERBOA_CRYPTO_DIR="$(JERBOA_CRYPTO_DIR)" \ + JERBOA_FUSE_DIR="$(JERBOA_FUSE_DIR)" \ + JERBOA_MAIL_DIR="$(JERBOA_MAIL_DIR)" \ + JERBOA_HTTPS_DIR="$(JERBOA_HTTPS_DIR)" \ + JERBOA_SSL_DIR="$(JERBOA_SSL_DIR)" \ + sh scripts/sbom.sh + +reproducibility-report: deps + @REPO_ROOT="$(CURDIR)" \ + DIST_DIR="$(DIST_DIR)" \ + JERBUILD="$(JERBUILD)" \ + MAKE="$(MAKE)" \ + BINARY_OUTPUT="$(BINARY_OUTPUT)" \ + sh scripts/reproducibility-report.sh + +target-evidence: + @REPO_ROOT="$(CURDIR)" \ + DIST_DIR="$(DIST_DIR)" \ + TARGET_EVIDENCE_DIR="$(TARGET_EVIDENCE_DIR)" \ + sh scripts/target-evidence.sh + +release-evidence: + @REPO_ROOT="$(CURDIR)" \ + DIST_DIR="$(DIST_DIR)" \ + RELEASE_EVIDENCE_DIR="$(RELEASE_EVIDENCE_DIR)" \ + TARGET_EVIDENCE_DIR="$(TARGET_EVIDENCE_DIR)" \ + JERBUILD="$(JERBUILD)" \ + sh scripts/release-evidence.sh bundle: binary JERBOA_PROTON_BRIDGE_DIR="$(JERBOA_PROTON_BRIDGE_DIR)" \ --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ backend is S3-compatible storage with client-side encryption. Start with [docs/user-guide.md](docs/user-guide.md) for installation, local state layout, authentication, encrypted credential vault setup, listing remote files, reading and writing Drive content, FUSE mounts, configuration variables, -and troubleshooting. +and troubleshooting. See [docs/security-model.md](docs/security-model.md) for +credential, local state, FUSE, native-code, and release-gate expectations. Short installed-binary form: @@ -105,11 +106,10 @@ Current runtime code is Scheme/Jerboa only: - [protonstorage/drive/discovery.ss](protonstorage/drive/discovery.ss) - active volume/share/root resolution. - [protonstorage/drive/crypto.ss](protonstorage/drive/crypto.ss) - Drive - name hashing, key/passphrase decrypt wrappers, raw content-session-key and - block decrypt calls, content-key signature verification, and encrypted-block - SHA-256 verification, plus OpenPGP node-key generation, signed link-name - encryption, node-passphrase encrypt/sign helpers, content-key packet - generation, and content block encryption. + name hashing, key/passphrase decrypt wrappers, disabled raw + content-session-key and block decrypt calls, disabled OpenPGP write helpers, + and encrypted-block SHA-256 verification. The disabled OpenPGP/Drive crypto + paths fail closed until the Proton backend is RustSec-clean. - [protonstorage/drive/client.ss](protonstorage/drive/client.ss) - end-to-end root unlock, decrypted traversal, file read pipeline, folder-path resolution, folder creation, file upload, and revision replacement. @@ -122,7 +122,8 @@ Current runtime code is Scheme/Jerboa only: - [protonstorage/drive/write.ss](protonstorage/drive/write.ss) - create-folder and create-file request payload assembly from unlocked keys and generated Drive crypto, encrypted block upload plans, encrypted xattrs, manifest - signatures, and revision commit payloads. + signatures, and revision commit payloads. The live crypto assembly paths + currently fail closed while Proton OpenPGP/Drive crypto is disabled. - [protonstorage/s3/config.ss](protonstorage/s3/config.ss) - S3-compatible endpoint, credential, region, path-style, and object-key helpers. - [protonstorage/s3/drive.ss](protonstorage/s3/drive.ss) - encrypted S3 drive @@ -309,6 +310,7 @@ make integration make s3-integration make doctor make bundle +make release-evidence ``` `make integration` skips unless `PROTONDRIVE_INTEGRATION=1` is set. When @@ -329,6 +331,12 @@ provider multipart upload. `dist/protonstorage-<os>-<arch>.tar.gz` with the binary, native Jerboa libraries, and a wrapper that sets the runtime library search path. +Release dependency and provenance evidence is documented in +[`docs/dependency-provenance.md`](docs/dependency-provenance.md) and +[`docs/release-evidence.md`](docs/release-evidence.md). `make release-evidence` +writes the local evidence bundle under `dist/release-evidence/` and fails +closed while upstream dependency gates remain blocked. + ## Remaining Work - Prove the encrypted S3 live integration against each target provider. new file mode 100644 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,38 @@ +# Security Policy + +This repository is part of the Jerboa ecosystem. Treat it as experimental unless the README and release notes explicitly state a stronger support level. + +Repository-specific credential, local state, FUSE, native-code, and release +gate details are documented in [docs/security-model.md](docs/security-model.md). + +Production claims require the release gates tracked in `~/Release-plan.md` and `~/mine/jerboa-production-readiness.md` to be complete for this repository. + +## Supported Status + +No public production-support commitment exists yet. Security-sensitive releases must be cut from a clean checkout after: + +- `git status --short` shows only intentional release changes. +- A secret scan is clean for the working tree, and history has been reviewed or intentionally reset before first public release. +- The documented build and test commands pass. +- `make run ARGS='doctor'` passes. +- Dependency and native-code audits are clean, or accepted risks are documented. +- `make release-evidence` writes current test, audit, dependency, SBOM, and + reproducibility evidence. +- Target Drive/S3/FUSE release proof must be marker-validated. Raw Proton or + S3 account output, tokens, credentials, vault passwords, PIV PINs, plaintext + file bodies, and plaintext remote object manifests must not be preserved in + evidence. +- Any FFI, filesystem, network, shell, or credential-handling behavior is documented. + +## Security Expectations + +- Do not commit secrets, private keys, tokens, production `.env` files, operational hostnames, customer/user data, or private infrastructure details. +- Do not implement cryptography directly. Use audited libraries or delegated components. +- Prefer Rust for byte-level parsing, FFI boundaries, crypto-adjacent integration, and sandbox setup. +- Prefer Jerboa for policy, orchestration, configuration, routing, tests, and high-level behavior. +- Document file I/O, shell execution, network access, FFI ownership/lifetime rules, and credential storage before publication. +- Daemons and hostile-input parsers require threat models, fuzz harnesses, regression corpora, resource limits, sandboxing documentation, and review before public 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/bin/protonstorage +++ b/bin/protonstorage @@ -2,8 +2,22 @@ set -euo pipefail DIR="$(cd "$(dirname "$0")/.." && pwd)" -JERBOA_HOME="${JERBOA_HOME:-"$DIR/../jerboa"}" -SCHEME="${SCHEME:-"$JERBOA_HOME/.chez/bin/scheme"}" +if [ -z "${JERBUILD:-}" ]; then + if [ -x "$DIR/.jerboa/bin/jerbuild" ]; then + JERBUILD="$DIR/.jerboa/bin/jerbuild" + elif [ -x "$DIR/../jerboa/dist/jerbuild" ]; then + JERBUILD="$DIR/../jerboa/dist/jerbuild" + elif [ -x "$DIR/../jerboa/jerbuild" ]; then + JERBUILD="$DIR/../jerboa/jerbuild" + else + JERBUILD="$(command -v jerbuild || true)" + fi +fi +if [ -z "$JERBUILD" ] || [ ! -x "$JERBUILD" ]; then + echo "ERROR: jerbuild not found; set JERBUILD=/path/to/jerbuild or run make deps" >&2 + exit 1 +fi +JERBOA_HOME="${JERBOA_HOME:-"$("$JERBUILD" --jerboa-home)"}" JERBOA_PROTON_BRIDGE_DIR="${JERBOA_PROTON_BRIDGE_DIR:-"$DIR/../jerboa-proton-bridge"}" JERBOA_YUBIKEY_DIR="${JERBOA_YUBIKEY_DIR:-"$DIR/../jerboa-yubikey"}" JERBOA_PGP_DIR="${JERBOA_PGP_DIR:-"$DIR/../jerboa-pgp"}" @@ -18,11 +32,12 @@ JERBOA_NATIVE_DIR="${JERBOA_NATIVE_DIR:-"$JERBOA_HOME/jerboa-native-rs/target/re export JERBOA_HOME export JERBOA_SSL_LIB="$JERBOA_SSL_DIR" export JERBOA_CRYPTO_LIB="$JERBOA_CRYPTO_DIR" +export JERBOA_FUSE_LIB="$JERBOA_FUSE_DIR" PROTON_BRIDGE_NATIVE_DIR="$JERBOA_PROTON_BRIDGE_DIR/proton-bridge-native/target/release" export DYLD_LIBRARY_PATH="$JERBOA_NATIVE_DIR:$JERBOA_CRYPTO_DIR:$JERBOA_FUSE_DIR:$PROTON_BRIDGE_NATIVE_DIR:${DYLD_LIBRARY_PATH:-}" export DYLD_FALLBACK_LIBRARY_PATH="$JERBOA_NATIVE_DIR:${DYLD_FALLBACK_LIBRARY_PATH:-}" export LD_LIBRARY_PATH="$JERBOA_NATIVE_DIR:$JERBOA_CRYPTO_DIR:$JERBOA_FUSE_DIR:$PROTON_BRIDGE_NATIVE_DIR:${LD_LIBRARY_PATH:-}" -exec "$SCHEME" -q \ +exec "$JERBUILD" exec \ --libdirs "$DIR:$JERBOA_AWS_DIR/lib:$JERBOA_PROTON_BRIDGE_DIR:$JERBOA_YUBIKEY_DIR:$JERBOA_YUBIKEY_DIR/lib:$JERBOA_PGP_DIR:$JERBOA_CRYPTO_DIR/lib:$JERBOA_FUSE_DIR/lib:$JERBOA_MAIL_DIR:$JERBOA_HTTPS_DIR/lib:$JERBOA_SSL_DIR/lib:$JERBOA_HOME/lib" \ - --script "$DIR/main.ss" -- "$@" + "$DIR/main.ss" -- "$@" new file mode 100644 --- /dev/null +++ b/docs/dependency-provenance.md @@ -0,0 +1,28 @@ +# Dependency Provenance + +`jerboa-drive` is a credentialed storage client. Production evidence must cover +the local source tree and the sibling Jerboa libraries it delegates to: + +- `jerboa-proton-bridge` for Proton SRP/FIDO2/OpenPGP/Drive crypto helper + symbols. Its strict build keeps SRP/OpenPGP/Drive crypto disabled until + RustSec-clean backends exist. +- `jerboa-yubikey` for hardware-backed unlock/auth flows. +- `jerboa-aws` for S3-compatible storage requests. +- `jerboa-pgp`, `jerboa-crypto`, `jerboa-fuse`, `jerboa-mail`, + `jerboa-https`, and `jerboa-ssl` for crypto, filesystem, mail/HTTP, and TLS + support. + +`make dependency-evidence` runs sibling `release-evidence` targets when they +exist and fails closed when any required sibling gate fails or is missing. +Sibling evidence discovery parses Makefile target definitions instead of using +dry-run make recipes, so stale copied evidence is not accepted when a sibling +target is missing. +Current known blockers are live Proton/S3/FUSE integration evidence and +external credential/storage/FFI review. Sibling release evidence is expected +for `jerboa-proton-bridge`, `jerboa-aws`, `jerboa-pgp`, `jerboa-crypto`, +`jerboa-fuse`, `jerboa-mail`, `jerboa-https`, `jerboa-ssl`, and +`jerboa-yubikey`. + +No public production release should enable Proton SRP/OpenPGP/Drive crypto +until those backends are RustSec-clean or explicitly accepted with owner, date, +scope, and mitigation. new file mode 100644 --- /dev/null +++ b/docs/release-evidence.md @@ -0,0 +1,84 @@ +# Release Evidence + +Drive release evidence is written under `dist/release-evidence/`: + +```sh +make release-evidence +``` + +The gate records: + +- high-confidence tracked-file secret scan output, +- local smoke test output, +- `doctor` output through both `jerbuild exec` and the standalone binary, +- delegated Proton Bridge native dependency audit output, +- sibling dependency release-evidence output, +- target Drive/S3/FUSE proof status, +- SBOM/source/dependency hashes, and +- post-warm two-build binary reproducibility output. + +The target is allowed to fail after writing evidence. A failed target means the +repo is not production-releasable yet, but the evidence directory should explain +which gate blocked publication. + +Current expected blockers are live Proton/S3/FUSE integration evidence and +external credential/storage/FFI review. The former Proton Bridge and PGP +RustSec blockers were removed from the production dependency graph by disabling +strict-audit-blocked SRP/OpenPGP/Drive crypto paths. Release evidence should +record those paths as fail-closed until RustSec-clean backends exist. + +## Reproducibility + +`make reproducibility-report` performs an unmeasured warmup build and a +stabilization build before comparing two measured standalone binaries. The +release gate requires the measured binary SHA-256 values and source manifest to +match. + +The report also records generated compiler scratch input hashes from the fixed +`JERBUILD_BINARY_OBJ_DIR`. Some bundled std `.so`/`.wpo` scratch files can drift +while the final executable remains byte-identical, so +`generated_input_status` is diagnostic and is recorded with +`generated_input_policy=diagnostic-scratch-not-release-gate`. + +## Target Storage Proof + +`make target-evidence` always writes `dist/target-evidence/status.txt`. +Without a proof file, local evidence records target-only gates as +`blocked-not-run` and `target_storage_proof_status=not-run`. + +Release hosts may attach reviewed proof with: + +```sh +JDRIVE_TARGET_STORAGE_PROOF_FILE=/path/to/proof.txt make target-evidence +``` + +Production/release review can require that proof: + +```sh +JDRIVE_REQUIRE_TARGET_STORAGE_PROOF=1 \ +JDRIVE_TARGET_STORAGE_PROOF_FILE=/path/to/proof.txt \ +make release-evidence +``` + +The proof file must contain these exact marker lines and must not contain raw +Proton/S3 account output, tokens, credentials, vault passwords, PIV PINs, +plaintext file bodies, plaintext remote manifests, or plaintext object keys +derived from local filenames: + +```text +proton_live_drive_status=target-evidence-recorded +s3_encrypted_roundtrip_status=target-evidence-recorded +fuse_mount_smoke_status=target-evidence-recorded +credential_redaction_status=target-evidence-recorded +no_plaintext_remote_state_status=target-evidence-recorded +vault_permissions_status=target-evidence-recorded +proton_crypto_disabled_status=confirmed +target_platform_smoke_status=target-evidence-recorded +production_storage_review_status=current-run-recorded +raw_remote_output_status=not-preserved +sensitive_artifact_policy=no-proton-passwords-tokens-s3-keys-vault-passwords-piv-pins-plaintext-files-or-raw-account-output-preserved +``` + +Missing, empty, or marker-incomplete proof files fail closed as +`status=blocked-target-proof`. Accepted proof files are copied to +`dist/target-evidence/target-storage-proof.txt` and hashed. new file mode 100644 --- /dev/null +++ b/docs/security-model.md @@ -0,0 +1,86 @@ +# jerboa-drive Security Model + +`jerboa-drive` is a Jerboa-native encrypted storage client with two security +surfaces: + +- Proton Drive access through Proton API sessions, SRP/FIDO2 auth, OpenPGP key + material, and optional FUSE mounts. In the strict build, SRP/OpenPGP/Drive + crypto paths delegated to Proton Bridge are disabled until RustSec-clean + backends exist. +- S3-compatible storage with local drive-key wrapping and remote encrypted + manifests/objects. + +## Trust Boundaries + +- Proton credentials, refresh tokens, key pass material, S3 drive keys, vault + passwords, and YubiKey PIV PINs are secrets. +- `~/.jproton` and `~/.jdrive` are local state roots. They may contain + encrypted credential vaults, encrypted S3 drive-key vaults, daemon marker + files, cache directories, and staging directories. +- Remote S3 object keys are opaque implementation details. Plaintext paths and + file contents must not be stored in S3 object keys or unencrypted object + bodies. +- FUSE exposes decrypted filenames and file contents to local processes with + access to the mountpoint. Use OS permissions, mountpoint ownership, and an + encrypted local disk for multi-user or stolen-device threat models. + +## Credential Storage + +The Proton credential vault stores the Proton username/password and, after a +successful login, reusable session material. The S3 profile vault stores a +random drive key. Both vault formats use `scrypt` key derivation and +`chacha20-poly1305` authenticated encryption. + +The vault password is not stored. Noninteractive use should pass vault +passwords through an environment variable selected with `--vault-password-env`. +Do not commit shell profiles, CI variables, logs, or command transcripts that +contain vault passwords, Proton tokens, AWS credentials, or PIV PINs. + +## File System Safety + +Profile names are restricted to letters, digits, `.`, `-`, and `_`. Profile +marker and vault writers only write known profile-owned files. S3 sync and cp +refuse local symlinks to avoid following attacker-controlled paths during tree +uploads. + +Current daemon state is foreground-supervised. `drive daemon stop` clears local +marker files; unmounting a live FUSE mount is still delegated to the platform +unmount command. + +## Native Code And FFI + +Drive content decrypt/encrypt helpers are delegated to the +`jerboa-proton-bridge` Rust native library. The Scheme module loads native +symbols lazily, so library import remains safe in verifier and documentation +contexts. Current strict builds report SRP/OpenPGP/Drive crypto helpers as +disabled and assert fail-closed behavior before live Proton credentials or +plaintext payloads are processed. + +Release builds must audit the Proton bridge Rust dependency graph with +`cargo-audit`, run the Jerboa scanner over changed Scheme/Rust code, and record +dependency provenance for the sibling Jerboa libraries listed in +[`dependency-provenance.md`](dependency-provenance.md). + +## Network And Remote State + +Network access is explicit through Proton Drive API calls and S3-compatible +requests. API responses are treated as untrusted JSON and are validated before +key fields are used. S3 manifest updates use conditional writes against the +manifest ETag to avoid silent concurrent-writer overwrites. + +## Release Gates + +Before production release: + +- `make test` must pass from a clean checkout. +- `make run ARGS='doctor'` must pass. +- `make release-evidence` must write test, audit, dependency, SBOM, and + reproducibility evidence. +- The high-confidence secret scan and Jerboa security scanner must be clean, or + accepted exceptions must be documented with owner/date/rationale. +- `cargo-audit` must pass for the Proton bridge native dependency. +- Target Drive/S3/FUSE proof must be marker-complete and must not preserve raw + Proton/S3 output, credentials, tokens, vault passwords, PIV PINs, plaintext + files, or plaintext remote state. +- Live Proton and S3 integration tests should be run with disposable accounts + or buckets before publishing release artifacts. --- a/main.ss +++ b/main.ss @@ -1,34 +1,6 @@ #!chezscheme ;;; Script entry point for jerboa-protonstorage.