How It Works
Overview
ShieldCI analyzes your Laravel application using static code analysis, examining your codebase without executing it. This deep-dive explains the technical architecture, analysis process, and how ShieldCI delivers actionable security and quality insights.
Architecture
High-Level Flow
┌─────────────────────────────────────────────────────────────┐
│ 1. Discovery Phase │
│ ├─ Scan project structure │
│ ├─ Read composer.json/composer.lock │
│ ├─ Detect Laravel version │
│ └─ Identify relevant files │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 2. Parsing Phase │
│ ├─ Convert PHP files to Abstract Syntax Trees (AST) │
│ ├─ Extract configuration values │
│ ├─ Map routes and middleware │
│ └─ Build dependency graph │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 3. Analysis Phase │
│ ├─ Run all specialized analyzers │
│ ├─ Check security patterns │
│ ├─ Detect performance anti-patterns │
│ └─ Validate best practices │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 4. Reporting Phase │
│ ├─ Aggregate issues by severity │
│ ├─ Generate recommendations │
│ ├─ Create code snippets │
│ └─ Output results (console/JSON/HTML) │
└─────────────────────────────────────────────────────────────┘Core Components
1. Analysis Engine
- Orchestrates analyzer execution
- Manages file parsing and caching
- Coordinates result aggregation
- Handles error recovery
2. Parser Interface
- Converts PHP code to Abstract Syntax Trees using nikic/php-parser
- Provides AST traversal methods
- Extracts code patterns and structures
- Caches parsed results for performance
3. Analyzer Registry
- Maintains catalog of all analyzers
- Filters analyzers by category, severity
- Manages analyzer dependencies
- Supports dynamic analyzer loading
4. Result Collector
- Aggregates issues from all analyzers
- Deduplicates similar issues
- Calculates overall quality scores
- Generates summary statistics
5. Reporter System
- Formats results for different outputs (console, JSON)
- Creates code snippets with context
- Generates actionable recommendations
- Supports custom report templates
Analysis Process Deep Dive
Phase 1: Discovery
ShieldCI starts by understanding your project structure:
// 1. Detect Laravel version
$laravelVersion = app()->version(); // e.g., "10.48.4"
// 2. Identify application type
$isAPI = file_exists(base_path('routes/api.php'));
$hasFrontend = file_exists(base_path('resources/views'));
// 3. Scan directory structure
$paths = [
'app',
'config',
'routes',
'resources',
'database',
'tests',
];
// 4. Read dependency manifest
$composer = json_decode(file_get_contents('composer.json'), true);
$packages = $composer['require'] ?? [];Optimization: ShieldCI only analyzes relevant paths, skipping vendor/, node_modules/, and storage/ for performance.
Phase 2: Parsing
ShieldCI converts PHP files into Abstract Syntax Trees (AST) for deep analysis:
Example: Parsing a Controller
// Original PHP code
namespace App\Http\Controllers;
class UserController extends Controller
{
public function index()
{
return User::all(); // ❌ N+1 query potential
}
}AST Representation:
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\ParserFactory;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP8);
$ast = $parser->parse(file_get_contents('UserController.php'));
// AST structure:
Node\Stmt\Namespace_
└─ Node\Stmt\Class_ (name: UserController)
└─ Node\Stmt\ClassMethod (name: index)
└─ Node\Stmt\Return_
└─ Node\Expr\StaticCall
├─ class: Node\Name (User)
└─ method: Identifier (all)Why AST Analysis?
- Precision: Understands code structure, not just text patterns
- Context-aware: Knows the difference between
User::all()in a controller vs. a service - Robust: Handles complex PHP syntax including closures, traits, and attributes
Phase 3: Analyzer Execution
Each analyzer is a specialized component that checks for specific patterns:
Example: N+1 Query Analyzer
class NPlusOneQueryAnalyzer extends AbstractFileAnalyzer
{
protected function runAnalysis(): ResultInterface
{
$issues = [];
// 1. Find all Eloquent queries
foreach ($this->getPhpFiles() as $file) {
$ast = $this->parser->parseFile($file);
// 2. Look for ::all(), ::get() without ->with()
$queries = $this->findEloquentQueries($ast);
foreach ($queries as $query) {
// 3. Check if relationships are accessed in loops
if ($this->hasRelationshipAccessInLoop($query)) {
$issues[] = $this->createIssue(
message: 'N+1 query detected',
location: new Location($file, $query->getLine()),
severity: Severity::High,
recommendation: 'Use eager loading: User::with(\'posts\')->get()'
);
}
}
}
return $this->resultBySeverity($summary, $issues);
}
}Analyzer Categories:
Security Analyzers (22 total)
- Pattern: Look for vulnerable code patterns (SQL injection, XSS, CSRF)
- Method: AST analysis + configuration validation
- Example: Detect missing
->middleware('throttle')on login routes
Performance Analyzers (18 total)
- Pattern: Identify inefficient database queries and caching issues
- Method: AST analysis + configuration checks
- Example: Detect N+1 queries by finding relationship access in loops
Reliability Analyzers (13 total)
- Pattern: Validate error handling and configuration
- Method: Configuration parsing + environment checks
- Example: Ensure
APP_DEBUG=falsein production
Code Quality Analyzers (5 total)
- Pattern: Check for deprecated APIs and anti-patterns
- Method: AST traversal + Laravel version awareness
- Example: Detect usage of deprecated
array_divide()helper
Best Practice Analyzers (15 total)
- Pattern: Enforce Laravel conventions
- Method: AST analysis + naming pattern checks
- Example: Ensure route names follow kebab-case convention
Phase 4: Issue Detection
ShieldCI uses multiple detection strategies:
1. AST Pattern Matching
// Detect raw SQL queries (SQL injection risk)
$traverser = new NodeTraverser();
$traverser->addVisitor(new class extends NodeVisitorAbstract {
public function enterNode(Node $node) {
// Find DB::raw(), DB::select() with concatenation
if ($node instanceof Node\Expr\StaticCall &&
$node->class->toString() === 'DB' &&
in_array($node->name->toString(), ['raw', 'select'])) {
// Check if argument contains string concatenation
if ($this->hasStringConcatenation($node->args[0])) {
// ❌ SQL injection vulnerability
return $node;
}
}
}
});2. Configuration Analysis
// Check production environment settings
$appConfig = config('app');
if ($appConfig['debug'] === true && app()->environment('production')) {
// ❌ Debug mode enabled in production
$this->createIssue('Debug mode exposes sensitive data');
}
if (empty($appConfig['key'])) {
// ❌ Missing application key
$this->createIssue('APP_KEY not set - sessions/encryption insecure');
}3. Dependency Scanning
// Check for vulnerable packages
$composerLock = json_decode(file_get_contents('composer.lock'), true);
foreach ($composerLock['packages'] as $package) {
$vulnerabilities = $this->checkPackageVulnerabilities(
$package['name'],
$package['version']
);
if (!empty($vulnerabilities)) {
// ❌ Vulnerable dependency
$this->createIssue("Vulnerable package: {$package['name']}");
}
}4. Route Analysis
// Validate route security
$routes = Route::getRoutes();
foreach ($routes as $route) {
if ($this->isAuthenticationRoute($route)) {
$middleware = $route->middleware();
if (!in_array('throttle', $middleware)) {
// ❌ Missing rate limiting
$this->createIssue("Login route lacks throttle middleware");
}
}
}Phase 5: Result Generation
ShieldCI creates detailed, actionable reports:
Issue Structure:
[
'id' => 'login-throttling-001',
'analyzer' => 'LoginThrottlingAnalyzer',
'message' => 'Login route "/login" lacks rate limiting protection',
'severity' => 'high',
'category' => 'security',
'location' => [
'file' => 'routes/web.php',
'line' => 42,
],
'code_snippet' => "Route::post('/login', [LoginController::class, 'login']);",
'recommendation' => 'Add ->middleware("throttle:5,1") to prevent brute force attacks',
'time_to_fix' => 5, // minutes
'references' => [
'https://laravel.com/docs/routing#rate-limiting',
'https://owasp.org/www-community/controls/Blocking_Brute_Force_Attacks',
],
'metadata' => [
'route' => '/login',
'issue_type' => 'missing_route_throttle',
],
]Severity Calculation:
// Critical: Immediate security risk
- Debug mode in production
- Missing CSRF protection
- SQL injection vulnerability
// High: Significant security/performance risk
- Missing login throttling
- N+1 queries in critical paths
- Vulnerable dependencies
// Medium: Best practice violations
- Missing cache configuration
- Deprecated API usage
- Code quality issues
// Low: Minor improvements
- Missing route names
- Suboptimal configuration
- Documentation gapsCI/CD Integration
GitHub Actions
name: ShieldCI Analysis
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
- name: Install Dependencies
run: composer install --no-dev
- name: Run ShieldCI
run: php artisan shield:analyze --format=json > results.json
- name: Upload Results
uses: actions/upload-artifact@v3
with:
name: shieldci-results
path: results.json
- name: Fail on Critical Issues
run: |
CRITICAL=$(cat results.json | jq '.issues[] | select(.severity=="critical") | length')
if [ $CRITICAL -gt 0 ]; then exit 1; fiGitLab CI
shieldci:
stage: test
image: php:8.1
script:
- composer install --no-dev
- php artisan shield:analyze --format=json
artifacts:
reports:
codequality: shieldci-results.json
allow_failure: falseExit Codes
ShieldCI returns specific exit codes for CI/CD:
0 // Success: No High/Critical issues (even if Medium/Low issues exist with Warning status)
1 // Failure: High or Critical severity issues detectedExtending ShieldCI
Custom Analyzers
Create project-specific analyzers:
namespace App\Analyzers;
use ShieldCI\AnalyzersCore\Abstracts\AbstractFileAnalyzer;
use ShieldCI\AnalyzersCore\Contracts\ResultInterface;
class CustomSecurityAnalyzer extends AbstractFileAnalyzer
{
protected function metadata(): AnalyzerMetadata
{
return new AnalyzerMetadata(
id: 'custom-security',
name: 'Custom Security Analyzer',
description: 'Company-specific security checks',
category: Category::Security,
severity: Severity::High,
tags: ['custom', 'security']
);
}
protected function runAnalysis(): ResultInterface
{
$issues = [];
// Your custom analysis logic
foreach ($this->getPhpFiles() as $file) {
// Check for company-specific patterns
}
return $this->resultBySeverity($summary, $issues);
}
}Register Custom Analyzer:
Custom analyzers are automatically discovered if placed in the app/Analyzers directory and follow the proper namespace structure. ShieldCI will automatically load them during analysis.
Custom Rules
Disable specific analyzers or ignore specific errors:
// config/shieldci.php
return [
'disabled_analyzers' => [
'license-compliance', // Disable license checking
],
'ignore_errors' => [
'sql-injection' => [
[
'path' => 'app/Legacy/OldController.php',
'message' => 'Potential SQL injection vulnerability',
],
],
],
];Privacy & Security
Data Handling
What ShieldCI Accesses:
- ✅ PHP source code (AST parsing only)
- ✅ Configuration files (read-only)
- ✅ composer.json/composer.lock (dependency analysis)
- ✅ package.json/package-lock.json (NPM dependencies)
What ShieldCI Never Accesses:
- ❌ Database credentials (reads config structure, not values)
- ❌ API keys (pattern detection only, not values)
- ❌ User data or business logic
- ❌ Network requests (except CVE database for dependency checks)
Compliance
- GDPR: No personal data collected or transmitted
- SOC 2: Pro version includes audit reports
- HIPAA: Can be used in HIPAA-compliant environments
- ISO 27001: Compatible with security standards