From b754d1bf68b982185ea7ebc3573254769b64812a Mon Sep 17 00:00:00 2001 From: Luke Garceau Date: Thu, 23 Apr 2026 11:58:03 -0400 Subject: [PATCH 1/5] fix - harden install-flex against injection, input and supply-chain risks Address all findings from security review: - (High/F1) Replace perl s|...|...| substitution with printf %q heredoc to eliminate Perl injection via CLONE_DIR containing | or ; characters - (High/F2) Validate all user inputs: CLONE_DIR blocked on shell metacharacters, ACCOUNT_ID enforced as exactly 12 digits, AWS_REGION validated against standard pattern; tilde expanded before use - (Medium/F4) Add --frozen-lockfile to bun install so transitive versions are pinned to bun.lock, preventing silent upgrades when BUN_CONFIG_REGISTRY bypasses Artifactory - (Medium/F5) Wrap eval + opencode invocation in a subshell so exported AWS credentials do not persist in the interactive shell after opencode exits - (Medium/F6) Guard existing-clone path: abort on uncommitted changes or wrong branch; replace git checkout with merge --ff-only; add --depth 1 to fresh clone - (Medium/F7) Replace cat >> ~/.aws/config heredoc with aws configure set calls for atomic, section-safe writes; use aws configure get for idempotency check - (Low/F8) Verify aws-cli is v2 in check_prereqs; v1 produces an upgrade hint - (Low/F9) grep -q regex on profile name replaced by aws configure get (F7) - (Low/F10) Idempotency sentinel changed from function signature to fixed header comment, guarded with grep -qF --- install-flex | 105 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 35 deletions(-) diff --git a/install-flex b/install-flex index af77cade1a28..a562d03afa6e 100755 --- a/install-flex +++ b/install-flex @@ -46,7 +46,12 @@ check_prereqs() { local missing=() command -v git >/dev/null 2>&1 || missing+=("git") command -v bun >/dev/null 2>&1 || missing+=("bun (https://bun.sh)") - command -v aws >/dev/null 2>&1 || missing+=("aws-cli v2 (https://aws.amazon.com/cli/)") + if command -v aws >/dev/null 2>&1; then + aws --version 2>&1 | grep -q '^aws-cli/2\.' \ + || missing+=("aws-cli v2 (found v1 — upgrade: https://aws.amazon.com/cli/)") + else + missing+=("aws-cli v2 (https://aws.amazon.com/cli/)") + fi if [ ${#missing[@]} -gt 0 ]; then die "Missing prerequisites:$(printf '\n • %s' "${missing[@]}")" fi @@ -62,17 +67,29 @@ gather_inputs() { printf 'Clone directory [%s]: ' "$HOME/opencode" >/dev/tty IFS= read -r CLONE_DIR (){}!^*?\\']*) + die "Invalid clone directory — shell metacharacters are not allowed" ;; + esac while true; do printf 'AWS account ID: ' >/dev/tty IFS= read -r ACCOUNT_ID /dev/tty IFS= read -r AWS_REGION /dev/null; then - warn "AWS profile [$AWS_PROFILE] already in $config — skipping" + # Use aws configure set for atomic, structured writes — avoids raw cat >> which can + # corrupt ~/.aws/config if a concurrent write leaves an unterminated section header. + # aws configure get doubles as an idempotency check without relying on fragile grep. + if aws configure get sso_start_url --profile "$AWS_PROFILE" >/dev/null 2>&1; then + warn "AWS profile [$AWS_PROFILE] already configured — skipping" return fi - info "Writing AWS SSO profile to $config..." - cat >>"$config" </dev/null; then + # Sentinel comment is the idempotency guard — more reliable than matching the + # function signature, which a user might remove while leaving a stale comment. + if grep -qF "── Flexion opencode launcher ──" "$rc_file" 2>/dev/null; then warn "opencode-work() already defined in $rc_file — skipping" return fi info "Appending opencode-work() to $rc_file..." - # Single-quoted heredoc prevents variable expansion inside the function body. - # CLONE_DIR_PLACEHOLDER is substituted after writing via perl. - cat >>"$rc_file" <<'SHELL_EOF' + # Use printf %q to shell-quote the clone path at install time. + # This eliminates the previous Perl s|...|..| substitution which was vulnerable + # to injection if CLONE_DIR contained a | character. + local clone_dir_q + clone_dir_q=$(printf '%q' "$CLONE_DIR") + + # Double-quoted heredoc: $clone_dir_q expands now (install time); + # all other $ references are \-escaped so they expand at shell run time. + cat >>"$rc_file" < Date: Thu, 23 Apr 2026 12:18:08 -0400 Subject: [PATCH 2/5] fix - address shellcheck SC1003 and SC2088 findings in install-flex --- install-flex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install-flex b/install-flex index a562d03afa6e..2063781139f4 100755 --- a/install-flex +++ b/install-flex @@ -71,7 +71,7 @@ gather_inputs() { CLONE_DIR="${CLONE_DIR/#\~/$HOME}" # Reject shell metacharacters that could corrupt the rc file case "$CLONE_DIR" in - *['|;&$`<>(){}!^*?\\']*) + *['|;&$`<>(){}!^*?'\\]*) die "Invalid clone directory — shell metacharacters are not allowed" ;; esac @@ -280,8 +280,8 @@ main() { local rc_file case "$(basename "${SHELL:-bash}")" in - zsh) rc_file="~/.zshrc" ;; - *) rc_file="~/.bashrc" ;; + zsh) rc_file="$HOME/.zshrc" ;; + *) rc_file="$HOME/.bashrc" ;; esac printf '\n%s%sInstallation complete!%s\n' "$GREEN" "$BOLD" "$NC" From 43d924e72fae5479c2f7a3ec65a1a8b38adbd305 Mon Sep 17 00:00:00 2001 From: Luke Garceau Date: Thu, 23 Apr 2026 12:36:31 -0400 Subject: [PATCH 3/5] chore: enable opp-capture pre-commit hook to catch security issues before they occur --- .husky/pre-push | 6 ++++++ .pre-commit-config.yaml | 43 ++++++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 32 +++++++++++++++++++++++++++- scripts/block-env-files.sh | 6 ++++++ 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 .pre-commit-config.yaml create mode 100755 scripts/block-env-files.sh diff --git a/.husky/pre-push b/.husky/pre-push index 5d3cc53411be..46da9c988055 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,5 +1,11 @@ #!/bin/sh set -e + +# Run pre-commit hooks (if installed) +if command -v pre-commit >/dev/null 2>&1; then + pre-commit run --hook-stage push +fi + # Check if bun version matches package.json # keep in sync with packages/script/src/index.ts semver qualifier bun -e ' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000000..876a3a8daba6 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,43 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + # Standard pre-commit hooks + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-json + - id: check-toml + - id: check-added-large-files + args: [ "--maxkb=1000" ] + - id: detect-aws-credentials + args: [ '--allow-missing-credentials' ] + + # Detect secrets with GitLeaks + - repo: https://github.com/zricethezav/gitleaks + rev: v8.30.1 + hooks: + - id: gitleaks-docker + + # Lint GitHub Actions + - repo: https://github.com/rhysd/actionlint + rev: v1.7.10 + hooks: + - id: actionlint-docker + + - repo: https://github.com/lalten/check-gha-pinning + rev: v1.3.1 + hooks: + - id: check-gha-pinning + + # Block forbidden files (.env, etc.) + - repo: local + hooks: + - id: block-env-files + name: Block .env files + entry: scripts/block-env-files.sh + language: script + pass_filenames: false + always_run: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2ae3fc6f2fb5..19e81f6b10c0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ https://github.com/anomalyco/models.dev ## Developing OpenCode -- Requirements: Bun 1.3+ +- Requirements: Bun 1.3+, Python 3.10+ (for pre-commit hooks) - Install dependencies and start the dev server from the repo root: ```bash @@ -39,6 +39,36 @@ https://github.com/anomalyco/models.dev bun dev ``` +### Setting Up Pre-Commit Hooks + +Pre-commit hooks automatically validate code before pushing. This includes: +- Secret detection (GitLeaks) +- GitHub Actions linting (actionlint, GHA pinning) +- Standard checks (trailing whitespace, JSON/YAML validation, etc.) +- `.env` file protection + +To install pre-commit: + +```bash +pip install pre-commit +# or with uv: +uv pip install pre-commit +``` + +Then install the git hooks: + +```bash +pre-commit install +``` + +This integrates pre-commit into the `pre-push` Husky hook. Hooks will run automatically when you push. To manually run: + +```bash +pre-commit run --hook-stage push # Run all hooks +pre-commit run --hook-stage push --files # Run on specific file +pre-commit run gitleaks-docker --hook-stage push # Run single hook +``` + ### Running against a different directory By default, `bun dev` runs OpenCode in the `packages/opencode` directory. To run it against a different directory or repository: diff --git a/scripts/block-env-files.sh b/scripts/block-env-files.sh new file mode 100755 index 000000000000..6a506fb221e8 --- /dev/null +++ b/scripts/block-env-files.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# Block .env files from being committed +if git diff --cached --name-only | grep -qE '^\.(env|env\..*)$'; then + echo "Error: .env files cannot be committed" + exit 1 +fi From e49a8a77045a33c739c741911f84e826ada16083 Mon Sep 17 00:00:00 2001 From: Luke Garceau Date: Thu, 23 Apr 2026 14:10:40 -0400 Subject: [PATCH 4/5] fix: reject control characters in CLONE_DIR and restrict config file permissions - Add [[:cntrl:]] check to block newlines, tabs, and other control characters from CLONE_DIR input (per PR review comment) - chmod 600 opencode.json after write so it is not world-readable (per PR review comment) --- install-flex | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/install-flex b/install-flex index 2063781139f4..e0a207f765ef 100755 --- a/install-flex +++ b/install-flex @@ -74,6 +74,10 @@ gather_inputs() { *['|;&$`<>(){}!^*?'\\]*) die "Invalid clone directory — shell metacharacters are not allowed" ;; esac + # Reject control characters (especially newlines) + if [[ "$CLONE_DIR" =~ [[:cntrl:]] ]]; then + die "Invalid clone directory — control characters are not allowed" + fi while true; do printf 'AWS account ID: ' >/dev/tty @@ -215,6 +219,7 @@ write_opencode_config() { } } EOF + chmod 600 "$config_file" success "opencode config written" } From 1a9fb349ec1ff0c432ded8fea48b5805f3558cb3 Mon Sep 17 00:00:00 2001 From: Luke Garceau Date: Thu, 23 Apr 2026 15:19:46 -0400 Subject: [PATCH 5/5] chore: migrate from pre-commit to prek for faster hook execution - Replace pre-commit with prek (Rust-based alternative) - Update CONTRIBUTING.md with prek installation instructions - Update .husky/pre-push to use prek command - Add exclude pattern for JSON5 files (tsconfig.json, .oxlintrc.json) - Remove Python 3.10+ requirement (prek is a single binary) Benefits: - Faster hook execution (Rust vs Python) - No Python runtime dependency required - Drop-in compatible with existing .pre-commit-config.yaml - Better security features (SHA validation, cooldown periods) --- .husky/pre-push | 6 +++--- .pre-commit-config.yaml | 1 + CONTRIBUTING.md | 30 ++++++++++++++++++++---------- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/.husky/pre-push b/.husky/pre-push index 46da9c988055..44cf5e8c1e1d 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,9 +1,9 @@ #!/bin/sh set -e -# Run pre-commit hooks (if installed) -if command -v pre-commit >/dev/null 2>&1; then - pre-commit run --hook-stage push +# Run prek hooks (if installed) +if command -v prek >/dev/null 2>&1; then + prek run --stage pre-push fi # Check if bun version matches package.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 876a3a8daba6..72535793a9bd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,6 +9,7 @@ repos: - id: end-of-file-fixer - id: check-yaml - id: check-json + exclude: 'tsconfig\.json$|\.oxlintrc\.json$' - id: check-toml - id: check-added-large-files args: [ "--maxkb=1000" ] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 19e81f6b10c0..fb5383184d71 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ https://github.com/anomalyco/models.dev ## Developing OpenCode -- Requirements: Bun 1.3+, Python 3.10+ (for pre-commit hooks) +- Requirements: Bun 1.3+ - Install dependencies and start the dev server from the repo root: ```bash @@ -47,26 +47,36 @@ Pre-commit hooks automatically validate code before pushing. This includes: - Standard checks (trailing whitespace, JSON/YAML validation, etc.) - `.env` file protection -To install pre-commit: +We use [prek](https://prek.j178.dev/), a fast Rust-based drop-in replacement for pre-commit. + +To install prek: ```bash -pip install pre-commit -# or with uv: -uv pip install pre-commit +# Using uv (recommended) +uv tool install prek + +# Using pip +pip install prek + +# Using Homebrew +brew install prek + +# Or via standalone installer +curl --proto '=https' --tlsv1.2 -LsSf https://github.com/j178/prek/releases/latest/download/prek-installer.sh | sh ``` Then install the git hooks: ```bash -pre-commit install +prek install ``` -This integrates pre-commit into the `pre-push` Husky hook. Hooks will run automatically when you push. To manually run: +This integrates prek into the `pre-push` Husky hook. Hooks will run automatically when you push. To manually run: ```bash -pre-commit run --hook-stage push # Run all hooks -pre-commit run --hook-stage push --files # Run on specific file -pre-commit run gitleaks-docker --hook-stage push # Run single hook +prek run --stage pre-push # Run all hooks +prek run --stage pre-push --files # Run on specific file +prek run gitleaks-docker --stage pre-push # Run single hook ``` ### Running against a different directory