Rename the `rails/` directory to `tools/` for agent discoverability — agents frequently failed to locate helper scripts due to the non-intuitive directory name. Add backward-compat symlink `rails/ → tools/`. New tool suites: - Authentik: auth-token, user-list, user-create, group-list, app-list, flow-list, admin-status (8 scripts) - Coolify: team-list, project-list, service-list, service-status, deploy, env-set (7 scripts) - Woodpecker: pipeline-list, pipeline-status, pipeline-trigger (3 stubs) - GLPI: session-init, computer-list, ticket-list, ticket-create, user-list (6 scripts) - Health: stack-health.sh — stack-wide connectivity check Infrastructure: - Shared credential loader at tools/_lib/credentials.sh - install.sh creates symlink + chmod on tool scripts - All ~253 rails/ path references updated across 68+ files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
236 lines
7.0 KiB
Bash
Executable File
236 lines
7.0 KiB
Bash
Executable File
#!/bin/bash
|
|
# codex-security-review.sh - Run an AI-powered security vulnerability review using Codex CLI
|
|
# Usage: codex-security-review.sh [OPTIONS]
|
|
#
|
|
# Runs codex exec in read-only sandbox mode with a security-focused review prompt.
|
|
# Outputs findings as JSON and optionally posts them to a PR.
|
|
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "$SCRIPT_DIR/common.sh"
|
|
|
|
# Defaults
|
|
PR_NUMBER=""
|
|
BASE_BRANCH="main"
|
|
COMMIT_SHA=""
|
|
OUTPUT_FILE=""
|
|
POST_TO_PR=false
|
|
UNCOMMITTED=false
|
|
REVIEW_MODE=""
|
|
|
|
show_help() {
|
|
cat <<'EOF'
|
|
Usage: codex-security-review.sh [OPTIONS]
|
|
|
|
Run an AI-powered security vulnerability review using OpenAI Codex CLI.
|
|
|
|
Options:
|
|
-n, --pr <number> PR number (auto-enables posting findings to PR)
|
|
-b, --base <branch> Base branch to diff against (default: main)
|
|
-c, --commit <sha> Review a specific commit
|
|
-o, --output <path> Write JSON results to file
|
|
--post-to-pr Post findings as PR comment (requires -n)
|
|
--uncommitted Review uncommitted changes (staged + unstaged + untracked)
|
|
-h, --help Show this help
|
|
|
|
Examples:
|
|
# Security review uncommitted changes
|
|
codex-security-review.sh --uncommitted
|
|
|
|
# Security review a PR and post findings
|
|
codex-security-review.sh -n 42
|
|
|
|
# Security review against main, save JSON
|
|
codex-security-review.sh -b main -o security.json
|
|
|
|
# Security review a specific commit
|
|
codex-security-review.sh -c abc123f
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
# Parse arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-n|--pr)
|
|
PR_NUMBER="$2"
|
|
POST_TO_PR=true
|
|
REVIEW_MODE="pr"
|
|
shift 2
|
|
;;
|
|
-b|--base)
|
|
BASE_BRANCH="$2"
|
|
REVIEW_MODE="base"
|
|
shift 2
|
|
;;
|
|
-c|--commit)
|
|
COMMIT_SHA="$2"
|
|
REVIEW_MODE="commit"
|
|
shift 2
|
|
;;
|
|
-o|--output)
|
|
OUTPUT_FILE="$2"
|
|
shift 2
|
|
;;
|
|
--post-to-pr)
|
|
POST_TO_PR=true
|
|
shift
|
|
;;
|
|
--uncommitted)
|
|
UNCOMMITTED=true
|
|
REVIEW_MODE="uncommitted"
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
show_help
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1" >&2
|
|
echo "Run with --help for usage" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Validate
|
|
if [[ -z "$REVIEW_MODE" ]]; then
|
|
echo "Error: Specify a review mode: --uncommitted, --base <branch>, --commit <sha>, or --pr <number>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$POST_TO_PR" == true && -z "$PR_NUMBER" ]]; then
|
|
echo "Error: --post-to-pr requires -n <pr_number>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
check_codex
|
|
check_jq
|
|
|
|
# Verify we're in a git repo
|
|
if ! git rev-parse --is-inside-work-tree &>/dev/null; then
|
|
echo "Error: Not inside a git repository" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Get the diff context
|
|
echo "Gathering diff context..." >&2
|
|
case "$REVIEW_MODE" in
|
|
uncommitted) DIFF_CONTEXT=$(build_diff_context "uncommitted" "") ;;
|
|
base) DIFF_CONTEXT=$(build_diff_context "base" "$BASE_BRANCH") ;;
|
|
commit) DIFF_CONTEXT=$(build_diff_context "commit" "$COMMIT_SHA") ;;
|
|
pr) DIFF_CONTEXT=$(build_diff_context "pr" "$PR_NUMBER") ;;
|
|
esac
|
|
|
|
if [[ -z "$DIFF_CONTEXT" ]]; then
|
|
echo "No changes found to review." >&2
|
|
exit 0
|
|
fi
|
|
|
|
# Build the security review prompt
|
|
REVIEW_PROMPT=$(cat <<'PROMPT'
|
|
You are an expert application security engineer performing a security-focused code review.
|
|
Your goal is to identify vulnerabilities, security anti-patterns, and data exposure risks.
|
|
|
|
## Security Review Scope
|
|
|
|
### OWASP Top 10 (2021)
|
|
- A01: Broken Access Control — missing authorization checks, IDOR, privilege escalation
|
|
- A02: Cryptographic Failures — weak algorithms, plaintext secrets, missing encryption
|
|
- A03: Injection — SQL, NoSQL, OS command, LDAP, XPath injection
|
|
- A04: Insecure Design — missing threat modeling, unsafe business logic
|
|
- A05: Security Misconfiguration — debug mode, default credentials, unnecessary features
|
|
- A06: Vulnerable Components — known CVEs in dependencies
|
|
- A07: Authentication Failures — weak auth, missing MFA, session issues
|
|
- A08: Data Integrity Failures — deserialization, unsigned updates
|
|
- A09: Logging Failures — sensitive data in logs, missing audit trails
|
|
- A10: SSRF — unvalidated URLs, internal service access
|
|
|
|
### Additional Checks
|
|
- Hardcoded secrets, API keys, tokens, passwords
|
|
- Insecure direct object references
|
|
- Missing input validation at trust boundaries
|
|
- Cross-Site Scripting (XSS) — reflected, stored, DOM-based
|
|
- Cross-Site Request Forgery (CSRF) protection
|
|
- Insecure file handling (path traversal, unrestricted upload)
|
|
- Race conditions and TOCTOU vulnerabilities
|
|
- Information disclosure (stack traces, verbose errors)
|
|
- Supply chain risks (typosquatting, dependency confusion)
|
|
|
|
## Severity Guide
|
|
- **critical**: Exploitable vulnerability with immediate impact (RCE, auth bypass, data breach)
|
|
- **high**: Significant vulnerability requiring prompt fix (injection, XSS, secrets exposure)
|
|
- **medium**: Vulnerability with limited exploitability or impact (missing headers, weak config)
|
|
- **low**: Minor security concern or hardening opportunity (informational, defense-in-depth)
|
|
|
|
## Rules
|
|
- Include CWE IDs when applicable
|
|
- Include OWASP category when applicable
|
|
- Provide specific remediation steps for every finding
|
|
- Only report findings you are confident about
|
|
- Do NOT flag non-security code quality issues
|
|
- If no security issues found, say so clearly
|
|
|
|
PROMPT
|
|
)
|
|
|
|
# Set up temp files for output and diff
|
|
TEMP_OUTPUT=$(mktemp /tmp/codex-security-XXXXXX.json)
|
|
TEMP_DIFF=$(mktemp /tmp/codex-diff-XXXXXX.txt)
|
|
trap 'rm -f "$TEMP_OUTPUT" "$TEMP_DIFF"' EXIT
|
|
|
|
SCHEMA_FILE="$SCRIPT_DIR/schemas/security-review-schema.json"
|
|
|
|
# Write diff to temp file
|
|
echo "$DIFF_CONTEXT" > "$TEMP_DIFF"
|
|
|
|
echo "Running Codex security review..." >&2
|
|
echo " Diff size: $(wc -l < "$TEMP_DIFF") lines" >&2
|
|
|
|
# Build full prompt with diff reference
|
|
FULL_PROMPT="${REVIEW_PROMPT}
|
|
|
|
Here are the code changes to security review:
|
|
|
|
\`\`\`diff
|
|
$(cat "$TEMP_DIFF")
|
|
\`\`\`"
|
|
|
|
# Run codex exec with prompt from stdin to avoid arg length limits
|
|
echo "$FULL_PROMPT" | codex exec \
|
|
--sandbox read-only \
|
|
--output-schema "$SCHEMA_FILE" \
|
|
-o "$TEMP_OUTPUT" \
|
|
- 2>&1 | while IFS= read -r line; do
|
|
echo " [codex] $line" >&2
|
|
done
|
|
|
|
# Check output was produced
|
|
if [[ ! -s "$TEMP_OUTPUT" ]]; then
|
|
echo "Error: Codex produced no output" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Validate JSON
|
|
if ! jq empty "$TEMP_OUTPUT" 2>/dev/null; then
|
|
echo "Error: Codex output is not valid JSON" >&2
|
|
cat "$TEMP_OUTPUT" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Save output if requested
|
|
if [[ -n "$OUTPUT_FILE" ]]; then
|
|
cp "$TEMP_OUTPUT" "$OUTPUT_FILE"
|
|
echo "Results saved to: $OUTPUT_FILE" >&2
|
|
fi
|
|
|
|
# Post to PR if requested
|
|
if [[ "$POST_TO_PR" == true && -n "$PR_NUMBER" ]]; then
|
|
echo "Posting findings to PR #$PR_NUMBER..." >&2
|
|
post_to_pr "$PR_NUMBER" "$TEMP_OUTPUT" "security"
|
|
echo "Posted security review to PR #$PR_NUMBER" >&2
|
|
fi
|
|
|
|
# Always print results to stdout
|
|
print_results "$TEMP_OUTPUT" "security"
|