Initial commit: PHP/Laravel Security Linter v1.0.0
A static security analysis tool for PHP and Laravel applications
with recursive taint analysis capabilities.
Features:
- Comprehensive vulnerability detection (XSS, SQL Injection,
Command Injection, Path Traversal, CSRF, Authentication issues)
- Recursive taint analysis across function calls
- Blade template analysis with context-aware XSS detection
- Smart escape detection and escape bypass detection
- Syntax highlighting in terminal output
- Multi-language support (Japanese/English)
- Docker support for easy deployment
- Multiple output formats (text, JSON, HTML, SARIF, Markdown)
- CI/CD integration ready (GitHub Actions, GitLab CI)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 15:18:53 +09:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
#
|
|
|
|
|
# php-security-lint - PHP/Laravel Security Linter
|
|
|
|
|
#
|
|
|
|
|
# A wrapper script to run the security linter via Docker.
|
|
|
|
|
# Install this script to /usr/local/bin/ for system-wide access.
|
|
|
|
|
#
|
|
|
|
|
# Usage:
|
|
|
|
|
# php-security-lint [options] <path>
|
|
|
|
|
# php-security-lint app/
|
|
|
|
|
# php-security-lint -s high -f json .
|
|
|
|
|
#
|
|
|
|
|
# Installation:
|
2026-02-02 19:32:08 +09:00
|
|
|
# Copy this script to /usr/local/bin/ after building the Docker image:
|
|
|
|
|
# sudo cp php-security-lint /usr/local/bin/
|
|
|
|
|
# sudo chmod +x /usr/local/bin/php-security-lint
|
Initial commit: PHP/Laravel Security Linter v1.0.0
A static security analysis tool for PHP and Laravel applications
with recursive taint analysis capabilities.
Features:
- Comprehensive vulnerability detection (XSS, SQL Injection,
Command Injection, Path Traversal, CSRF, Authentication issues)
- Recursive taint analysis across function calls
- Blade template analysis with context-aware XSS detection
- Smart escape detection and escape bypass detection
- Syntax highlighting in terminal output
- Multi-language support (Japanese/English)
- Docker support for easy deployment
- Multiple output formats (text, JSON, HTML, SARIF, Markdown)
- CI/CD integration ready (GitHub Actions, GitLab CI)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 15:18:53 +09:00
|
|
|
#
|
|
|
|
|
|
|
|
|
|
set -e
|
|
|
|
|
|
|
|
|
|
# Global variable to track container ID for cleanup
|
|
|
|
|
CONTAINER_ID=""
|
|
|
|
|
|
|
|
|
|
# Cleanup function to stop container on interrupt
|
|
|
|
|
cleanup() {
|
|
|
|
|
if [[ -n "$CONTAINER_ID" ]]; then
|
|
|
|
|
docker stop "$CONTAINER_ID" >/dev/null 2>&1 || true
|
|
|
|
|
fi
|
|
|
|
|
echo ""
|
|
|
|
|
exit 130
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Trap SIGINT (Ctrl+C) and SIGTERM
|
|
|
|
|
trap cleanup SIGINT SIGTERM
|
|
|
|
|
|
|
|
|
|
# Configuration
|
|
|
|
|
DOCKER_IMAGE="${PHP_SECURITY_LINT_IMAGE:-php-security-linter:latest}"
|
|
|
|
|
CONTAINER_TARGET="/target"
|
|
|
|
|
|
|
|
|
|
# Colors
|
|
|
|
|
RED='\033[0;31m'
|
|
|
|
|
GREEN='\033[0;32m'
|
|
|
|
|
YELLOW='\033[1;33m'
|
|
|
|
|
NC='\033[0m' # No Color
|
|
|
|
|
|
|
|
|
|
# Show usage
|
|
|
|
|
show_usage() {
|
|
|
|
|
cat << 'EOF'
|
|
|
|
|
PHP/Laravel Security Linter
|
|
|
|
|
|
|
|
|
|
Usage:
|
|
|
|
|
php-security-lint [options] <path>
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
php-security-lint . # Scan current directory
|
|
|
|
|
php-security-lint app/ # Scan app directory
|
|
|
|
|
php-security-lint -s high . # Show only high+ severity
|
|
|
|
|
php-security-lint -f json -o report.json . # Output as JSON
|
|
|
|
|
|
|
|
|
|
Options:
|
|
|
|
|
-f, --format <format> Output format: text, json, html, sarif, markdown
|
|
|
|
|
-s, --severity <level> Minimum severity: low, medium, high, critical
|
|
|
|
|
-o, --output <file> Write output to file
|
|
|
|
|
-l, --lang <lang> Language: ja, en
|
|
|
|
|
-c, --context [N] Show N lines of code context (default: 3)
|
|
|
|
|
-e, --exclude <pattern> Exclude pattern (can be used multiple times)
|
|
|
|
|
-i, --include <pattern> Include pattern (overrides exclude)
|
|
|
|
|
-d, --recursive-depth <n> Recursive analysis depth (default: 10)
|
|
|
|
|
--include-vendor Include vendor directory
|
|
|
|
|
--include-tests Include tests directory
|
|
|
|
|
--no-colors Disable colored output
|
|
|
|
|
-q, --quiet Suppress progress output
|
|
|
|
|
--verbose Show detailed information
|
|
|
|
|
-h, --help Show this help message
|
|
|
|
|
--version Show version
|
|
|
|
|
|
|
|
|
|
Environment Variables:
|
|
|
|
|
PHP_SECURITY_LINT_IMAGE Docker image to use (default: php-security-linter:latest)
|
|
|
|
|
|
|
|
|
|
EOF
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Check if Docker is available
|
|
|
|
|
check_docker() {
|
|
|
|
|
if ! command -v docker &> /dev/null; then
|
|
|
|
|
echo -e "${RED}Error: Docker is not installed or not in PATH${NC}" >&2
|
|
|
|
|
echo "Please install Docker: https://docs.docker.com/get-docker/" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if ! docker info &> /dev/null; then
|
|
|
|
|
echo -e "${RED}Error: Docker daemon is not running${NC}" >&2
|
|
|
|
|
echo "Please start the Docker daemon" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Check if image exists, build if necessary
|
|
|
|
|
check_image() {
|
|
|
|
|
if ! docker image inspect "$DOCKER_IMAGE" &> /dev/null; then
|
|
|
|
|
echo -e "${YELLOW}Docker image '$DOCKER_IMAGE' not found.${NC}"
|
|
|
|
|
|
|
|
|
|
# Check if we're in the project directory with Dockerfile
|
|
|
|
|
if [[ -f "Dockerfile" ]] && grep -q "security-linter" "Dockerfile" 2>/dev/null; then
|
|
|
|
|
echo "Building image from local Dockerfile..."
|
|
|
|
|
docker build -t "$DOCKER_IMAGE" .
|
|
|
|
|
else
|
|
|
|
|
echo -e "${RED}Error: Docker image '$DOCKER_IMAGE' not found.${NC}" >&2
|
|
|
|
|
echo "" >&2
|
2026-02-02 19:32:08 +09:00
|
|
|
echo "To build the image, clone the repository and run:" >&2
|
|
|
|
|
echo " git clone https://opensource.rogarithm.net/rogarithm/php-security-linter.git" >&2
|
|
|
|
|
echo " cd php-security-linter" >&2
|
Initial commit: PHP/Laravel Security Linter v1.0.0
A static security analysis tool for PHP and Laravel applications
with recursive taint analysis capabilities.
Features:
- Comprehensive vulnerability detection (XSS, SQL Injection,
Command Injection, Path Traversal, CSRF, Authentication issues)
- Recursive taint analysis across function calls
- Blade template analysis with context-aware XSS detection
- Smart escape detection and escape bypass detection
- Syntax highlighting in terminal output
- Multi-language support (Japanese/English)
- Docker support for easy deployment
- Multiple output formats (text, JSON, HTML, SARIF, Markdown)
- CI/CD integration ready (GitHub Actions, GitLab CI)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 15:18:53 +09:00
|
|
|
echo " docker build -t php-security-linter:latest ." >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Parse arguments and find target path
|
|
|
|
|
parse_args() {
|
|
|
|
|
local args=()
|
|
|
|
|
local target_path=""
|
|
|
|
|
local output_file=""
|
|
|
|
|
local has_output=false
|
|
|
|
|
|
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
|
|
|
case "$1" in
|
|
|
|
|
-h|--help)
|
|
|
|
|
show_usage
|
|
|
|
|
exit 0
|
|
|
|
|
;;
|
|
|
|
|
--version)
|
|
|
|
|
docker run --rm "$DOCKER_IMAGE" --version
|
|
|
|
|
exit 0
|
|
|
|
|
;;
|
|
|
|
|
-o|--output)
|
|
|
|
|
has_output=true
|
|
|
|
|
output_file="$2"
|
|
|
|
|
args+=("$1" "$CONTAINER_TARGET/$(basename "$2")")
|
|
|
|
|
shift 2
|
|
|
|
|
;;
|
|
|
|
|
-f|--format|-s|--severity|-l|--lang|-e|--exclude|-i|--include|-d|--recursive-depth)
|
|
|
|
|
args+=("$1" "$2")
|
|
|
|
|
shift 2
|
|
|
|
|
;;
|
|
|
|
|
-c|--context)
|
|
|
|
|
# Handle optional numeric argument (must be a number, not a path)
|
|
|
|
|
if [[ -n "$2" ]] && [[ "$2" =~ ^[0-9]+$ ]] && [[ ! "$2" =~ / ]]; then
|
|
|
|
|
args+=("$1" "$2")
|
|
|
|
|
shift 2
|
|
|
|
|
else
|
|
|
|
|
args+=("$1")
|
|
|
|
|
shift
|
|
|
|
|
fi
|
|
|
|
|
;;
|
|
|
|
|
--include-vendor|--include-tests|--no-colors|--no-default-excludes|--show-excluded|-q|--quiet|--verbose)
|
|
|
|
|
args+=("$1")
|
|
|
|
|
shift
|
|
|
|
|
;;
|
|
|
|
|
-*)
|
|
|
|
|
args+=("$1")
|
|
|
|
|
shift
|
|
|
|
|
;;
|
|
|
|
|
*)
|
|
|
|
|
# This is the target path
|
|
|
|
|
if [[ -z "$target_path" ]]; then
|
|
|
|
|
target_path="$1"
|
|
|
|
|
else
|
|
|
|
|
args+=("$1")
|
|
|
|
|
fi
|
|
|
|
|
shift
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# Default to current directory if no path specified
|
|
|
|
|
if [[ -z "$target_path" ]]; then
|
|
|
|
|
target_path="."
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Convert to absolute path
|
|
|
|
|
if [[ "$target_path" == "." ]]; then
|
|
|
|
|
TARGET_HOST_PATH="$(pwd)"
|
|
|
|
|
TARGET_CONTAINER_PATH="$CONTAINER_TARGET"
|
|
|
|
|
elif [[ "$target_path" == /* ]]; then
|
|
|
|
|
TARGET_HOST_PATH="$target_path"
|
|
|
|
|
TARGET_CONTAINER_PATH="$CONTAINER_TARGET"
|
|
|
|
|
else
|
|
|
|
|
TARGET_HOST_PATH="$(pwd)"
|
|
|
|
|
TARGET_CONTAINER_PATH="$CONTAINER_TARGET/$target_path"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
DOCKER_ARGS=("${args[@]}" "$TARGET_CONTAINER_PATH")
|
|
|
|
|
OUTPUT_FILE="$output_file"
|
|
|
|
|
HAS_OUTPUT="$has_output"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Main execution
|
|
|
|
|
main() {
|
|
|
|
|
check_docker
|
|
|
|
|
check_image
|
|
|
|
|
parse_args "$@"
|
|
|
|
|
|
|
|
|
|
# Build docker run command
|
|
|
|
|
local docker_cmd=(
|
|
|
|
|
docker run --rm
|
|
|
|
|
--init # Proper signal handling (Ctrl+C)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Allocate TTY and interactive mode if terminal is interactive
|
|
|
|
|
if [[ -t 0 ]] && [[ -t 1 ]]; then
|
|
|
|
|
docker_cmd+=(-it)
|
|
|
|
|
elif [[ -t 1 ]]; then
|
|
|
|
|
docker_cmd+=(-t)
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
docker_cmd+=(-v "$TARGET_HOST_PATH:$CONTAINER_TARGET:ro")
|
|
|
|
|
|
|
|
|
|
# If output file specified, we need write access to output directory
|
|
|
|
|
if [[ "$HAS_OUTPUT" == "true" ]] && [[ -n "$OUTPUT_FILE" ]]; then
|
|
|
|
|
local output_dir
|
|
|
|
|
output_dir="$(dirname "$(pwd)/$OUTPUT_FILE")"
|
|
|
|
|
mkdir -p "$output_dir"
|
|
|
|
|
docker_cmd+=(-v "$output_dir:$CONTAINER_TARGET:rw")
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Add image and arguments
|
|
|
|
|
docker_cmd+=("$DOCKER_IMAGE" "${DOCKER_ARGS[@]}")
|
|
|
|
|
|
|
|
|
|
# Execute
|
|
|
|
|
"${docker_cmd[@]}"
|
|
|
|
|
local exit_code=$?
|
|
|
|
|
|
|
|
|
|
# Copy output file if specified
|
|
|
|
|
if [[ "$HAS_OUTPUT" == "true" ]] && [[ -n "$OUTPUT_FILE" ]] && [[ -f "$OUTPUT_FILE" ]]; then
|
|
|
|
|
echo -e "${GREEN}Report written to: $OUTPUT_FILE${NC}"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
exit $exit_code
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
main "$@"
|