3 · Extract & snapshot the vendor
Objective — get the vendor’s code onto disk and capture a pristine reference of exactly what shipped, so every future vendor update is a reviewable diff instead of an archaeology dig.
Steps at a glance:
- Extract the ZIP and place the root — CodeCanyon ZIPs have unpredictable nesting — an outer ZIP wrapping inner ZIPs, docs mixed with source, sometimes multiple framework versions.
- Verify the Laravel root — After the user has placed the files, the agent confirms the extraction is correct. Run from the project directory.
- Capture vendor docs & detect plugins — Vendor docs carry context the listing page doesn’t — admin features, required cron jobs, API endpoints, server requirements.
- Back up the pristine originals (vendor files only) — Snapshot the untouched vendor-shipped top-level
files into a versioned vault — an offline, easy-to-eyeball record of “exactly as shipped.” This is a
convenience copy: the true pristine baseline is already preserved in git via the
author/vbranch +author-vtag (the frozen vendor import). - Inspect & classify author vendor patches (the seam) — CodeCanyon authors frequently modify third-party
packages inside
vendor/. Some of those edits are load-bearing patches (a real bug fix the app depends on — ifcomposer installoverwrites them, install breaks).
Background
Section titled “Background”The code arrives now (or already landed during AI dev environment setup). Capture a pristine reference of exactly what the vendor shipped and identify any patches the author baked into vendor/. This is the page that makes every future vendor update a reviewable diff.
flowchart LR Z["CodeCanyon ZIP<br/>(👤 extract manually)"] --> R["verify the<br/>Laravel root"] R --> D["capture vendor docs<br/>+ detect plugins"] D --> B["back up pristine VENDOR<br/>originals → Admin-Local"] B --> P["compare shipped vendor/<br/>vs a fresh install"] P --> G["core-package guard<br/>+ backdoor-signature scan"] G --> V["read + classify each diff<br/>functional vs telemetry/backdoor"] V --> C["preserve patches · strip backdoors<br/>document in Zaj-CUSTOMIZATIONS.md"] C --> T["boot → composer reinstall →<br/>boot: routes equal, signature gone"]1. Extract the ZIP and place the root
Section titled “1. Extract the ZIP and place the root”CodeCanyon ZIPs have unpredictable nesting — an outer ZIP wrapping inner ZIPs, docs mixed with source, sometimes multiple framework versions. Extract manually, then let the agent verify.
-
Unpack and inspect. Double-click the ZIP in Finder (or use The Unarchiver) and look at what came out.
- ✅ The archive is extracted and its top-level contents are visible.
-
Find the Laravel root — the folder containing
artisan,composer.json,app/,config/,routes/.What you see What to do artisandirectly in the extracted folderThat folder is the Laravel root A main-files/orscript/subfolder holdingartisanThe root is inside that subfolder Inner ZIPs ( main-files.zip,documentation.zip)Extract the source ZIP — that contains the root vendor.zip/vendor/inside the rootShipped vendor — extract it into the root if zipped documentation/,license/,readme/foldersNot the root — move these to Admin-Local/2-Docs/- ✅ You can point to the one folder that contains
artisan.
- ✅ You can point to the one folder that contains
-
Move (cut, don’t copy) the root into your workspace so nothing is left behind.
Terminal window mv /path/to/extracted/laravel-root ~/MyWork/Projects/<ProjectName># Expected: the root now lives under your workspace; the extraction folder is empty- ✅ The Laravel root is in your workspace and the source extraction folder is empty.
2. Verify the Laravel root
Section titled “2. Verify the Laravel root”After the user has placed the files, the agent confirms the extraction is correct. Run from the project directory.
-
Run the structure check.
Terminal window echo "--- Essential files ---"for f in artisan composer.json .env.example; do[ -f "$f" ] && echo " ✅ $f" || echo " ❌ MISSING: $f"doneecho "--- Essential directories ---"for d in app config database public resources routes storage bootstrap; do[ -d "$d" ] && echo " ✅ $d/" || echo " ❌ MISSING: $d/"doneecho "--- Shipped (do NOT overwrite) ---"[ -d vendor ] && echo " 📦 vendor/ present ($(du -sh vendor | cut -f1)) — SHIPPED" || echo " ⬜ vendor/ absent — composer install later"[ -d public/build ] && echo " 📦 public/build/ present — pre-built assets" || echo " ⬜ public/build/ absent — npm run build later"echo "--- Nesting / leftover ZIPs ---"[ -f artisan ] && [ -d app ] || { echo " ❌ root is nested — find it:"; find . -maxdepth 3 -name artisan; }find . -maxdepth 2 -name "*.zip" | head -5# Expected: all ✅, vendor/ + public/build flagged 📦 if shipped, no ❌, no stray .zip- ✅ Every essential file/dir prints ✅, no nesting error, no unextracted inner ZIP.
3. Capture vendor docs & detect plugins
Section titled “3. Capture vendor docs & detect plugins”Vendor docs carry context the listing page doesn’t — admin features, required cron jobs, API endpoints, server requirements. Mirror them in so later phases can grep them.
-
Scan for documentation files.
Terminal window echo "=== Vendor Documentation Scan ==="echo "--- In _source/ or _Source/ ---"find _source/ _Source/ -maxdepth 2 \( -name "*.pdf" -o -name "*.html" -o -name "*.txt" \-o -name "*.md" -o -name "README*" -o -name "INSTALL*" -o -name "CHANGELOG*" \) 2>/dev/nullecho "--- Documentation folders in project root ---"for dir in documentation docs doc Documentation Docs; do[ -d "$dir" ] && echo " 📂 $dir/ ($(find "$dir" -type f | wc -l | tr -d ' ') files)"doneecho "--- Key files ---"for file in README.md readme.md README.txt INSTALL.md INSTALL.txt CHANGELOG.md changelog.md; do[ -f "$file" ] && echo " 📄 $file ($(wc -l < "$file" | tr -d ' ') lines)"doneecho "--- Online docs references ---"grep -rih "documentation\|docs\.\|wiki\.\|support\." README* *.md _source/* _Source/* 2>/dev/null | grep -i "http" | head -5# Expected: bundled docs and/or URLs surfaced — note what you found- ✅ You know whether docs are bundled, online-only, or both.
-
Copy bundled docs into the docs vault (Step B).
Terminal window mkdir -p Admin-Local/2-Docs/1-VendorDocsfind . -maxdepth 2 \( -iname "*.pdf" -o -iname "readme*" -o -iname "install*" -o -iname "changelog*" \) \-exec cp {} Admin-Local/2-Docs/1-VendorDocs/ \; 2>/dev/nullls -lh Admin-Local/2-Docs/1-VendorDocs/# Expected: any shipped PDF/README/INSTALL/CHANGELOG files listed in the vault- ✅ Bundled docs are mirrored into
Admin-Local/2-Docs/1-VendorDocs/.
- ✅ Bundled docs are mirrored into
-
Generate
00-INDEX.mdfor the vendor docs vault.Terminal window cat > Admin-Local/2-Docs/1-VendorDocs/00-INDEX.md << 'EOF'# Vendor Documentation Index## Bundled filesEOFls -1 Admin-Local/2-Docs/1-VendorDocs/ 2>/dev/null | grep -v '^00-INDEX.md$' >> Admin-Local/2-Docs/1-VendorDocs/00-INDEX.md[ -f Admin-Local/2-Docs/1-VendorDocs/online/source-urls.txt ] && {echo "" >> Admin-Local/2-Docs/1-VendorDocs/00-INDEX.mdecho "## Online URLs" >> Admin-Local/2-Docs/1-VendorDocs/00-INDEX.mdcat Admin-Local/2-Docs/1-VendorDocs/online/source-urls.txt >> Admin-Local/2-Docs/1-VendorDocs/00-INDEX.md}# Expected: 00-INDEX.md lists every mirrored file and any indexed URLs- ✅
Admin-Local/2-Docs/1-VendorDocs/00-INDEX.mdexists and lists bundled + online sources.
- ✅
-
If the vendor docs are online-only (more than a handful of pages), mirror them locally so later phases can
grepthem instead of burning live fetches. Skip this when the vendor ships PDFs/README/docs in the archive.Terminal window # From the scraper tool's directory (see its README for the full contract):python3 -m venv .venv && source .venv/bin/activatepip install -r requirements.txtplaywright install chromium # only needed for the JS-rendered scraper# Static HTML docs (interactive prompt):python3 simple_scraper.py# JS-rendered docs (positional args: <docs-url> <output-dir> <max-pages>):python3 docs_scraper.py "<VENDOR_DOCS_URL>" "Admin-Local/2-Docs/1-VendorDocs/scraped" 100# Expected: pages mirrored into Admin-Local/2-Docs/1-VendorDocs/scraped for later grep- ✅ The scraped docs land under a project-local
Admin-Local/2-Docs/1-VendorDocs/scraped/path.
If you only have URLs and are not scraping yet, index them first:
Terminal window mkdir -p Admin-Local/2-Docs/1-VendorDocs/onlinegrep -RhoE 'https?://[^ )"]+' . | grep -Ei 'docs|documentation|help|support' \| sort -u > Admin-Local/2-Docs/1-VendorDocs/online/source-urls.txt# Expected: source-urls.txt lists docs URLs, or is empty if none were found- ✅ Online-only docs are indexed locally and scraped when the vendor docs are too large to grep live.
- ✅ The scraped docs land under a project-local
-
Read all documentation and extract eight categories into
CLAUDE.md(Step C — agents must read every file found):- Server requirements — PHP version, extensions, memory limits
- Installation steps — anything beyond standard Laravel
- Admin panel features — super-admin sections and settings
- Cron jobs required — scheduler, queue workers
- API documentation — endpoints, auth, webhooks
- Third-party integrations — payment, mail, storage services
- Known issues / gotchas — FAQ or troubleshooting
- Update instructions — vendor’s recommended update path
- ✅ Every category above is filled in or explicitly marked “not documented.”
-
Sweep for plugins and addons. Many CodeCanyon apps sell or bundle plugins separately — detect them now and hold installation until the base app runs (Phase 6+).
Terminal window echo "=== Plugin / Addon Detection ==="# Plugin directories shipped in the treefor d in plugins addons modules extensions packages Modules Plugins; do[ -d "$d" ] && echo " 📂 $d/ ($(ls -1 "$d" | wc -l | tr -d ' ') items):" && ls -1 "$d" | head -10done# Plugin-like Composer packagesecho "--- composer.json plugin-like packages ---"grep -iE "plugin|addon|module|extension" composer.json 2>/dev/null | head -10# Plugin / marketplace routesecho "--- plugin routes ---"grep -iE "plugin|addon|module|marketplace|extension" routes/*.php 2>/dev/null | head -5# Separate plugin ZIPs in the source archiveecho "--- plugin ZIPs in the source archive ---"find _source/ -maxdepth 2 -name "*.zip" 2>/dev/null | grep -iv "codecanyon\|main\|source" | head -10# Expected: any shipped/separate plugins surfaced for the action table belowFinding Action Bundled plugins Note which are included; they may need activation in the admin panel (Phase 6). Separate plugin ZIPs Stage them under the source archive’s plugins/for later install.Available for purchase Note on the CodeCanyon listing; record in CLAUDE.mdas “available but not purchased.”Installation Defer to post-deploy (Phase 6+). Never install plugins before the base app runs. - ✅ Any plugins/addons are surfaced and slotted into the action table; none installed yet.
-
Record the essentials in
CLAUDE.mdunder a## Vendor Documentationheading: source/version/author, server requirements, admin-panel features, cron jobs, API/webhooks, known gotchas, update process, and any plugins/addons (bundled, for-purchase, or installed). Defer installing any plugin until the base app runs (Phase 6+).- ✅
CLAUDE.mdhas a## Vendor Documentationsection covering all eight extraction categories plus plugins.
- ✅
4. Back up the pristine originals (vendor files only)
Section titled “4. Back up the pristine originals (vendor files only)”Snapshot the untouched vendor-shipped top-level files into a versioned vault — an offline, easy-to-eyeball record of “exactly as shipped.” This is a convenience copy: the true pristine baseline is already preserved in git via the author/v<VERSION> branch + author-v<VERSION> tag (the frozen vendor import). So this vault only needs the files the vendor shipped — never the project/AI-overlay files you add on top.
-
Copy each vendor-shipped top-level file with a version suffix. The exclude list below drops the project + AI-overlay files we add (so they never reach the vault), then writes metadata beside the backup.
Vendor root scripts such as
server-requirements.php,extract-translations.php,phpinfo.php, and the publicindex.phpare vendor/framework-owned, not Zaj-owned. Do not rename them with theZaj-prefix. List them in theZaj-CUSTOMIZATIONS.mdownership map so any unmarked root file is understood as vendor/framework territory.Terminal window export VERSION="x.y.z" # replace with the vendor's actual version, e.g. "10.4"mkdir -p "Admin-Local/3-Versions/1-v${VERSION}/1-Originals"# Files WE add on top of the vendor tree — never snapshot these.# (Personal/secret files are gitignored by exact name; their -vX copies would bypass that.)OVERLAY_EXCLUDE="CLAUDE.md CLAUDE.local.md AGENTS.md GEMINI.md \Zaj-CUSTOMIZATIONS.md Zaj-PROGRESS.md Zaj-BACKLOG.md Zaj-CHANGELOG.md \_onboarding-summary.md .mcp.json .env .env.tpl"for f in $(ls -A | while read x; do [ -f "$x" ] && echo "$x"; done); docase " $OVERLAY_EXCLUDE " in*" $f "*) echo " ⏭️ skip (overlay/secret, not vendor): $f"; continue ;;esaccp "$f" "Admin-Local/3-Versions/1-v${VERSION}/1-Originals/${f}-v${VERSION}"echo " 📦 snapshot: $f"donecat > "Admin-Local/3-Versions/1-v${VERSION}/Originals-v${VERSION}.md" << EOF# Originals backup — v${VERSION} (vendor-shipped files only)- **Date:** $(date +%Y-%m-%d)- **Source ZIP:** _source/[PROJECT]-v${VERSION}.zip (or CodeCanyon download name)- **Path:** Admin-Local/3-Versions/1-v${VERSION}/1-Originals/- **Scope:** vendor-shipped top-level files ONLY (overlay/secret files excluded)- **True baseline:** git \`author/v${VERSION}\` branch + \`author-v${VERSION}\` tag- **Checksum (optional):** shasum -a 256 _source/*.zipEOF# Expected: only vendor files copied; overlay/secret files printed as ⏭️ skip; Originals-vX.md present- ✅
Admin-Local/3-Versions/1-v<VERSION>/1-Originals/holds version-suffixed copies of vendor files only; overlay/secret files were skipped;Originals-v<VERSION>.mdrecords date, ZIP name, path, and notes the git baseline.
- ✅
-
Belt-and-suspenders: gitignore the
-vXsnapshot copies of any root-gitignored file. Even if a future edit re-introduces a broadcp, these rules guarantee a secret/personal file can never be committed through its snapshot copy. Append once.Terminal window if ! grep -q '3-Versions/\*\*/.env' .gitignore 2>/dev/null; thencat >> .gitignore << 'EOF'# Snapshot guard — never commit -vX copies of gitignored personal/secret files.# (The root rules ignore the EXACT names; the `-vX` suffix would otherwise slip past them.)Admin-Local/3-Versions/**/.envAdmin-Local/3-Versions/**/.env-*Admin-Local/3-Versions/**/.env.*Admin-Local/3-Versions/**/CLAUDE.local.md*Admin-Local/3-Versions/**/_onboarding-summary.md*/.env-*/.env.*/CLAUDE.local.md*/_onboarding-summary.md*EOFfi# Expected: the snapshot guard block appended once (idempotent — re-running won't duplicate it)- ✅
.gitignorecontains the snapshot-copy guards (Admin-Local/3-Versions/**/.env*,/.env-*,/.env.*,/CLAUDE.local.md*,/_onboarding-summary.md*).
- ✅
-
The vendor’s own
.env(if the archive shipped one) belongs in the gitignored credential vault, not in1-Originals/. Keep it as a reference of the vendor’s default config keys without risking a committed snapshot.Terminal window # If the vendor shipped a real .env (rare, but some archives do):if [ -f .env ] && grep -qiE 'codecanyon|vendor|demo' .env 2>/dev/null; thenmkdir -p Admin-Local/1-Project/2-Vaultcp .env Admin-Local/1-Project/2-Vault/.env.vendor-originalecho " 🔐 vendor .env → vault/.env.vendor-original (gitignored vault, NOT 1-Originals)"fi# Expected: any vendor-shipped .env lands in the gitignored vault as .env.vendor-original- ✅ Any vendor-shipped
.envis preserved asAdmin-Local/1-Project/2-Vault/.env.vendor-original(gitignored) — never in1-Originals/.
- ✅ Any vendor-shipped
-
Verify no secret/personal snapshot is stageable. After the snapshot,
git statusmust show no-vXcopy of a secret/personal file staged or untracked-but-trackable.Terminal window git add -A --dry-run 2>/dev/null \| grep -iE '\.env(-|\.|$)|CLAUDE\.local\.md|_onboarding-summary\.md' \&& echo " ❌ a secret/personal snapshot is stageable — fix the exclude/gitignore above" \|| echo " ✅ git status clean of .env-* / CLAUDE.local.md-* / _onboarding-summary.md-* snapshots"- ✅
git status(viagit add --dry-run) shows no.env-*,CLAUDE.local.md-*, or_onboarding-summary.md-*snapshot staged or trackable.
- ✅
5. Inspect & classify author vendor patches (the seam)
Section titled “5. Inspect & classify author vendor patches (the seam)”CodeCanyon authors frequently modify third-party packages inside vendor/. Some of those edits are load-bearing patches (a real bug fix the app depends on — if composer install overwrites them, install breaks). Others are license-injection backdoors disguised as patches. You must find every diff, read and classify each one, and then decide — preserve, strip, or surgically split — on evidence, not on the file count.
-
If no
vendor/shipped, there’s nothing to compare — you’llcomposer installin Phase 3. Skip to the gate.- ✅ Confirmed
vendor/absent → nothing to preserve.
- ✅ Confirmed
-
If
vendor/shipped, compare it against a fresh install in a temp directory (never the project root). This produces the review list — not a keep list.Terminal window mkdir -p /tmp/vendor-comparecp composer.json composer.lock /tmp/vendor-compare/ 2>/dev/null( cd /tmp/vendor-compare && composer install --no-scripts --no-autoloader 2>&1 | tail -3 )diff -rq vendor /tmp/vendor-compare/vendor 2>/dev/null \| grep -v "autoload\|installed.json\|installed.php\|\.git" | tee /tmp/vendor-diff.txtecho "=== $(wc -l < /tmp/vendor-diff.txt | tr -d ' ') differing files to REVIEW (not to keep) ==="# Expected: a count of differing files (often 0). Every line is a file you must read.- ✅ A review-list count printed — proceed to the core-package guard before classifying anything.
-
Run the core-package guard. Diffs inside the framework core (
laravel/framework,symfony/*,illuminate/*) are SUSPECT by default — legitimate author patches almost never live there; nulled/license-injection backdoors almost always do. Partition the review list.Terminal window echo "=== 🔴 CORE-FRAMEWORK diffs — SUSPECT, never auto-preserve ==="grep -iE 'vendor/(laravel/framework|symfony/|illuminate/)' /tmp/vendor-diff.txt | tee /tmp/vendor-diff-core.txtecho "=== 🟡 NON-CORE diffs — could be a real author patch, still must read ==="grep -ivE 'vendor/(laravel/framework|symfony/|illuminate/)' /tmp/vendor-diff.txt | tee /tmp/vendor-diff-noncore.txt# Expected: core diffs isolated. If /tmp/vendor-diff-core.txt is non-empty → treat as a likely backdoor until proven otherwise.- ✅ Review list split into 🔴 core (suspect) and 🟡 non-core. Any non-empty core file triggers the signature scan in step 4 and is not a preserve candidate.
-
Scan every differing file for backdoor signatures before you classify it as a patch. These are the fingerprints of the nulled-license / phone-home injections shipped in real CodeCanyon archives. Run against the actual differing files (both core and non-core).
Terminal window # Build the list of differing files as real paths under vendor/awk '/^Files /{print $2}' /tmp/vendor-diff.txt | sed 's#/tmp/vendor-compare/##' | sort -u > /tmp/vendor-diff-files.txt# Fallback if your diff lines read "Files vendor/... and /tmp/... differ":[ -s /tmp/vendor-diff-files.txt ] || grep -oE 'vendor/[^ ]+' /tmp/vendor-diff.txt | sort -u > /tmp/vendor-diff-files.txtecho "=== Backdoor-signature scan over $(wc -l < /tmp/vendor-diff-files.txt | tr -d ' ') file(s) ==="while read -r f; do[ -f "$f" ] || continuehits=$(grep -nE \'chr\(|\\x[0-9a-f]{2}|[0-9]{2,3}[,.|][0-9]{2,3}[,.|][0-9]{2,3}|getScript|eval\(|gzinflate|gzuncompress|str_rot13|base64_decode|getCourant|HeaderCodec|\.dist["'\'']|envato\.|verify\.js|product_id|->name *== *["'\'']\s*(login|register)' \"$f")[ -n "$hits" ] && { echo " 🔴 SUSPECT: $f"; echo "$hits" | sed 's/^/ /'; }done < /tmp/vendor-diff-files.txtecho "=== scan done — any 🔴 line = do NOT preserve; treat as malicious until disproven ==="The signatures, and why each matters:
Signature What it betrays chr(/\xNN/ decimal- or hex-delimited number blobs (72,84,84,80…)Obfuscated string assembly hiding a URL or payload (the WorkDo HeaderCodec.distdecoder)getScript/eval(/gzinflate/gzuncompress/str_rot13/base64_decodeRuntime fetch or decode-and-execute of remote/packed code getCourant/HeaderCodec/ hidden methods shadowing framework coreAuthor-added methods that don’t exist in clean upstream — the route-name oracle that gates injection Vendor phone-home domains ( envato.,verify.js,product_id, any non-app host)License/anti-null beacon calling home Gating on login/registerroute names (->name == 'login')Injection that fires only on credential-entry pages (hardest to notice, highest harm) - ✅ Every 🔴 hit is recorded. A signature hit means strip, never preserve — even if the file is also a “patch.”
-
Validate before trust — read, diff-vs-clean, classify. For each file still on the review list, do all four (never skip on a small count):
-
Read the live shipped file end-to-end. You are looking for added methods, injected output, encoded blobs, and anything that touches the network or the response body.
-
Diff it against clean upstream (the temp install you already built) to see exactly what the author changed — not the whole file:
Terminal window f="vendor/<vendor>/<package>/<file>.php" # one path from /tmp/vendor-diff-files.txtdiff -u "/tmp/vendor-compare/$f" "$f" | sed -n '1,120p'# Expected: the precise added/removed lines. Telemetry shows as injected output/network calls;# a real patch shows as a logic fix (null guard, type cast, bug workaround). -
Classify the change as one of:
Class What it looks like in the diff Decision Functional patch A real bug fix / behavior change the app needs (null guard, signature fix, compat shim) — no network call, no encoded blob, no response injection Preserve (step 6) Telemetry / malicious Phone-home, license beacon, response-body injection, encoded payload, hidden oracle method Strip — clean upstream is the fix (step 6); never preserve Mixed One file holds both a real fix and an injected beacon Surgical edit — re-apply only the functional hunk onto clean upstream; drop the malicious hunk -
Cross-check core files hard. If the file is in the 🔴 core set and you think it’s a functional patch, raise the bar: a genuine app-required edit to
laravel/frameworkorsymfony/*is rare. Confirm the same fix isn’t already available by bumping the locked version, and get a second pair of eyes before preserving anything in core.
- ✅ Every review-list file is classified (functional / telemetry / mixed) with the diff evidence captured.
-
-
Act on the classification, then clean up the temp dirs.
Terminal window # FUNCTIONAL PATCH → preserve into the tracked customization area (this is the real bug-fix case)mkdir -p "resources/vendor-customizations/<vendor>/<package>"cp -r "vendor/<vendor>/<package>/src" "resources/vendor-customizations/<vendor>/<package>/"# TELEMETRY / MALICIOUS → preserve NOTHING. Do not copy it anywhere "to keep".# The fix is a clean upstream copy, applied in §5.5 (composer reinstall) — NOT composer install (see caution).# MIXED → re-apply only the functional hunk onto the clean upstream copy, then preserve that edited file.[ -d /tmp/vendor-compare ] && rm -rf /tmp/vendor-comparerm -f /tmp/vendor-diff*.txt# Expected: only functional/mixed patches land in resources/vendor-customizations/; temp dirs removed- ✅ Functional patches preserved; telemetry/malicious diffs preserved nowhere; mixed files split surgically.
-
Append findings to
Zaj-CUSTOMIZATIONS.md— one row per reviewed file, including the ones you deliberately did not keep (the audit trail matters most for those):## Vendor patches (vX.X.X)| Package | File | Classification | Decision | Preserved in / removed by || --- | --- | --- | --- | --- || vendor/foo/bar | src/Baz.php | functional | preserve | resources/vendor-customizations/foo/bar/ || vendor/laravel/framework | …/Routing/Router.php | telemetry (backdoor) | strip | clean upstream via composer reinstall (§5.5) |If you find a backdoor, also record an incident block (what it does, exposure, remediation, and a “re-scan after every vendor update” hard rule) — see the worked example below.
- ✅
Zaj-CUSTOMIZATIONS.mdlists every reviewed file with classification + decision (or states “0 differences”).
- ✅
5.5. Boot, clean the seam, smoke-test (static diff is not proof)
Section titled “5.5. Boot, clean the seam, smoke-test (static diff is not proof)”A diff -rq tells you which files changed on disk — it cannot tell you whether a clean tree still boots and serves the same routes. A telemetry-only diff is safe to overwrite; a load-bearing patch is not, and only a runtime test distinguishes them. Prove it.
-
Boot the app and baseline it — BEFORE any clean/reinstall. Use Herd or
php artisan serveand capture a baseline you can compare against.Terminal window php artisan route:list 2>/dev/null | wc -l # baseline route count, e.g. 919php artisan route:list 2>/dev/null | sort > /tmp/routes-before.txt# Boot it (one of):# herd link <project> && herd secure <project> # then open https://<project>.test# php artisan serve # http://127.0.0.1:8000With the backdoor signature still present, also confirm the beacon is observable (so you can prove it’s gone after):
Terminal window grep -rEl 'getCourant|envato\.|verify\.js' vendor/ 2>/dev/null # baseline: lists the tampered file(s)- ✅ Baseline captured: route count,
/tmp/routes-before.txt, and the list of files still carrying the signature.
- ✅ Baseline captured: route count,
-
Smoke-test the public seam BEFORE the clean — visit each surface and note that it loads (and, for the backdoor case, that the beacon fires). Cover home, login, register, dashboard, and the admin entry point — the exact pages a credential-injection backdoor targets.
Terminal window for path in / /login /register /dashboard /admin; docode=$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:8000${path}")echo " BEFORE ${path} → ${code}"done# Expected: a working/expected status per route (200/302/401 as appropriate) — this is your baseline shape.(When a browser is available, use a Playwright run instead so JS-injected
<script src="…verify.js">is actually observable in the rendered page / network tab.)- ✅ Baseline behavior recorded for every seam page.
-
Clean the seam — reinstall the affected packages (or the whole tree). This is the step that actually removes a tampered/backdoored file;
composer installwould not (see the danger above).Terminal window # Targeted (preferred when you know which packages were tampered):composer reinstall laravel/framework symfony/http-foundation # ← your actual flagged packages# Full rebuild (when in doubt, or many packages diff):# rm -rf vendor && composer install# grep-verify the signature is gone:grep -rEl 'getCourant|envato\.|verify\.js|HeaderCodec' vendor/ 2>/dev/null \&& echo " ❌ signature STILL present — reinstall did not cover it" \|| echo " ✅ signature gone from vendor/"- ✅
grep -rovervendor/returns no backdoor signature.
- ✅
-
Re-apply only the functional patches you classified in §5 step 6 (Mixed/Functional), restoring them onto the now-clean tree from
resources/vendor-customizations/. Re-apply nothing from the telemetry/malicious set.- ✅ Clean tree + load-bearing patches only; no stripped beacon re-introduced.
-
Boot again and compare — AFTER the clean. The gate is AFTER ≥ BEFORE: equal-or-greater route count, no new boot errors, and the backdoor signature gone.
Terminal window php artisan route:list 2>/dev/null | wc -l # must equal (or exceed) the baselinephp artisan route:list 2>/dev/null | sort > /tmp/routes-after.txtdiff /tmp/routes-before.txt /tmp/routes-after.txt && echo " ✅ route set unchanged" || echo " ⚠️ routes changed — investigate the diff above"for path in / /login /register /dashboard /admin; docode=$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:8000${path}")echo " AFTER ${path} → ${code}"done# Expected: same/better route count, same seam-page statuses, no new errors, signature gone.- ✅ Gate passes: AFTER route count ≥ BEFORE, seam pages unchanged, no new boot errors, signature confirmed gone.
The shipped vendor/ stays exactly where it is — git-ignored later, never deleted — until this §5.5 gate proves a clean tree boots and serves the same seam. Telemetry/backdoor diffs are never preserved; load-bearing patches are re-applied surgically and proven by the boot-clean-boot test, not by a static diff.
Checklist
Section titled “Checklist”Do not mark this step done until every box below is checked.
- 🔀 Laravel root verified — all essential files/dirs present, no nesting, no unextracted inner ZIPs.
- 🤖 Vendor docs captured — copied to
Admin-Local/2-Docs/1-VendorDocs/; key facts inCLAUDE.md. - 🤖 Pristine VENDOR backup taken — vendor-shipped files only in
Admin-Local/3-Versions/1-v<VERSION>/1-Originals/(overlay/secret files excluded);-vXsnapshot guards added to.gitignore;git statusclean of any.env-*/CLAUDE.local.md-*/_onboarding-summary.md-*snapshot; vendor.env(if any) in vault as.env.vendor-original. - 🤖 Every
vendor/diff classified — core-package guard run, backdoor-signature scan run, each file read + diffed-vs-clean and labelled functional / telemetry / mixed inZaj-CUSTOMIZATIONS.md(or confirmed: 0 differences). No diff was auto-preserved. - 🤖 Backdoors stripped, never preserved — any signature-hit / phone-home / response-injection file copied nowhere; only functional/mixed patches landed in
resources/vendor-customizations/. - 🤖 Seam proven by boot-clean-boot — baseline routes captured →
composer reinstall(notinstall) the tampered packages → routes equal-or-greater, seam pages unchanged, backdoor signaturegrep -rconfirmed gone (AFTER ≥ BEFORE). - 🤖 Shipped
vendor/intact until proven — not renamed/deleted; only the verified-clean tree advances to Phase 3.