AI Skill Report Card

Reviewing Fullstack Code Changes

A90·Nov 6, 2026·Source: Web
YAML
--- name: reviewing-fullstack-code-changes description: Reviews uncommitted git changes in PHP/MariaDB/Apache fullstack projects for syntax errors, logic flaws, and OWASP security vulnerabilities. Use when new code has been written and needs review before committing, especially for PHP backend code interacting with MariaDB and handling user input. --- # Reviewing Fullstack Code Changes
14 / 15
Bash
# 1. See what changed git status git diff # 2. Syntax-check every modified PHP file git diff --name-only --diff-filter=ACM -- '*.php' | xargs -I{} php -l {} # 3. Review diff for logic + security issues (see Workflow below) git diff > /tmp/review.diff

Then analyze /tmp/review.diff (or the diff output directly) following the four-pass workflow.

Recommendation▾
Add an example showing a clean/passing review to illustrate a good outcome, not just flagged issues
15 / 15

Always run the passes in this order. Each pass has a distinct focus — don't mix them or issues get missed.

Progress:
- [ ] Pass 0: Identify scope (git status/diff)
- [ ] Pass 1: Syntax / programming errors
- [ ] Pass 2: Logic errors
- [ ] Pass 3: OWASP security review
- [ ] Pass 4: Summarize findings with severity + fix suggestions

Pass 0 — Identify Scope

  • Run git status to list new/modified/staged files.
  • Run git diff (unstaged) and git diff --staged (staged) separately — review both.
  • Ignore vendor/, node_modules/, generated assets.
  • Build a list of changed files grouped by type: PHP, JS, SQL, config (.htaccess, apache confs), templates.

Pass 1 — Programming / Syntax Errors

For each changed PHP file:

  • Run php -l <file> (lint). Flag any parse errors immediately — this is a hard blocker.
  • Manually scan for:
    • Undefined variables/array keys (especially $_GET/$_POST/$_SESSION used without isset/null checks).
    • Type mismatches (PHP 8.5 is strict about deprecated implicit coercions — check for deprecation notices).
    • Unclosed resources (DB connections, file handles).
    • Missing declare(strict_types=1) where the rest of the codebase uses it.
    • Function/method signature mismatches vs. call sites.
    • Dead code / unreachable branches introduced by the diff.
  • For JS changes, check browser console errors mentally: undefined refs, missing await, unhandled promise rejections.

Pass 2 — Logic Errors

  • Trace each modified function's data flow: input → transformation → output/DB write.
  • For functions touching the database, mentally run or actually run test queries against MariaDB with edge-case inputs:
    • empty string, NULL, 0, negative numbers, very long strings, unicode/emoji, duplicate keys.
  • Check boundary conditions: off-by-one in loops/pagination, < vs <=, inclusive/exclusive date ranges.
  • Check conditional logic: inverted conditions, missing else, short-circuit evaluation side effects.
  • Verify state consistency across request lifecycle (sessions, transactions — is a DB transaction committed/rolled back on all paths?).
  • Cross-check business rules against requirements if known (e.g., "price can't go negative", "user can't book past dates").
  • If a function changed, check every caller of that function for now-broken assumptions.

Pass 3 — OWASP Security Review

Walk the OWASP Top 10 against the diff explicitly:

CheckWhat to look for
Injection (SQLi)Any SQL built via string concatenation/interpolation with user input. Require prepared statements (PDO::prepare / mysqli bound params). Flag ->query($var) patterns.
XSSUser input echoed into HTML/JS without htmlspecialchars() / proper escaping. Check template files too.
Broken Auth/SessionSession fixation, missing session_regenerate_id() on login, weak password hashing (must be password_hash/password_verify, never md5/sha1).
Access ControlMissing authorization checks on new endpoints/actions (IDOR — can user A access user B's resource by changing an ID?).
Security MisconfigNew .htaccess/Apache vhost changes exposing directories; display_errors on in prod; default credentials.
Sensitive Data ExposureSecrets/passwords/API keys committed in code; logging of sensitive data (passwords, tokens, full card numbers).
CSRFState-changing forms/AJAX without CSRF token validation.
Insecure Deserializationunserialize() on user-controlled input.
File Upload/Path TraversalUser-controlled filenames/paths passed to fopen, include, require, file_get_contents without sanitization.
Command InjectionUser input passed to exec, shell_exec, system, passthru.

Actively try to break it:

  • Append ' OR '1'='1 to any input feeding a query — confirm it's neutralized by prepared statements.
  • Inject <script>alert(1)</script> into any input that gets rendered — confirm it's escaped.
  • Try path traversal (../../etc/passwd) on any file-path input.

Pass 4 — Summarize

Output findings grouped by severity (Critical / High / Medium / Low), each with:

  • File + line number
  • Category (Syntax / Logic / Security)
  • Concrete fix (code snippet), not just a description of the problem.
Recommendation▾
Include guidance on handling ambiguous cases where business rules are unknown (what to flag vs. ask about)
18 / 20

Example 1: Input:

PHP
$id = $_GET['id']; $result = $db->query("SELECT * FROM orders WHERE user_id = $id");

Output:

[CRITICAL][Security - SQL Injection] file: orders.php:12
`$id` from $_GET is concatenated directly into SQL.
Fix:
$stmt = $db->prepare("SELECT * FROM orders WHERE user_id = :id");
$stmt->execute(['id' => $id]);
$result = $stmt->fetchAll();
Also validate $id is an expected type (e.g. ctype_digit($id)) before use.

Example 2: Input:

PHP
function getDiscount($total) { if ($total > 100) { return $total * 0.1; } }

Output:

[MEDIUM][Logic] file: cart.php:45
Function returns NULL (not 0) when $total <= 100, which will cause
"Unsupported operand types" if the caller does arithmetic with the result.
Fix: add `return 0;` as the default/else branch.

[LOW][Programming] Missing type hints — add `function getDiscount(float $total): float`
given the project uses strict_types elsewhere.
Recommendation▾
Consider a brief note on scaling the workflow for large diffs (many files) to keep review time bounded
  • Always lint (php -l) before reading logic — don't waste time reviewing code that won't parse.
  • Test logic against real data pulled from MariaDB, not assumptions — query edge cases (NULLs, empty tables, duplicate rows) directly.
  • Treat every $_GET, $_POST, $_REQUEST, $_COOKIE, and header value as hostile until proven otherwise.
  • Prefer showing a corrected code snippet over a prose description — faster to apply.
  • Re-check files that call/include the changed file, not just the file itself.
  • For WSL/Apache/PHP-FPM setups, remember config changes (php.ini, vhost, .htaccess) are part of the diff scope too — review them.
  • Don't stop at the first syntax error file — lint all changed PHP files before moving to logic review.
  • Don't assume ORM/query builder usage is automatically safe — raw fragments or whereRaw-style calls can still be injectable.
  • Don't skip reviewing JS/frontend changes for XSS just because the backend "already escapes" — double-check both sides.
  • Don't flag issues without a concrete fix — "this could be insecure" is not actionable.
  • Don't review staged and unstaged diffs together without distinguishing them — know what's actually about to be committed.
0
Grade AAI Skill Framework
Scorecard
Criteria Breakdown
Quick Start
14/15
Workflow
15/15
Examples
18/20
Completeness
19/20
Format
15/15
Conciseness
13/15