centralize guides and rails under mosaic with runtime compatibility links
This commit is contained in:
265
rails/codex/README.md
Normal file
265
rails/codex/README.md
Normal file
@@ -0,0 +1,265 @@
|
||||
# Codex CLI Review Scripts
|
||||
|
||||
AI-powered code review and security review scripts using OpenAI's Codex CLI.
|
||||
|
||||
These scripts provide **independent** code analysis separate from Claude sessions, giving you a second AI perspective on code changes to catch issues that might be missed.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
# Install Codex CLI
|
||||
npm i -g @openai/codex
|
||||
|
||||
# Verify installation
|
||||
codex --version
|
||||
|
||||
# Authenticate (first run)
|
||||
codex # Will prompt for ChatGPT account or API key
|
||||
|
||||
# Verify jq is installed (for JSON processing)
|
||||
jq --version
|
||||
```
|
||||
|
||||
## Scripts
|
||||
|
||||
### `codex-code-review.sh`
|
||||
General code quality review focusing on:
|
||||
- **Correctness** — logic errors, edge cases, error handling
|
||||
- **Code Quality** — complexity, duplication, naming, dead code
|
||||
- **Testing** — coverage, test quality
|
||||
- **Performance** — N+1 queries, blocking operations, resource cleanup
|
||||
- **Dependencies** — deprecated packages
|
||||
- **Documentation** — comments, public API docs
|
||||
|
||||
**Output:** Structured JSON with findings categorized as `blocker`, `should-fix`, or `suggestion`.
|
||||
|
||||
### `codex-security-review.sh`
|
||||
Security vulnerability review focusing on:
|
||||
- **OWASP Top 10** — injection, broken auth, XSS, CSRF, SSRF, etc.
|
||||
- **Secrets Detection** — hardcoded credentials, API keys, tokens
|
||||
- **Injection Flaws** — SQL, NoSQL, OS command, LDAP
|
||||
- **Auth/Authz Gaps** — missing checks, privilege escalation, IDOR
|
||||
- **Data Exposure** — logging sensitive data, information disclosure
|
||||
- **Supply Chain** — vulnerable dependencies, typosquatting
|
||||
|
||||
**Output:** Structured JSON with findings categorized as `critical`, `high`, `medium`, or `low` with CWE IDs and OWASP categories.
|
||||
|
||||
## Usage
|
||||
|
||||
### Review Uncommitted Changes
|
||||
|
||||
```bash
|
||||
# Code review
|
||||
~/.mosaic/rails/codex/codex-code-review.sh --uncommitted
|
||||
|
||||
# Security review
|
||||
~/.mosaic/rails/codex/codex-security-review.sh --uncommitted
|
||||
```
|
||||
|
||||
### Review a Pull Request
|
||||
|
||||
```bash
|
||||
# Review and post findings as a PR comment
|
||||
~/.mosaic/rails/codex/codex-code-review.sh -n 42
|
||||
|
||||
# Security review and post to PR
|
||||
~/.mosaic/rails/codex/codex-security-review.sh -n 42
|
||||
```
|
||||
|
||||
### Review Against Base Branch
|
||||
|
||||
```bash
|
||||
# Code review changes vs main
|
||||
~/.mosaic/rails/codex/codex-code-review.sh -b main
|
||||
|
||||
# Security review changes vs develop
|
||||
~/.mosaic/rails/codex/codex-security-review.sh -b develop
|
||||
```
|
||||
|
||||
### Review a Specific Commit
|
||||
|
||||
```bash
|
||||
~/.mosaic/rails/codex/codex-code-review.sh -c abc123f
|
||||
~/.mosaic/rails/codex/codex-security-review.sh -c abc123f
|
||||
```
|
||||
|
||||
### Save Results to File
|
||||
|
||||
```bash
|
||||
# Save JSON output
|
||||
~/.mosaic/rails/codex/codex-code-review.sh --uncommitted -o review-results.json
|
||||
~/.mosaic/rails/codex/codex-security-review.sh --uncommitted -o security-results.json
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
Both scripts support the same options:
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `-n, --pr <number>` | PR number (auto-enables posting 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 help |
|
||||
|
||||
## Woodpecker CI Integration
|
||||
|
||||
Automated PR reviews in CI pipelines.
|
||||
|
||||
### Setup
|
||||
|
||||
1. **Copy the pipeline template to your repo:**
|
||||
```bash
|
||||
cp ~/.mosaic/rails/codex/woodpecker/codex-review.yml your-repo/.woodpecker/
|
||||
```
|
||||
|
||||
2. **Copy the schemas directory:**
|
||||
```bash
|
||||
cp -r ~/.mosaic/rails/codex/schemas your-repo/.woodpecker/
|
||||
```
|
||||
|
||||
3. **Add Codex API key to Woodpecker:**
|
||||
- Go to your repo in Woodpecker CI
|
||||
- Settings → Secrets
|
||||
- Add secret: `codex_api_key` with your OpenAI API key
|
||||
|
||||
4. **Commit and push:**
|
||||
```bash
|
||||
cd your-repo
|
||||
git add .woodpecker/
|
||||
git commit -m "feat: Add Codex AI review pipeline"
|
||||
git push
|
||||
```
|
||||
|
||||
### Pipeline Behavior
|
||||
|
||||
- **Triggers on:** Pull requests
|
||||
- **Runs:** Code review + Security review in parallel
|
||||
- **Fails if:**
|
||||
- Code review finds blockers
|
||||
- Security review finds critical or high severity issues
|
||||
- **Outputs:** Structured JSON results in CI logs
|
||||
|
||||
## Output Format
|
||||
|
||||
### Code Review JSON
|
||||
|
||||
```json
|
||||
{
|
||||
"summary": "Overall assessment...",
|
||||
"verdict": "approve|request-changes|comment",
|
||||
"confidence": 0.85,
|
||||
"findings": [
|
||||
{
|
||||
"severity": "blocker",
|
||||
"title": "SQL injection vulnerability",
|
||||
"file": "src/api/users.ts",
|
||||
"line_start": 42,
|
||||
"line_end": 45,
|
||||
"description": "User input directly interpolated into SQL query",
|
||||
"suggestion": "Use parameterized queries"
|
||||
}
|
||||
],
|
||||
"stats": {
|
||||
"files_reviewed": 5,
|
||||
"blockers": 1,
|
||||
"should_fix": 3,
|
||||
"suggestions": 8
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Security Review JSON
|
||||
|
||||
```json
|
||||
{
|
||||
"summary": "Security assessment...",
|
||||
"risk_level": "high",
|
||||
"confidence": 0.90,
|
||||
"findings": [
|
||||
{
|
||||
"severity": "high",
|
||||
"title": "Hardcoded API key",
|
||||
"file": "src/config.ts",
|
||||
"line_start": 10,
|
||||
"description": "API key hardcoded in source",
|
||||
"cwe_id": "CWE-798",
|
||||
"owasp_category": "A02:2021-Cryptographic Failures",
|
||||
"remediation": "Move to environment variables or secrets manager"
|
||||
}
|
||||
],
|
||||
"stats": {
|
||||
"files_reviewed": 5,
|
||||
"critical": 0,
|
||||
"high": 1,
|
||||
"medium": 2,
|
||||
"low": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Platform Support
|
||||
|
||||
Works with both **GitHub** and **Gitea** via the shared `~/.mosaic/rails/git/` infrastructure:
|
||||
- Auto-detects platform from git remote
|
||||
- Posts PR comments using `gh` (GitHub) or `tea` (Gitea)
|
||||
- Unified interface across both platforms
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
codex-code-review.sh
|
||||
codex-security-review.sh
|
||||
↓
|
||||
common.sh
|
||||
↓ sources
|
||||
../git/detect-platform.sh (platform detection)
|
||||
../git/pr-review.sh (post PR comments)
|
||||
↓ uses
|
||||
gh (GitHub) or tea (Gitea)
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "codex: command not found"
|
||||
```bash
|
||||
npm i -g @openai/codex
|
||||
```
|
||||
|
||||
### "jq: command not found"
|
||||
```bash
|
||||
# Arch Linux
|
||||
sudo pacman -S jq
|
||||
|
||||
# Debian/Ubuntu
|
||||
sudo apt install jq
|
||||
```
|
||||
|
||||
### "Error: Not inside a git repository"
|
||||
Run the script from inside a git repository.
|
||||
|
||||
### "No changes found to review"
|
||||
The specified mode (--uncommitted, --base, etc.) found no changes to review.
|
||||
|
||||
### "Codex produced no output"
|
||||
Check your Codex API key and authentication:
|
||||
```bash
|
||||
codex # Re-authenticate if needed
|
||||
```
|
||||
|
||||
## Model Configuration
|
||||
|
||||
By default, scripts use the model configured in `~/.codex/config.toml`:
|
||||
- **Model:** `gpt-5.3-codex` (recommended for code review)
|
||||
- **Reasoning effort:** `high`
|
||||
|
||||
For best results, use `gpt-5.2-codex` or newer for strongest review accuracy.
|
||||
|
||||
## See Also
|
||||
|
||||
- `~/.mosaic/guides/code-review.md` — Manual code review checklist
|
||||
- `~/.mosaic/rails/git/` — Git helper scripts (issue/PR management)
|
||||
- OpenAI Codex CLI docs: https://developers.openai.com/codex/cli/
|
||||
238
rails/codex/codex-code-review.sh
Executable file
238
rails/codex/codex-code-review.sh
Executable file
@@ -0,0 +1,238 @@
|
||||
#!/bin/bash
|
||||
# codex-code-review.sh - Run an AI-powered code quality review using Codex CLI
|
||||
# Usage: codex-code-review.sh [OPTIONS]
|
||||
#
|
||||
# Runs codex exec in read-only sandbox mode with a structured code 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-code-review.sh [OPTIONS]
|
||||
|
||||
Run an AI-powered code quality 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:
|
||||
# Review uncommitted changes
|
||||
codex-code-review.sh --uncommitted
|
||||
|
||||
# Review a PR and post findings as a comment
|
||||
codex-code-review.sh -n 42
|
||||
|
||||
# Review changes against main, save JSON
|
||||
codex-code-review.sh -b main -o review.json
|
||||
|
||||
# Review a specific commit
|
||||
codex-code-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 review prompt
|
||||
REVIEW_PROMPT=$(cat <<'PROMPT'
|
||||
You are an expert code reviewer. Review the following code changes thoroughly.
|
||||
|
||||
Focus on issues that are ACTIONABLE and IMPORTANT. Do not flag trivial style issues.
|
||||
|
||||
## Review Checklist
|
||||
|
||||
### Correctness
|
||||
- Code does what it claims to do
|
||||
- Edge cases are handled
|
||||
- Error conditions are managed properly
|
||||
- No obvious bugs or logic errors
|
||||
|
||||
### Code Quality
|
||||
- Functions are focused and reasonably sized
|
||||
- No unnecessary complexity
|
||||
- DRY - no significant duplication
|
||||
- Clear naming for variables and functions
|
||||
- No dead code or commented-out code
|
||||
|
||||
### Testing
|
||||
- Tests exist for new functionality
|
||||
- Tests cover happy path AND error cases
|
||||
- No flaky tests introduced
|
||||
|
||||
### Performance
|
||||
- No obvious N+1 queries
|
||||
- No blocking operations in hot paths
|
||||
- Resource cleanup (connections, file handles)
|
||||
|
||||
### Dependencies
|
||||
- No deprecated packages
|
||||
- No unnecessary new dependencies
|
||||
|
||||
### Documentation
|
||||
- Complex logic has explanatory comments
|
||||
- Public APIs are documented
|
||||
|
||||
## Severity Guide
|
||||
- **blocker**: Must fix before merge (bugs, correctness issues, missing error handling)
|
||||
- **should-fix**: Important but not blocking (code quality, minor issues)
|
||||
- **suggestion**: Optional improvements (nice-to-haves)
|
||||
|
||||
Only report findings you are confident about (confidence > 0.7).
|
||||
If the code looks good, say so — don't manufacture issues.
|
||||
|
||||
PROMPT
|
||||
)
|
||||
|
||||
# Set up temp files for output and diff
|
||||
TEMP_OUTPUT=$(mktemp /tmp/codex-review-XXXXXX.json)
|
||||
TEMP_DIFF=$(mktemp /tmp/codex-diff-XXXXXX.txt)
|
||||
trap 'rm -f "$TEMP_OUTPUT" "$TEMP_DIFF"' EXIT
|
||||
|
||||
SCHEMA_FILE="$SCRIPT_DIR/schemas/code-review-schema.json"
|
||||
|
||||
# Write diff to temp file
|
||||
echo "$DIFF_CONTEXT" > "$TEMP_DIFF"
|
||||
|
||||
echo "Running Codex code 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 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" "code"
|
||||
echo "Posted review to PR #$PR_NUMBER" >&2
|
||||
fi
|
||||
|
||||
# Always print results to stdout
|
||||
print_results "$TEMP_OUTPUT" "code"
|
||||
235
rails/codex/codex-security-review.sh
Executable file
235
rails/codex/codex-security-review.sh
Executable file
@@ -0,0 +1,235 @@
|
||||
#!/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"
|
||||
191
rails/codex/common.sh
Executable file
191
rails/codex/common.sh
Executable file
@@ -0,0 +1,191 @@
|
||||
#!/bin/bash
|
||||
# common.sh - Shared utilities for Codex review scripts
|
||||
# Source this file from review scripts: source "$(dirname "${BASH_SOURCE[0]}")/common.sh"
|
||||
|
||||
set -e
|
||||
|
||||
CODEX_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
GIT_SCRIPT_DIR="$CODEX_SCRIPT_DIR/../git"
|
||||
|
||||
# Source platform detection
|
||||
source "$GIT_SCRIPT_DIR/detect-platform.sh"
|
||||
|
||||
# Check codex is installed
|
||||
check_codex() {
|
||||
if ! command -v codex &>/dev/null; then
|
||||
echo "Error: codex CLI not found. Install with: npm i -g @openai/codex" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check jq is installed (needed for JSON processing)
|
||||
check_jq() {
|
||||
if ! command -v jq &>/dev/null; then
|
||||
echo "Error: jq not found. Install with your package manager." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Build the codex exec command args for the review mode
|
||||
# Arguments: $1=mode (--uncommitted|--base|--commit), $2=value (branch/sha)
|
||||
build_diff_context() {
|
||||
local mode="$1"
|
||||
local value="$2"
|
||||
local diff_text=""
|
||||
|
||||
case "$mode" in
|
||||
uncommitted)
|
||||
diff_text=$(git diff HEAD 2>/dev/null; git diff --cached 2>/dev/null; git ls-files --others --exclude-standard 2>/dev/null | while read -r f; do echo "=== NEW FILE: $f ==="; cat "$f" 2>/dev/null; done)
|
||||
;;
|
||||
base)
|
||||
diff_text=$(git diff "${value}...HEAD" 2>/dev/null)
|
||||
;;
|
||||
commit)
|
||||
diff_text=$(git show "$value" 2>/dev/null)
|
||||
;;
|
||||
pr)
|
||||
# For PRs, we need to fetch the PR diff
|
||||
detect_platform
|
||||
if [[ "$PLATFORM" == "github" ]]; then
|
||||
diff_text=$(gh pr diff "$value" 2>/dev/null)
|
||||
elif [[ "$PLATFORM" == "gitea" ]]; then
|
||||
# tea doesn't have a direct pr diff command, use git
|
||||
local pr_base
|
||||
pr_base=$(tea pr list --fields index,base --output simple 2>/dev/null | grep "^${value}" | awk '{print $2}')
|
||||
if [[ -n "$pr_base" ]]; then
|
||||
diff_text=$(git diff "${pr_base}...HEAD" 2>/dev/null)
|
||||
else
|
||||
# Fallback: fetch PR info via API
|
||||
local repo_info
|
||||
repo_info=$(get_repo_info)
|
||||
local remote_url
|
||||
remote_url=$(git remote get-url origin 2>/dev/null)
|
||||
local host
|
||||
host=$(echo "$remote_url" | sed -E 's|.*://([^/]+).*|\1|; s|.*@([^:]+).*|\1|')
|
||||
diff_text=$(curl -s "https://${host}/api/v1/repos/${repo_info}/pulls/${value}" \
|
||||
-H "Authorization: token $(tea login list --output simple 2>/dev/null | head -1 | awk '{print $2}')" \
|
||||
2>/dev/null | jq -r '.diff_url // empty')
|
||||
if [[ -n "$diff_text" && "$diff_text" != "null" ]]; then
|
||||
diff_text=$(curl -s "$diff_text" 2>/dev/null)
|
||||
else
|
||||
diff_text=$(git diff "main...HEAD" 2>/dev/null)
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "$diff_text"
|
||||
}
|
||||
|
||||
# Format JSON findings as markdown for PR comments
|
||||
# Arguments: $1=json_file, $2=review_type (code|security)
|
||||
format_findings_as_markdown() {
|
||||
local json_file="$1"
|
||||
local review_type="$2"
|
||||
|
||||
if [[ ! -f "$json_file" ]]; then
|
||||
echo "Error: JSON file not found: $json_file" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local summary verdict confidence
|
||||
summary=$(jq -r '.summary' "$json_file")
|
||||
confidence=$(jq -r '.confidence' "$json_file")
|
||||
|
||||
if [[ "$review_type" == "code" ]]; then
|
||||
verdict=$(jq -r '.verdict' "$json_file")
|
||||
local blockers should_fix suggestions files_reviewed
|
||||
blockers=$(jq -r '.stats.blockers' "$json_file")
|
||||
should_fix=$(jq -r '.stats.should_fix' "$json_file")
|
||||
suggestions=$(jq -r '.stats.suggestions' "$json_file")
|
||||
files_reviewed=$(jq -r '.stats.files_reviewed' "$json_file")
|
||||
|
||||
cat <<EOF
|
||||
## Codex Code Review
|
||||
|
||||
**Verdict:** ${verdict} | **Confidence:** ${confidence} | **Files reviewed:** ${files_reviewed}
|
||||
**Findings:** ${blockers} blockers, ${should_fix} should-fix, ${suggestions} suggestions
|
||||
|
||||
### Summary
|
||||
${summary}
|
||||
|
||||
EOF
|
||||
else
|
||||
local risk_level critical high medium low files_reviewed
|
||||
risk_level=$(jq -r '.risk_level' "$json_file")
|
||||
critical=$(jq -r '.stats.critical' "$json_file")
|
||||
high=$(jq -r '.stats.high' "$json_file")
|
||||
medium=$(jq -r '.stats.medium' "$json_file")
|
||||
low=$(jq -r '.stats.low' "$json_file")
|
||||
files_reviewed=$(jq -r '.stats.files_reviewed' "$json_file")
|
||||
|
||||
cat <<EOF
|
||||
## Codex Security Review
|
||||
|
||||
**Risk Level:** ${risk_level} | **Confidence:** ${confidence} | **Files reviewed:** ${files_reviewed}
|
||||
**Findings:** ${critical} critical, ${high} high, ${medium} medium, ${low} low
|
||||
|
||||
### Summary
|
||||
${summary}
|
||||
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Output findings
|
||||
local finding_count
|
||||
finding_count=$(jq '.findings | length' "$json_file")
|
||||
|
||||
if [[ "$finding_count" -gt 0 ]]; then
|
||||
echo "### Findings"
|
||||
echo ""
|
||||
|
||||
jq -r '.findings[] | "#### [\(.severity | ascii_upcase)] \(.title)\n- **File:** `\(.file)`\(if .line_start then " (L\(.line_start)\(if .line_end and .line_end != .line_start then "-L\(.line_end)" else "" end))" else "" end)\n- \(.description)\(if .suggestion then "\n- **Suggestion:** \(.suggestion)" else "" end)\(if .cwe_id then "\n- **CWE:** \(.cwe_id)" else "" end)\(if .owasp_category then "\n- **OWASP:** \(.owasp_category)" else "" end)\(if .remediation then "\n- **Remediation:** \(.remediation)" else "" end)\n"' "$json_file"
|
||||
else
|
||||
echo "*No issues found.*"
|
||||
fi
|
||||
|
||||
echo "---"
|
||||
echo "*Reviewed by Codex ($(codex --version 2>/dev/null || echo "unknown"))*"
|
||||
}
|
||||
|
||||
# Post review findings to a PR
|
||||
# Arguments: $1=pr_number, $2=json_file, $3=review_type (code|security)
|
||||
post_to_pr() {
|
||||
local pr_number="$1"
|
||||
local json_file="$2"
|
||||
local review_type="$3"
|
||||
|
||||
local markdown
|
||||
markdown=$(format_findings_as_markdown "$json_file" "$review_type")
|
||||
|
||||
detect_platform
|
||||
|
||||
# Determine review action based on findings
|
||||
local action="comment"
|
||||
if [[ "$review_type" == "code" ]]; then
|
||||
local verdict
|
||||
verdict=$(jq -r '.verdict' "$json_file")
|
||||
action="$verdict"
|
||||
else
|
||||
local risk_level
|
||||
risk_level=$(jq -r '.risk_level' "$json_file")
|
||||
case "$risk_level" in
|
||||
critical|high) action="request-changes" ;;
|
||||
medium) action="comment" ;;
|
||||
low|none) action="comment" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Post the review
|
||||
"$GIT_SCRIPT_DIR/pr-review.sh" -n "$pr_number" -a "$action" -c "$markdown"
|
||||
}
|
||||
|
||||
# Print review results to stdout
|
||||
# Arguments: $1=json_file, $2=review_type (code|security)
|
||||
print_results() {
|
||||
local json_file="$1"
|
||||
local review_type="$2"
|
||||
|
||||
format_findings_as_markdown "$json_file" "$review_type"
|
||||
}
|
||||
84
rails/codex/schemas/code-review-schema.json
Normal file
84
rails/codex/schemas/code-review-schema.json
Normal file
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"summary": {
|
||||
"type": "string",
|
||||
"description": "Brief overall assessment of the code changes"
|
||||
},
|
||||
"verdict": {
|
||||
"type": "string",
|
||||
"enum": ["approve", "request-changes", "comment"],
|
||||
"description": "Overall review verdict"
|
||||
},
|
||||
"confidence": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"description": "Confidence score for the review (0-1)"
|
||||
},
|
||||
"findings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"severity": {
|
||||
"type": "string",
|
||||
"enum": ["blocker", "should-fix", "suggestion"],
|
||||
"description": "Finding severity: blocker (must fix), should-fix (important), suggestion (optional)"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Short title describing the issue"
|
||||
},
|
||||
"file": {
|
||||
"type": "string",
|
||||
"description": "File path where the issue was found"
|
||||
},
|
||||
"line_start": {
|
||||
"type": "integer",
|
||||
"description": "Starting line number"
|
||||
},
|
||||
"line_end": {
|
||||
"type": "integer",
|
||||
"description": "Ending line number"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Detailed explanation of the issue"
|
||||
},
|
||||
"suggestion": {
|
||||
"type": "string",
|
||||
"description": "Suggested fix or improvement"
|
||||
}
|
||||
},
|
||||
"required": ["severity", "title", "file", "line_start", "line_end", "description", "suggestion"]
|
||||
}
|
||||
},
|
||||
"stats": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"files_reviewed": {
|
||||
"type": "integer",
|
||||
"description": "Number of files reviewed"
|
||||
},
|
||||
"blockers": {
|
||||
"type": "integer",
|
||||
"description": "Count of blocker findings"
|
||||
},
|
||||
"should_fix": {
|
||||
"type": "integer",
|
||||
"description": "Count of should-fix findings"
|
||||
},
|
||||
"suggestions": {
|
||||
"type": "integer",
|
||||
"description": "Count of suggestion findings"
|
||||
}
|
||||
},
|
||||
"required": ["files_reviewed", "blockers", "should_fix", "suggestions"]
|
||||
}
|
||||
},
|
||||
"required": ["summary", "verdict", "confidence", "findings", "stats"]
|
||||
}
|
||||
96
rails/codex/schemas/security-review-schema.json
Normal file
96
rails/codex/schemas/security-review-schema.json
Normal file
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"summary": {
|
||||
"type": "string",
|
||||
"description": "Brief overall security assessment of the code changes"
|
||||
},
|
||||
"risk_level": {
|
||||
"type": "string",
|
||||
"enum": ["critical", "high", "medium", "low", "none"],
|
||||
"description": "Overall security risk level"
|
||||
},
|
||||
"confidence": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"description": "Confidence score for the review (0-1)"
|
||||
},
|
||||
"findings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"severity": {
|
||||
"type": "string",
|
||||
"enum": ["critical", "high", "medium", "low"],
|
||||
"description": "Vulnerability severity level"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Short title describing the vulnerability"
|
||||
},
|
||||
"file": {
|
||||
"type": "string",
|
||||
"description": "File path where the vulnerability was found"
|
||||
},
|
||||
"line_start": {
|
||||
"type": "integer",
|
||||
"description": "Starting line number"
|
||||
},
|
||||
"line_end": {
|
||||
"type": "integer",
|
||||
"description": "Ending line number"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Detailed explanation of the vulnerability"
|
||||
},
|
||||
"cwe_id": {
|
||||
"type": "string",
|
||||
"description": "CWE identifier if applicable (e.g., CWE-79)"
|
||||
},
|
||||
"owasp_category": {
|
||||
"type": "string",
|
||||
"description": "OWASP Top 10 category if applicable (e.g., A03:2021-Injection)"
|
||||
},
|
||||
"remediation": {
|
||||
"type": "string",
|
||||
"description": "Specific remediation steps to fix the vulnerability"
|
||||
}
|
||||
},
|
||||
"required": ["severity", "title", "file", "line_start", "line_end", "description", "cwe_id", "owasp_category", "remediation"]
|
||||
}
|
||||
},
|
||||
"stats": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"files_reviewed": {
|
||||
"type": "integer",
|
||||
"description": "Number of files reviewed"
|
||||
},
|
||||
"critical": {
|
||||
"type": "integer",
|
||||
"description": "Count of critical findings"
|
||||
},
|
||||
"high": {
|
||||
"type": "integer",
|
||||
"description": "Count of high findings"
|
||||
},
|
||||
"medium": {
|
||||
"type": "integer",
|
||||
"description": "Count of medium findings"
|
||||
},
|
||||
"low": {
|
||||
"type": "integer",
|
||||
"description": "Count of low findings"
|
||||
}
|
||||
},
|
||||
"required": ["files_reviewed", "critical", "high", "medium", "low"]
|
||||
}
|
||||
},
|
||||
"required": ["summary", "risk_level", "confidence", "findings", "stats"]
|
||||
}
|
||||
90
rails/codex/woodpecker/codex-review.yml
Normal file
90
rails/codex/woodpecker/codex-review.yml
Normal file
@@ -0,0 +1,90 @@
|
||||
# Codex AI Review Pipeline for Woodpecker CI
|
||||
# Drop this into your repo's .woodpecker/ directory to enable automated
|
||||
# code and security reviews on every pull request.
|
||||
#
|
||||
# Required secrets:
|
||||
# - codex_api_key: OpenAI API key or Codex-compatible key
|
||||
#
|
||||
# Optional secrets:
|
||||
# - gitea_token: Gitea API token for posting PR comments (if not using tea CLI auth)
|
||||
|
||||
when:
|
||||
event: pull_request
|
||||
|
||||
variables:
|
||||
- &node_image "node:22-slim"
|
||||
- &install_codex "npm i -g @openai/codex"
|
||||
|
||||
steps:
|
||||
# --- Code Quality Review ---
|
||||
code-review:
|
||||
image: *node_image
|
||||
environment:
|
||||
CODEX_API_KEY:
|
||||
from_secret: codex_api_key
|
||||
commands:
|
||||
- *install_codex
|
||||
- apt-get update -qq && apt-get install -y -qq jq git > /dev/null 2>&1
|
||||
|
||||
# Generate the diff
|
||||
- git fetch origin ${CI_COMMIT_TARGET_BRANCH:-main}
|
||||
- DIFF=$(git diff origin/${CI_COMMIT_TARGET_BRANCH:-main}...HEAD)
|
||||
|
||||
# Run code review with structured output
|
||||
- |
|
||||
codex exec \
|
||||
--sandbox read-only \
|
||||
--output-schema .woodpecker/schemas/code-review-schema.json \
|
||||
-o /tmp/code-review.json \
|
||||
"You are an expert code reviewer. Review the following code changes for correctness, code quality, testing, performance, and documentation issues. Only flag actionable, important issues. Categorize as blocker/should-fix/suggestion. If code looks good, say so.
|
||||
|
||||
Changes:
|
||||
$DIFF"
|
||||
|
||||
# Output summary
|
||||
- echo "=== Code Review Results ==="
|
||||
- jq '.' /tmp/code-review.json
|
||||
- |
|
||||
BLOCKERS=$(jq '.stats.blockers // 0' /tmp/code-review.json)
|
||||
if [ "$BLOCKERS" -gt 0 ]; then
|
||||
echo "FAIL: $BLOCKERS blocker(s) found"
|
||||
exit 1
|
||||
fi
|
||||
echo "PASS: No blockers found"
|
||||
|
||||
# --- Security Review ---
|
||||
security-review:
|
||||
image: *node_image
|
||||
environment:
|
||||
CODEX_API_KEY:
|
||||
from_secret: codex_api_key
|
||||
commands:
|
||||
- *install_codex
|
||||
- apt-get update -qq && apt-get install -y -qq jq git > /dev/null 2>&1
|
||||
|
||||
# Generate the diff
|
||||
- git fetch origin ${CI_COMMIT_TARGET_BRANCH:-main}
|
||||
- DIFF=$(git diff origin/${CI_COMMIT_TARGET_BRANCH:-main}...HEAD)
|
||||
|
||||
# Run security review with structured output
|
||||
- |
|
||||
codex exec \
|
||||
--sandbox read-only \
|
||||
--output-schema .woodpecker/schemas/security-review-schema.json \
|
||||
-o /tmp/security-review.json \
|
||||
"You are an expert application security engineer. Review the following code changes for security vulnerabilities including OWASP Top 10, hardcoded secrets, injection flaws, auth/authz gaps, XSS, CSRF, SSRF, path traversal, and supply chain risks. Include CWE IDs and remediation steps. Only flag real security issues, not code quality.
|
||||
|
||||
Changes:
|
||||
$DIFF"
|
||||
|
||||
# Output summary
|
||||
- echo "=== Security Review Results ==="
|
||||
- jq '.' /tmp/security-review.json
|
||||
- |
|
||||
CRITICAL=$(jq '.stats.critical // 0' /tmp/security-review.json)
|
||||
HIGH=$(jq '.stats.high // 0' /tmp/security-review.json)
|
||||
if [ "$CRITICAL" -gt 0 ] || [ "$HIGH" -gt 0 ]; then
|
||||
echo "FAIL: $CRITICAL critical, $HIGH high severity finding(s)"
|
||||
exit 1
|
||||
fi
|
||||
echo "PASS: No critical or high severity findings"
|
||||
Reference in New Issue
Block a user