-
Notifications
You must be signed in to change notification settings - Fork 56
Fix/export sandbox security #459
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| name: Dependency security audit | ||
|
|
||
| on: | ||
| pull_request: | ||
| paths: | ||
| - ".github/workflows/security-audit.yml" | ||
| - "Cargo.lock" | ||
| - "**/Cargo.lock" | ||
| - "**/package-lock.json" | ||
| - "**/bun.lock" | ||
| - "**/uv.lock" | ||
| - "**/requirements*.lock" | ||
| - "pyproject.toml" | ||
| - "Cargo.toml" | ||
| - "**/Cargo.toml" | ||
| - "package.json" | ||
| - "**/package.json" | ||
| - "docs/app/pyproject.toml" | ||
| - "docs/app/reflex.lock/package.json" | ||
| - "benchmarks/requirements*.txt" | ||
| - "scripts/verify_dependency_lock_inventory.py" | ||
| schedule: | ||
| - cron: "17 3 * * 1" | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| audit: | ||
| name: Audit committed dependency locks | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
|
harsh21234i marked this conversation as resolved.
|
||
| with: | ||
| fetch-depth: 1 | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| persist-credentials: false | ||
| - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable | ||
| - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 | ||
| with: | ||
| enable-cache: false | ||
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | ||
| with: | ||
| node-version: "22" | ||
|
|
||
| - name: Verify audit inventory | ||
| run: python3 scripts/verify_dependency_lock_inventory.py | ||
|
|
||
| - name: Audit root Python environment | ||
| run: | | ||
| uv sync --locked --all-extras --all-groups | ||
| uv run --with pip-audit pip-audit --progress-spinner off | ||
|
|
||
| - name: Audit documentation Python environment | ||
| run: | | ||
| uv sync --project docs/app --frozen --group dev | ||
| uv run --project docs/app --no-sync --with pip-audit pip-audit --progress-spinner off | ||
|
|
||
| - name: Audit benchmark Python lock | ||
| run: uvx --from pip-audit pip-audit -r benchmarks/requirements-ci.lock --progress-spinner off | ||
|
|
||
| - name: Audit Rust locks | ||
| run: | | ||
| cargo install cargo-audit --locked | ||
| cargo audit --locked | ||
| cargo audit --file examples/osm/osmium-rs/Cargo.lock | ||
|
|
||
| - name: Audit npm lock | ||
| run: npm audit --package-lock-only --audit-level=high | ||
|
|
||
| - name: Audit Bun lock | ||
| run: | | ||
| npm install --global bun@1.2.20 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
file=".github/workflows/security-audit.yml"
printf '%s\n' '--- workflow excerpt ---'
sed -n '1,110p' "$file"
printf '%s\n' '--- related workflow references ---'
rg -n -C 2 'bun|npm install|audit|workflow_dispatch|pull_request|push|permissions|runs-on' .github/workflows "$file" 2>/dev/null || trueRepository: reflex-dev/xy Length of output: 43325 🏁 Script executed: #!/bin/bash
set -eu
python3 - <<'PY'
import json, urllib.request
url = "https://registry.npmjs.org/bun/1.2.20"
with urllib.request.urlopen(url, timeout=15) as r:
data = json.load(r)
print("name:", data.get("name"))
print("version:", data.get("version"))
print("scripts:", json.dumps(data.get("scripts", {}), sort_keys=True))
print("dist:", json.dumps({
k: data.get("dist", {}).get(k)
for k in ("tarball", "integrity", "shasum")
}, sort_keys=True))
PYRepository: reflex-dev/xy Length of output: 2437 🌐 Web query:
💡 Result: In Bun, including version 1.2.20, the handling of package.json scripts like postinstall is distinct from npm due to a "default-secure" security model [1][2][3]. 1. Default Security Behavior: Bun does not execute arbitrary lifecycle scripts (such as postinstall, preinstall, or prepare) for installed dependencies by default [1][2][3]. This is done to mitigate the security risks associated with running unknown code during package installation [1][2][3]. 2. Opt-in via trustedDependencies: To run lifecycle scripts for specific packages, you must explicitly allow them by adding the package name to the trustedDependencies array in your root package.json [1][2][3]. Once added, Bun will execute the lifecycle scripts for those packages upon installation [1][2][3]. 3. Project-Level Scripts: Bun does execute lifecycle scripts defined in your own project's package.json at the appropriate time when you run commands like bun install [1][4]. 4. Integrity and dist: Bun verifies the integrity of downloaded packages [1][4]. Recent updates, including those relevant to the 1.2.x series, have improved how Bun handles and stores integrity hashes (such as sha512) in the lockfile for various dependency types, including remote tarballs, to ensure consistency and security [5]. 5. Dist/Optimization: Bun automatically optimizes postinstall scripts for certain popular packages (e.g., esbuild, sharp) by determining if they are necessary, though this behavior can be configured or disabled using specific environment variables like BUN_FEATURE_FLAG_DISABLE_NATIVE_DEPENDENCY_LINKER [1][4]. For your specific scenario, if you are expecting a postinstall script to run for a dependency and it is not, ensure the package is listed in your package.json's trustedDependencies [1][2]. Integrity verification is handled automatically by Bun and is recorded in the lockfile to maintain security [1][4][5]. Citations:
🏁 Script executed: #!/bin/bash
set -eu
json="$(mktemp)"
trap 'rm -f "$json"' EXIT
curl -fsSLk --max-time 20 'https://registry.npmjs.org/bun/1.2.20' -o "$json"
python3 - "$json" <<'PY'
import json, sys
with open(sys.argv[1]) as f:
data = json.load(f)
print("name:", data.get("name"))
print("version:", data.get("version"))
print("scripts:", json.dumps(data.get("scripts", {}), sort_keys=True))
print("dist:", json.dumps({
k: data.get("dist", {}).get(k)
for k in ("tarball", "integrity", "shasum")
}, sort_keys=True))
PYRepository: reflex-dev/xy Length of output: 451 🏁 Script executed: #!/bin/bash
set -eu
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
curl -fsSLk --max-time 30 'https://registry.npmjs.org/bun/-/bun-1.2.20.tgz' -o "$tmp/bun.tgz"
tar -tzf "$tmp/bun.tgz" | sed -n '1,80p'
tar -xzf "$tmp/bun.tgz" -C "$tmp"
printf '%s\n' '--- package manifest ---'
cat "$tmp/package/package.json"
printf '%s\n' '--- install.js (first 240 lines) ---'
sed -n '1,240p' "$tmp/package/install.js"Repository: reflex-dev/xy Length of output: 9695 🏁 Script executed: #!/bin/bash
set -eu
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
curl -fsSLk --max-time 30 'https://registry.npmjs.org/bun/-/bun-1.2.20.tgz' -o "$tmp/bun.tgz"
tar -xzf "$tmp/bun.tgz" -C "$tmp"
script="$tmp/package/install.js"
printf '%s\n' '--- installer security-relevant references ---'
rg -n -i 'sha|hash|checksum|integrity|download|fetch|exec|spawn|child_process|https?://|optional|binary|rename|chmod|writeFile|bun' "$script"
printf '%s\n' '--- installer tail ---'
tail -n 180 "$script"Repository: reflex-dev/xy Length of output: 10718 Security Misconfiguration (CWE-494): Download of Code Without Integrity Check Reachability: External · Exploitability: Difficult Use an integrity-pinned Bun bootstrap.
🧰 Tools🪛 zizmor (1.29.0)[warning] 65-65: ad-hoc installation of packages (adhoc-packages): installs a package outside of a lockfile (adhoc-packages) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
| cd docs/app/reflex.lock | ||
| bun install --frozen-lockfile | ||
| bun audit --audit-level=high | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| #!/usr/bin/env python3 | ||
| """Keep the dependency-audit lockfile inventory explicit and complete.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import subprocess | ||
| import sys | ||
| from pathlib import Path | ||
|
|
||
| ROOT = Path(__file__).resolve().parents[1] | ||
| EXPECTED_LOCKFILES = frozenset( | ||
| { | ||
| "Cargo.lock", | ||
| "examples/osm/osmium-rs/Cargo.lock", | ||
| "package-lock.json", | ||
| "uv.lock", | ||
| "docs/app/uv.lock", | ||
| "docs/app/reflex.lock/bun.lock", | ||
| "benchmarks/requirements-ci.lock", | ||
| } | ||
| ) | ||
| EXCLUDED_PATH_PARTS = frozenset({"launch_baselines"}) | ||
|
|
||
|
|
||
| def _is_lockfile(path: Path) -> bool: | ||
| return path.name in {"Cargo.lock", "package-lock.json", "bun.lock", "uv.lock"} or ( | ||
| path.name.startswith("requirements") and path.name.endswith(".lock") | ||
| ) | ||
|
|
||
|
|
||
| def find_dependency_lockfiles(root: Path = ROOT) -> frozenset[str]: | ||
| """Return committed dependency lockfiles, excluding local generated files.""" | ||
| try: | ||
| result = subprocess.run( | ||
| ["git", "-C", str(root), "ls-files", "-z"], | ||
| check=True, | ||
| capture_output=True, | ||
| ) | ||
| except (OSError, subprocess.CalledProcessError) as exc: | ||
| raise RuntimeError("dependency lock inventory requires a git checkout and git") from exc | ||
| return frozenset( | ||
| path | ||
| for raw_path in result.stdout.split(b"\0") | ||
| if raw_path | ||
| for path in (raw_path.decode("utf-8", "surrogateescape"),) | ||
| if not any(part in EXCLUDED_PATH_PARTS for part in Path(path).parts) | ||
| if _is_lockfile(Path(path)) | ||
| ) | ||
|
|
||
|
|
||
| def main() -> int: | ||
| try: | ||
| actual = find_dependency_lockfiles() | ||
| except RuntimeError as exc: | ||
| print(f"dependency lockfile inventory failed: {exc}", file=sys.stderr) | ||
| return 1 | ||
| missing = sorted(EXPECTED_LOCKFILES - actual) | ||
| unexpected = sorted(actual - EXPECTED_LOCKFILES) | ||
| if missing or unexpected: | ||
| if missing: | ||
| print(f"missing expected dependency lockfiles: {missing}", file=sys.stderr) | ||
| if unexpected: | ||
| print( | ||
| "dependency lockfiles missing from the audit inventory: " | ||
| f"{unexpected}", | ||
| file=sys.stderr, | ||
| ) | ||
| return 1 | ||
| print(f"dependency lockfile inventory OK ({len(actual)} files)") | ||
| return 0 | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| raise SystemExit(main()) |
Uh oh!
There was an error while loading. Please reload this page.