Skip to content

fix: harden install-flex against injection, input validation, and supply-chain risks#12

Open
lgarceau768 wants to merge 5 commits intoflexfrom
security/ensure-install-safe
Open

fix: harden install-flex against injection, input validation, and supply-chain risks#12
lgarceau768 wants to merge 5 commits intoflexfrom
security/ensure-install-safe

Conversation

@lgarceau768
Copy link
Copy Markdown
Collaborator

Issue for this PR

Closes #

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Addresses 10 findings from a security review of the install-flex installer script. All changes are contained to that single file.

High severity (2 fixed)

  • F1 — Perl injection: The previous approach wrote a CLONE_DIR_PLACEHOLDER with a single-quoted heredoc, then substituted the real path using perl -i -pe "s|...|${CLONE_DIR}|g". A clone directory containing | would terminate the Perl s/// delimiter and allow arbitrary Perl execution against ~/.zshrc / ~/.bashrc. Fixed by switching to a double-quoted heredoc where $(printf '%q' "$CLONE_DIR") shell-quotes the path at install time — no Perl involved.
  • F2 — No input validation: CLONE_DIR, ACCOUNT_ID, and AWS_REGION were accepted without any checks. Added: shell-metacharacter denylist on CLONE_DIR (plus ~ expansion), 12-digit enforcement on ACCOUNT_ID with re-prompt on failure, and region-format regex on AWS_REGION.

Medium severity (4 fixed)

  • F4: Added --frozen-lockfile to bun install so bun.lock versions are pinned and can't silently drift when BUN_CONFIG_REGISTRY bypasses Artifactory.
  • F5: Wrapped the eval "$(aws configure export-credentials ...)" + binary invocation in a subshell so exported credentials are scoped to the opencode process and don't persist in the interactive shell.
  • F6: Before fetching an existing clone, the script now aborts if there are uncommitted changes or if HEAD is not on the flex branch. git checkout replaced with merge --ff-only. Fresh clones get --depth 1.
  • F7: Replaced cat >> ~/.aws/config heredoc (non-atomic, fragile) with five aws configure set calls. Idempotency check changed from a grep regex to aws configure get.

Low severity (3 fixed)

  • F8: check_prereqs now verifies aws --version reports aws-cli/2.*; v1 produces an upgrade link instead of a cryptic failure later.
  • F9: Resolved as a side-effect of F7 — the grep -q "\[profile ...]" regex is gone entirely.
  • F10: Idempotency sentinel changed from matching "opencode-work()" (which a stale comment could satisfy) to grep -qF "── Flexion opencode launcher ──".

How did you verify your code works?

  • bash -n install-flex — syntax check passes clean.
  • Manually traced the heredoc escaping: $clone_dir_q expands at install time; all other $ references in the generated function body are \-escaped and expand correctly at runtime.
  • Confirmed the aws configure set idempotency path by checking that aws configure get sso_start_url --profile ClaudeCodeAccess exits non-zero on a fresh config and zero on an existing one.

Screenshots / recordings

N/A — installer script only.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

…isks

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
@github-actions
Copy link
Copy Markdown

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • No issue referenced. Please add Closes #<number> linking to the relevant issue.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@github-actions
Copy link
Copy Markdown

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@lgarceau768 lgarceau768 self-assigned this Apr 23, 2026
Copy link
Copy Markdown

@tdonaworth tdonaworth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments in-line.

Comment thread install-flex
CLONE_DIR="${CLONE_DIR:-$HOME/opencode}"
# Expand leading ~ to $HOME before validation and use
CLONE_DIR="${CLONE_DIR/#\~/$HOME}"
# Reject shell metacharacters that could corrupt the rc file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also add in rejection for control characters (newlines, tabs, etc)

# Reject control characters (especially newlines)
if [[ "$CLONE_DIR" =~ [[:cntrl:]] ]]; then
  die "Invalid clone directory — control characters are not allowed"
fi

Comment thread install-flex
}
}
}
EOF
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

current file is world readable as it's written out, can update file permissions immediately:

chmod 600 "$config_file"

or 644 if not as paranoid.

…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)
- 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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants