Sample report

What a Code Audit report looks like, start to finish.

A complete sample report for a fictional application, so you can see the structure and depth before you order one. The application is invented. The vulnerability data comes from a real composer audit run.

Sample for a fictional application. Acme Logistics and Acme Fleet Portal do not exist.

Sample report, fictional application

"Acme Fleet Portal" and Acme Logistics do not exist. The code, metrics and configuration findings in this report are invented to show the structure and depth of a Refberg Code Audit. The vulnerability data is real: on 24 October 2026 we ran composer audit against a lock file with the package versions listed in section 4.1. The advisory counts, CVE identifiers and fixed versions come from that run. Prices are examples based on our published starting prices.

Code Audit report

ClientAcme Logistics Ltd (fictional)
ApplicationAcme Fleet Portal: booking, invoicing and a JSON API for the driver mobile app
Audit sizeM (50,000 to 200,000 lines of PHP code)
Accessread-only repository, staging and production .env (redacted), server configuration files, crontab
Date24 October 2026
Prepared byRefberg

1. Summary for management

The portal works and earns money, but it runs on software that stopped receiving security fixes between 2022 and 2025. Three problems need action this week, before any upgrade work:

  1. The staging server can be taken over from the internet. It is publicly reachable, runs in debug mode and has a component with a published remote code execution flaw (CVE-2021-3129). Staging holds a weekly copy of the production database, including customer names, addresses and invoices.
  2. Production passwords and the application's encryption key are in the Git history. Anyone who has, or had, a copy of the repository can read them. They have not been changed since 2021.
  3. Backups are stored on the same server as the database and have never been restored. A disk failure or ransomware on that server would take the data and the backups together.

All three can be closed in one to two days without changing application code (section 6, stage 0).

The longer-term problem is the platform itself: PHP 7.4 (end of life 28 November 2022) and Laravel 6 (security fixes ended 6 September 2022). The dependency scan found 114 published security advisories in 20 packages. Some can be fixed with patch releases inside Laravel 6. Most need the upgrade.

There are almost no automated tests (6.1% line coverage, none on the six business-critical paths), so any change today is a guess. Our recommendation is option A: build a safety net first, then upgrade in two fixed-price stages to Laravel 13 on PHP 8.4, over about four months, with Care Standard from month 5. Option B (stay on Laravel 6 with patched PHP) costs less this year, and section 6 explains when it makes sense.

2. Scope and method

In scope: application code in the repository (main branch at commit 4f1c9e2), composer.json and composer.lock, Git history, configuration files, server configuration supplied by the client, deploy script.

Tools: composer audit (Composer 2, Packagist advisory database), PHPStan 2.x without the Laravel extension (Larastan for Laravel 6 exists only for the old PHPStan 0.12 line, so we re-run with Larastan after the framework upgrade), Rector 2.x in dry-run mode, PHP_CodeSniffer with PHPCompatibility, PHPUnit with PCOV for coverage, gitleaks for secrets in history, git log for change frequency. Then a manual review of routes, authentication, file uploads, PDF generation, queues and scheduled jobs.

Not in scope: penetration testing, load testing, review of the mobile app, the payment provider's systems, or the network setup of the hosting provider. We did not attempt to exploit anything. Where we say a vulnerability is exploitable, we mean its published preconditions are met in your configuration.

3. Risk map

Likelihood and impact are rated 1 (low) to 3 (high). Priority: P0 this week, P1 within the first stage, P2 during the upgrade.

IDRiskLikelihoodImpactPriority
R1Remote code execution on public staging via Ignition (CVE-2021-3129); staging holds production personal data33P0
R2APP_KEY, database and mail passwords exposed in Git history since 202133P0
R3Backups on the same server, never restored23P0
R4PDF generation with remote resources enabled on dompdf 0.8.6 (CVE-2022-28368, remote code injection via fonts)23P0
R5No tests on critical paths; 9 of 42 existing tests fail32P1
R6PHP 7.4 from Ubuntu 20.04 without Ubuntu Pro: no runtime security updates since May 202523P1
R7Laravel 6 past end of life; 5 framework advisories have no fix on the 6.x line22P2
R8Abandoned or superseded packages (SwiftMailer, Faker, Ignition 1.x)22P2
R9One former developer held all knowledge; no runbook, deploy is a shell script run by hand32P1

4.1 Dependencies

Result: 114 advisories in 20 packages: 13 critical, 44 high, 50 medium, 7 low. 110 of them are in production dependencies. Selected packages:

PackageInstalledAdvisoriesNotable advisoriesFirst fixed versionFixable inside Laravel 6?
laravel/framework6.20.011CVE-2021-21263 (query binding), CVE-2021-43808 (Blade XSS), CVE-2024-52301 (environment via query string)6.20.45 for 6 of 11partly: 6.20.45
facade/ignition (dev)1.16.33CVE-2021-3129 (remote code execution)1.16.14 (1.16.15 for all three)yes; and must not be installed in production
dompdf/dompdf0.8.614CVE-2022-28368 (remote code injection via fonts), CVE-2021-3838 (deserialization)1.2.1 / 2.0.x / 3.1.6partly: needs a major laravel-dompdf upgrade
guzzlehttp/guzzle6.5.514CVE-2022-31090, CVE-2022-31091 (credentials kept after a redirect to another origin)6.5.8 for 5 of 14partly
firebase/php-jwt5.2.02CVE-2021-46743 (key/algorithm confusion)6.0.0yes, with code changes (new API)
league/flysystem1.0.702CVE-2021-32708 (race condition in file writes)1.1.4yes
phpoffice/phpspreadsheet1.15.029XXE in XLSX reader (CVE-2024-45293, CVE-2024-48917)1.29.4 and later (for the two XXE issues)probably, via a newer maatwebsite/excel 3.1.x; to confirm in stage 1
swiftmailer/swiftmailer6.2.31CVE-2024-28859 (deserialization gadget)6.2.5yes, but the package is abandoned

Abandoned or superseded: swiftmailer/swiftmailer is marked abandoned on Packagist in favor of symfony/mailer (Laravel 9 switched to it). fzaninotto/faker is marked abandoned. facade/ignition was superseded by spatie/laravel-ignition; its last release was in January 2023. fideloper/proxy has had no release since February 2022; its functionality moved into Laravel 9.

Why this matters more than the count: the dangerous items are the ones whose preconditions you meet. In this application those are Ignition on staging (R1, debug mode is on), dompdf with enable_remote => true in config/dompdf.php (R4, the invoice template loads the logo by URL), and php-jwt in the mobile API (we could not confirm exploitability without the key setup; see 4.4).

4.2 Tests and CI

  • 42 PHPUnit tests, 9 failing on main. The failures date from 2023 and the tests were not run since.
  • Line coverage 6.1%. Covered: two helper classes and the login form.
  • Critical paths with no test: booking creation, price calculation, invoice generation (PDF), payment callback, driver API authentication, monthly invoice export (XLSX).
  • No CI pipeline. deploy.sh runs git pull, composer install (without --no-dev, which is why Ignition and Faker are on the servers), and php artisan migrate --force on production, in that order, with no backup step.

4.3 Static analysis and upgrade readiness

CheckResult
PHPStan level 0 (no Laravel extension)286 errors. 41 are calls to undefined methods or functions, at least 7 of them on code paths reachable from routes
PHPStan level 5 (no Laravel extension)1,912 errors. We recommend a baseline at level 5 and no new errors from stage 1 onward
PHPCompatibility, target PHP 8.437 errors, 112 warnings
Rector dry-run, PHP 8.1 rule set214 files would change; about 80% are mechanical (constructor promotion, str_contains, null-safe calls)

Blockers for PHP 8, in the order they would surface:

  1. app/Libraries/LegacyCsv.php uses each(), removed in PHP 8.0. This class is used by the monthly import: a fatal error on the first run after the upgrade.
  2. 23 classes use dynamic properties (deprecated in PHP 8.2), mostly in app/Services/Pricing/.
  3. ${…} string interpolation (dollar sign outside the braces) in 31 places (deprecated in PHP 8.2).
  4. Implicitly nullable parameters in 58 method signatures (deprecated in PHP 8.4).
  5. Laravel 6 is supported on PHP 8.0 at most, so the PHP and framework upgrades have to be interleaved (section 6).

Hotspots

Files changed most often in the last 24 months, by size.

FileLinesCommits (24 months)Note
app/Http/Controllers/BookingController.php2,14061pricing, validation and email in one class; first candidate for characterization tests
app/Services/InvoiceService.php1,31038PDF generation, numbering, VAT; numbering uses MAX(id)+1 without a lock
app/Http/Controllers/Api/DriverController.php87029JWT handling written by hand around php-jwt

4.4 Configuration and security

IDFindingEvidenceRecommendation
C1Staging is public, APP_DEBUG=true, Ignition installedstaging.acme-fleet.example answers without authenticationrestrict by IP or HTTP auth today; set APP_DEBUG=false; deploy with --no-dev
C2Staging database is a weekly copy of production with personal datacron job refresh-staging.shreplace with an anonymized copy; this is also a GDPR issue
C3Secrets in historygitleaks: .env.production committed in March 2021 and removed in a later commit; contains APP_KEY, DB_PASSWORD, MAIL_PASSWORDrotate all three; see the note on APP_KEY below
C4Trusted proxies set to *app/Http/Middleware/TrustProxies.phplist the load balancer address; today any client can set its own IP via X-Forwarded-For, which defeats the login rate limit
C5Session cookie without Secure flag, no HSTSSESSION_SECURE_COOKIE unset; no Strict-Transport-Security headerset both after confirming HTTPS everywhere
C6dompdf remote resources enabledconfig/dompdf.php: enable_remote => trueembed the logo as a local file and disable remote
C7Queue worker started from cron every minute, as rootcrontab -l for rootrun under Supervisor or systemd as the application user
C8Backups: nightly mysqldump to /var/backups on the same servercrontab, disk listingoff-site copy, encryption, monthly restore test
C9JWT verification accepts the algorithm from the token headerDriverController::decodeToken()covered by the php-jwt 6 upgrade; fix the handler at the same time

About rotating APP_KEY

Laravel uses this key for encrypted cookies, signed URLs and any value your code encrypts with Crypt::encrypt() or encrypt(). Changing it logs out all users and makes those values unreadable unless they are re-encrypted. The portal stores one such field (drivers' national ID numbers, encrypted in a model mutator). Stage 0 includes a short script that decrypts with the old key and re-encrypts with the new one.

4.5 Runtime

The production server runs Ubuntu 20.04 with the distribution's PHP 7.4 package. Ubuntu's standard security maintenance for 20.04 ended in May 2025. The server is not attached to Ubuntu Pro, so the PHP package and the rest of the operating system get no security updates.

Attaching the server to Ubuntu Pro would resume them until May 2030 for packages in Ubuntu's main repository, which includes PHP 7.4. This fixes the runtime only; it does nothing for Laravel or the packages in section 4.1.

4.6 What is in good shape

  • Database migrations rebuild the full schema from an empty database without errors, so a reproducible environment needs no manual SQL.
  • The payment callback verifies the provider's signature before it changes an order.
  • The application uses Laravel's own queues, scheduler and mail components without custom replacements, so the upgrade path follows the official upgrade guides.

5. Priorities

PriorityWhatWhy first
P0, this weekC1, C2, C3, C6, C8exploitable now or would make an incident unrecoverable
P0, this weeklaravel/framework 6.20.0 → 6.20.45, league/flysystem → 1.1.4, guzzlehttp/guzzle → 6.5.8, guzzlehttp/psr7 → 1.9.1patch releases, same major version, low risk even without tests
P1, stage 1tests on the six critical paths, CI, runbook, monitoring, C4, C5, C7, Ubuntu Pro or a new servernothing else can be changed safely until this exists
P2, stages 2–3PHP and Laravel upgrade, replacement of abandoned packages, C9needs the test net from stage 1

6. Plan and fixed prices

Prices in this sample are examples based on our published starting prices. A real audit ends with a quote like this one, with prices fixed for the scope set out in it.

Option A (recommended): safety net, then Laravel 13 on PHP 8.4

Stages 1–3 together: one Takeover M and two Upgrade M stages, about four months. End state: Laravel 13 (security fixes until 17 March 2028) on PHP 8.4 (security support until 31 December 2028), tests on every critical path, CI on every change, and a runbook another developer can use.
StageScopeDurationFixed pricePayment
0. Containitems marked P0 in section 5; your team can do it in 1–2 days with our step-by-step instructions, or we do it as the first week of stage 11 weekincluded in stage 1with stage 1
1. TakeoverDocker environment that reproduces production; CI; characterization tests for the six critical paths; PHPStan baseline at level 5; off-site backups with a restore test; monitoring; runbook and architecture map; access inventory4 weeks (size M)€3,000100% at order
2. Upgrade ILaravel 6 → 9 and PHP 7.4 → 8.1 in steps (6 → 8 on PHP 7.4, then PHP 8.0, then Laravel 9 on PHP 8.1); SwiftMailer → Symfony Mailer; php-jwt 6 with the JWT handler fix; dompdf 2.x; Guzzle 7; Faker → fakerphp/faker; LegacyCsv rewrite6 weeks€6,00030% / 70% after green CI and your sign-off on staging
3. Upgrade IILaravel 9 → 13 and PHP 8.1 → 8.4; Ubuntu 24.04 server; phpspreadsheet and dompdf to current versions; removal of fideloper/proxy; dynamic properties and deprecations cleared6 weeks€6,00030% / 70% after green CI and your sign-off on staging
Care Standardmonthly updates of PHP and dependencies, monitoring, 8 hours of changes, critical response within 4 business hours, quarterly reviewfrom month 5€800/monthmonthly

Option B: keep it safe on the current version for 12 months

Stage 0 and stage 1 as above (€3,000), Ubuntu Pro on the existing server for runtime patches (subscription paid by you to Canonical), the patch releases from section 5, and Care Basic at €400 a month.

This keeps the obvious holes closed while you decide about the application's future. It does not remove the 5 Laravel advisories that have no fix on the 6.x line, the dompdf and phpspreadsheet issues that need major upgrades, or the PHP 8 blockers. After 12 months, option A will cost about the same as today, plus whatever new advisories appear in the meantime.

Option B makes sense if the portal will be replaced or retired within 18 months, or if the budget for this year is fixed.

Option C: rewrite

We don't recommend it. The portal's business rules (pricing, invoice numbering, VAT handling) exist only in the code, and none of them are tested. A rewrite would have to rediscover them, and the old system would still need option B while the new one is built.

7. What we need from you to start

  1. A decision on option A or B.
  2. For stage 0: someone with admin access to staging and the database who can rotate passwords with us on a call or by email.
  3. For stage 1: named accounts for us in Git hosting and on staging, and a person who can say what the six critical paths should do when the code and the users disagree.

Appendix A: full dependency scan. The complete composer audit output (JSON, all 114 advisories) is delivered with the report as a separate file.

The report and all findings are confidential and covered by the NDA.

How a report this detailed fits the price

Tools do the counting and we read the code. AI coding tools, used with your written consent, speed up the reading and the checks; an engineer reviews every finding before it goes into the report. More in How we work and Security & trust.

Want a report like this for your application?

Code Audit from €800, report in 5–7 business days. Not sure yet? Start with a Debt Check for €300.