← All repos

a-list-demo

Frozen Laravel 6 / PHP 7.2 demo & staging fork of the A-List backend. Last touched Nov 2020 — committed live secrets, an entirely unauthenticated admin panel, an EOL stack, and a deployment status that is still unknown.

Primary stackPHP 7.2 / Laravel 6 Last commit2020-11-02 Repo size446 MB Files1268 Branchmaster Health Critical — 8 P0 / 13 P1

Executive summary

Tech stack

PHP ^7.2 Laravel ^6.2 MySQL / MariaDB Supervisor queue Laravel Mix Sentry 1.9 dompdf 0.8.6 maatwebsite/excel 3.1
CategoryTechnologyVersionNotes
LanguagePHP^7.2EOL 2019-11-30. No security patches.
FrameworkLaravel^6.2 (LTS)LTS support ended 2022-01-25.
DatabaseMySQL / MariaDBMariaDB 10.1 per parhamandco.sql dump headerConnection parhaman_development_alist.
QueueSupervisor + queue:listena-list-supervisorconf.config targets /home/parhaman/public_html/development-alist/artisan.
Frontend buildLaravel Mix / webpackper webpack.mix.jsStandard Mix; no JS framework.
AuthLaravel built-inlaravel/ui ^1.1Registration disabled in routes/web.php — but re-enabled inside /admin (see ALD-03).
PDFbarryvdh/laravel-dompdf^0.8.6Old dompdf line; known RCE/SSRF CVEs, reachable via CreatePdfJob.
Excel I/Omaatwebsite/excel^3.1Wraps phpspreadsheet 1.15.0; used by admin exports/imports.
Error monitoringsentry/sentry-laravel^1.9Old SDK; portal is on 4.x.
Mail trackingjdavidbakr/mail-tracker3.*Recently re-added in final commit "Mailtacker Files".
3rd partyrapidwebltd/php-google-contacts-v3-api^2.0OAuth callback at /contacts in routes/web.php.
Dev toolsfacade/ignition^1.4 (1.16.3 locked)Vulnerable line — CVE-2021-3129 RCE when APP_DEBUG=true (which .env_4-May-2020 has).
Testingphpunit/phpunit^8.0Default Laravel scaffold; no meaningful tests committed.

Architecture

Monolithic Laravel 6 web app served via cPanel/LSAPI behind an .htaccess rewrite that funnels every request through /public/index.php. Eloquent models live flat under app/ (pre-Laravel-8 convention). Background work (queue worker) is driven by Supervisor. Inbound routes split across web.php (public site & offer flow), admin.php (admin panel), api.php (minimal — 15 lines), chat_agent.php, and channels.php.

a-list-demo/ ├── app/ │ ├── *.php 22 Eloquent models (User, Venues, FoodOffers, Signup, Categories, Countries, States, …) │ ├── Http/ │ │ ├── Controllers/ Home, Offer, Signup, VendorSignup, VenueOffer, UserReview, AutoComplete │ │ │ ├── Admin/ AdminController, Dashboard, FoodOffers, Venues, Settings, Audit, UsersPreviews │ │ │ ├── Auth/ Laravel default scaffolding │ │ │ └── ChatAgent/ Chat-agent flows │ │ └── Middleware/ │ ├── Jobs/ Queued jobs (offer review reminders, CreatePdfJob, etc.) │ ├── Mail/, Listeners/, Notifications/, Notifications_2/ (the "_2" copy is suspicious) │ ├── Exports/ Maatwebsite/Excel exports │ └── helpers.php ├── config/ Standard Laravel config (database.php contains a hardcoded password fallback) ├── database/ │ ├── migrations/ 22 migrations + 4 "backup-DD-MM-YYYY" folders │ ├── seeds/ DatabaseSeeder + step1..step10 ad-hoc historical data-fix scripts │ └── factories/ ├── routes/ web.php, admin.php, api.php, chat_agent.php, console.php, channels.php │ + web_15-May-2020.php (dead backup) ├── resources/views/ Blade templates ├── public/ Webroot — also contains info.php, audit_pdf/*.pdf, locales/, assets/ ├── storage/, bootstrap/, tests/ ├── __BACKUPS/ Old code snapshot from Jan 2020 ├── parhamandco.sql Schema + data dump (admin bcrypt hash + signup PII) ├── parhamandco.zip 24 MB binary blob — full backup of a SECOND app, embeds its own .env ├── error_log 124 KB of committed server error log (cPanel paths leak) ├── .env_4-May-2020 Real .env with live credentials, committed ├── .config.json Google OAuth secrets + refresh token ├── config/thirdparty.php 4 live Slack webhooks + Monday write token ├── info.php, phpinfo.php <?php phpinfo(); ?> at repo root ├── a-list-supervisorconf.config Supervisor queue worker config (cPanel paths) └── composer.json/.lock Laravel ^6.2, PHP ^7.2

Security assessment — methodology

This security review was conducted by reading the source directly (controllers, routes, middleware, config, composer.lock, .htaccess and committed artifacts) and tracing each tainted value from request input to its sink. Every finding below is backed by a code citation and was independently re-verified; over-stated raw findings were corrected or downgraded rather than dropped. Severity is assigned with CVSS 3.1 and mapped to OWASP Top 10 2021 and CWE; exploitability of host-dependent issues is gated on whether a live deployment still exists.

OWASP Top 10 2021 OWASP WSTG OWASP API Top 10 2023 OWASP MASVS for mobile CWE CVSS 3.1 NIST CSF 2.0
Anthropic cybersecurity skill playbooks applied to a-list-demo: secrets-in-git-scan broken-access-control-review sql-injection-source-to-sink ssrf-and-file-inclusion-review vulnerable-dependency-triage security-misconfiguration-sweep auth-and-session-review owasp-top-10-coverage-map cvss-severity-scoring remediation-roadmap-builder

Risk scorecard

Overall risk narrative

a-list-demo is a frozen Laravel 6 / PHP 7.2 fork (last commit 2020-11-02, EOL stack with no security patches) that ships an exceptionally large, code-confirmed attack surface. The entire ~70-route admin panel is unauthenticated at the route layer (the 'admin' middleware group is a copy of 'web' with no Authenticate), and even where controllers call $this->middleware('auth') the default 'web' guard has no role/is_admin concept, so any users-table account is a full admin — and Auth::routes() is re-enabled inside /admin, letting an anonymous attacker self-register straight to admin. From there, multiple raw-string DB::select sinks (UsersPreviews, FoodOffers, Venues, Audit, Admin controllers) yield UNION/blind SQL injection, and a DomPDF job renders attacker-supplied HTML with isRemoteEnabled=true (SSRF to cloud metadata + file:// read). Live secrets are committed and unrotated since 2020 — production MySQL password, Gmail SMTP password for info@alist.ae, the Laravel APP_KEY, a Google OAuth client secret + non-expiring refresh token, four Slack webhooks, a Monday write token — plus a DB dump with an admin bcrypt hash and a 24 MB backup zip embedding a second app's secrets and an even older RCE-vulnerable Ignition. If any host still serves this code (cPanel breadcrumbs strongly suggest it once did), it is trivially and remotely compromisable; the single most urgent operational question is whether demo/dev-alist is still online, and every committed credential must be treated as breached and rotated immediately.

Overall risk
Critical
8 P0 13 P1 6 P2 3 P3

Systemic themes

OWASP Top 10 2021 — coverage

CategoryStatusNotes
A01:2021 — Broken Access ControlvulnUnauthenticated admin panel, auth-as-authorization with no role, self-registration to admin, unauth state-changing offer endpoints, IDOR via default-salt Hashids, GET-based CSRF (ALD-01/02/03/15/16/22/26/27).
A02:2021 — Cryptographic FailuresvulnLive DB/SMTP/APP_KEY, Google OAuth secret+refresh token, Slack/Monday tokens, and a bcrypt-hash DB dump all committed (ALD-06/08/13/14).
A03:2021 — InjectionvulnFive confirmed raw-concatenation SQLi sinks across admin controllers plus one theoretical second-order (ALD-04/17/18/19/20/31).
A04:2021 — Insecure DesignpartialNo defense-in-depth: bare-auth-as-authorization, URL-literal/base64 privilege gates, GET for destructive actions — design-level rather than a single bug (ALD-02/22/25).
A05:2021 — Security MisconfigurationvulnAPP_DEBUG=true in prod, phpinfo()/error_log/secret files web-reachable via permissive .htaccess, insecure cookies, /run_queue, allow_url_include=On, no security headers (ALD-10/11/21/23/24/28/29/30).
A06:2021 — Vulnerable and Outdated ComponentsvulnEOL PHP 7.2 / Laravel 6; Ignition 1.16.3 (CVE-2021-3129), dompdf/phpspreadsheet/guzzle/flysystem CVEs; embedded 2nd app with Ignition 1.11.2 (ALD-07/09/10/12).
A07:2021 — Identification and Authentication FailuresvulnWeak primitives as auth tokens (sha1(date), base64('prive'), 'admin' literal), session-key presence check, no admin guard (ALD-25/27, and ALD-02/03).
A08:2021 — Software and Data Integrity Failurespartialopis/closure 3.6.0 deserialization gadget present and DomPDF renders untrusted HTML; no signed/verified update pipeline, but no confirmed standalone deserialization sink reached by attacker input (touched by ALD-05/12).
A09:2021 — Security Logging and Monitoring FailurespartialCommitted error_log shows ad-hoc logging; no centralized/alerting evidence and APP_DEBUG leaks rather than logs securely. Not separately scored.
A10:2021 — Server-Side Request Forgery (SSRF)vulnDomPDF job with isRemoteEnabled=true rendering raw attacker HTML (ALD-05); dormant cURL SSRF in Instagram rule (ALD-32).
API Top 10 (note)n/aNot an API tier — routes/api.php is 15 lines and the app is server-rendered Blade; the AJAX/JSON admin endpoints are covered under A01/A03 above.
Mobile Top 10 (M1-M10)n/aNo mobile client in this repo (server-side Laravel web app); MASVS / Mobile Top 10 not applicable.

Detailed findings

P0 — Critical (8) P1 — High (13) P2 — Medium (6) P3 — Low / Info (3)
ALD-01

Entire admin panel unauthenticated: 'admin' route group and 'admin' middleware group both lack auth

P0 A01:2021 CWE-862 CVSS 9.8 EPSS n/a confirmed
Description

The admin route group applies only a URL prefix and namespace; the 'admin' middleware group it runs under contains no authentication middleware. The dedicated AdminAuthValidate middleware exists but is wired only to a commented-out route. GET admin endpoints whose controllers also lack a constructor auth check are reachable with no login at all.

Evidence
routes/admin.php:21 Route::group(['prefix'=>'admin','namespace'=>'Admin'], ...) registers ~70 admin routes with NO 'middleware' key app/Providers/RouteServiceProvider.php:87-89 mapAdminRoutes applies only the 'admin' middleware GROUP app/Http/Kernel.php:41-49 the 'admin' group is a verbatim copy of 'web' (EncryptCookies, StartSession, VerifyCsrfToken, SubstituteBindings) — NO Authenticate app/Http/Kernel.php:89 adminAuthValidate alias exists but routes/admin.php:17-19 applies it only to a single commented-out route GET admin routes like usersList, exportSignups/{type}, blockORsuspend, deletePdfFile are reachable without auth
Attack scenario
An anonymous attacker issues GET /admin/exportSignups/approved (or /admin/usersList) and receives the full signups table; then GET /admin/blockORsuspend?... to disrupt accounts. No login, token, or CSRF needed for the GET endpoints.
Impact

Unauthenticated read/export of all influencer PII (signups: name, email, mobile, Instagram, nationality, DOB) via GET /admin/usersList and /admin/exportSignups, plus block/suspend, offer/venue mutation, and audit-PDF deletion. Confidentiality, integrity, and availability all impacted with zero credentials.

Remediation
Add an authentication+authorization middleware to the admin route group (Route::group(['prefix'=>'admin','namespace'=>'Admin','middleware'=>['adminAuthValidate']], ...)) AND add \Illuminate\Auth\Middleware\Authenticate to the 'admin' middleware group in Kernel.php. Back it with a real admin guard/role. Add a route-coverage test asserting every /admin/* route returns 401/redirect when unauthenticated.
Verifier note

Confirmed by reading routes/admin.php, RouteServiceProvider, and Kernel.php. Many admin controllers DO call middleware('auth') in their constructor (ALD-02), so those endpoints still require some authenticated users-table session; GET routes on controllers without that constructor (and the route group's missing gate) are the fully-open ones. Severity remains P0 because ALD-02/ALD-03 collapse 'authenticated' into 'admin'.

ALD-02

Auth used as authorization on the wrong guard with no admin role — any registered user is a full admin

P0 A01:2021 CWE-863 CVSS 9.1 EPSS n/a confirmed
Description

Every admin controller calls $this->middleware('auth') with no guard, resolving the default 'web' guard backed by the users table. The User model/users table has no role or is_admin attribute, so authentication equals authorization: any users-table account is implicitly a full administrator. No per-action privilege check exists anywhere.

Evidence
app/Http/Controllers/Admin/AdminController.php:25-28 public function __construct(){ $this->middleware('auth'); } (no guard arg => default guard) Same pattern in DashboardController, VenuesController, SettingsController, FoodOffersController, UsersPreviewsController, AuditController, AdminMailTracker config/auth.php:16-19 defaults guard 'web' -> provider 'users' -> App\User; NO 'admin' guard defined app/User.php:18-20 $fillable = ['name','email','password'] — no role/is_admin column
Attack scenario
An attacker who obtains or creates any users-table account logs in and reaches every admin controller; there is no role check distinguishing them from a legitimate admin.
Impact

Vertical privilege escalation — any single users-table account (including a self-registered one, see ALD-03) yields complete administrative control. Any credential compromise of any account is full platform compromise.

Remediation
Introduce a real authorization model: add an is_admin/role column and a dedicated admin guard, gate every admin action via a Gate/Policy or middleware('can:...'), and specify the guard explicitly ($this->middleware('auth:admin')). Never treat bare authentication as authorization.
Verifier note

Confirmed: AdminController.php:25-28, config/auth.php has only 'web' and 'chat_agent' guards, User model has no role field.

ALD-03

Auth::routes() re-enabled inside /admin prefix allows anonymous self-registration to instant admin

P0 A01:2021 CWE-269 CVSS 9.8 EPSS n/a confirmed
Description

web.php disables registration, but admin.php calls bare Auth::routes() inside the /admin prefix, re-registering POST /admin/register backed by the stock RegisterController. It creates a users-table row and redirects to /admin/dashboard. Because any users-table account is treated as admin (ALD-02), an anonymous attacker can register themselves into full admin in one request.

Evidence
routes/admin.php:13-15 Route::prefix('admin')->group(function(){ Auth::routes(); }); — no ['register'=>false], so POST /admin/register is live routes/web.php:16 Auth::routes(['register'=>false]); — registration deliberately disabled on web side, showing intent app/Http/Controllers/Auth/RegisterController.php:31 $redirectTo='/admin/dashboard' RegisterController.php:64-71 create() inserts into App\User with name/email/password only — no role, no approval
Attack scenario
Attacker POSTs name/email/password to /admin/register, is redirected to /admin/dashboard, and now holds a fully-privileged account.
Impact

Anonymous attacker to administrator in a single POST. Defeats any future login gate that merely checks for an authenticated users-table account.

Remediation
Remove Auth::routes() from inside /admin (use ['register'=>false] consistently, register only login/logout for admin). Disable public registration for the admin guard; provision admin accounts out of band. Confirm POST /admin/register returns 404/403 after the fix.
Verifier note

Confirmed: routes/admin.php:14 bare Auth::routes() vs routes/web.php:16 register=>false; RegisterController redirects to /admin/dashboard.

ALD-04

SQL injection in admin Users-Previews filter (WHERE/IN/LIKE/LIMIT all unparameterized)

P0 A03:2021 CWE-89 CVSS 8.8 EPSS n/a confirmed
Description

filterPreviews builds a large raw SQL string for DB::select by concatenating request-controlled venues, instagram_followers, search value, and pagination start/length with zero parameterization. str_replace('Pending',...) does not neutralize SQL metacharacters. The IN lists, WHERE/LIKE clauses, and LIMIT are all injectable.

Evidence
app/Http/Controllers/Admin/UsersPreviewsController.php:279 DB::select("...WHERE is_approved=1 ".$whereCondition." ORDER BY name ASC LIMIT $start,$limit") UsersPreviewsController.php:282 identical concatenation in $countQuery UsersPreviewsController.php:240 ' AND c.restaurant_name in ('.$venue_var.')' UsersPreviewsController.php:275 " AND ( name LIKE '%".$search."%' OR ...)" ; $search = $request->input('search.value') (line 208) $start/$limit from request->input('start'/'length') concatenated into LIMIT with no int cast routes/admin.php:91 filterPreviews; controller middleware('auth') at line 23-25
Attack scenario
Attacker sends venues=0) UNION SELECT email,password,... FROM users-- or start=0 UNION SELECT ... in the DataTables request and reads arbitrary tables.
Impact

Full database read (and write/file ops depending on grants) including signups PII and credentials. Error-based extraction is trivial with APP_DEBUG=true. A low-privilege (or self-registered) authenticated session suffices.

Remediation
Use the existing safe Query Builder branch (lines 291+) for all cases. Where raw SQL is unavoidable, bind every value with ? placeholders, build IN lists with placeholder arrays, and integer-cast pagination ((int)$start/(int)$length).
Verifier note

Confirmed by reading UsersPreviewsController.php:185-282. Tainted values traced from request to query string.

ALD-05

SSRF + stored-XSS via DomPDF audit-PDF job rendering raw attacker HTML with isRemoteEnabled=true

P0 A10:2021 CWE-918 CVSS 8.5 EPSS n/a confirmed
Description

auditPdfDownload passes request pdf_content verbatim to CreatePdfJob, which renders it through DomPDF with isRemoteEnabled=true; the Blade template prints it with {!! !!}. Attacker HTML (img/link/@import to http://169.254.169.254/, http://127.0.0.1:port, or file:///etc/passwd) forces server-side fetches, and the resulting PDF is saved under the public web root for retrieval.

Evidence
app/Http/Controllers/Admin/AuditController.php:661 $pdf_content=$request->pdf_content; line 690 dispatch(new CreatePdfJob($data)) (only venueTitle/date_range are sanitizeFilename'd, not content) app/Jobs/CreatePdfJob.php:38 PDF::setOptions(['isHtml5ParserEnabled'=>true,'isRemoteEnabled'=>true,'dpi'=>130])->loadView('admin.audit_pdf_download',$this->details) resources/views/admin/audit_pdf_download.blade.php:101 {!! $content !!} renders attacker HTML unescaped CreatePdfJob.php:42-44 saves PDF to public_path('audit_pdf/'); listFiles links url('public/audit_pdf/...') php.ini:6 allow_url_fopen=On (enables file:// / remote schemes)
Attack scenario
Attacker (after self-registering, ALD-03) POSTs pdf_content=<img src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"> to /admin/auditPdfDownload, then downloads the generated PDF from public/audit_pdf/ to read the fetched IAM credentials.
Impact

Server-side fetch of arbitrary internal/cloud/file URLs (instance-metadata/IAM credential theft, internal service access, local file read) with exfiltration via the web-readable PDF. Reachable by any authenticated session — which, per ALD-01/02/03, an anonymous attacker can obtain.

Remediation
Set isRemoteEnabled=false in CreatePdfJob (an audit table needs no remote assets). Never render user-supplied HTML with {!! !!}; build the PDF from validated structured fields. If remote assets are truly needed, use a strict allowlist blocking RFC1918/link-local/loopback and non-http(s) schemes, and set allow_url_fopen=Off. Move generated PDFs out of the public root.
Verifier note

Confirmed the inline isRemoteEnabled in AuditController is COMMENTED OUT (lines 675-688), but the ACTIVE path dispatches CreatePdfJob whose handle() genuinely sets isRemoteEnabled=true. PR set to L (not H) because admin access is not actually a barrier given ALD-01/03; Scope:Changed for cross-zone fetch.

ALD-06

Live MySQL DB password, Gmail SMTP password, and APP_KEY committed in .env_4-May-2020 (+ hardcoded DB fallback)

P0 A02:2021 CWE-798 CVSS 9.8 EPSS n/a confirmed
Description

The production env file (APP_ENV=production) is committed as a dated copy bypassing .gitignore, exposing the live MySQL credentials, the Gmail SMTP password for info@alist.ae, and the Laravel APP_KEY. The same DB password is a hardcoded fallback in config/database.php, leaking even without the .env file.

Evidence
.env_4-May-2020:12-14 DB_DATABASE=parhaman_development_alist / DB_USERNAME=parhaman_alist / DB_PASSWORD=!0325Parham! .env_4-May-2020:29-30 MAIL_USERNAME=info@alist.ae / MAIL_PASSWORD=Parham00332255! (smtp.gmail.com:587) .env_4-May-2020:3 APP_KEY=base64:8uOPYzCaTShDNX0d/tCTLg+A4UbO4jtAg+Tl/dzwN74= (AES-256-CBC) .gitignore ignores only '.env' and '.env.backup'; git check-ignore .env_4-May-2020 returns nothing (tracked) config/database.php:53 'password'=>env('DB_PASSWORD','!0325Parham!') hardcoded fallback
Attack scenario
Anyone with a repo clone reads DB_PASSWORD and connects to MySQL, or authenticates SMTP as info@alist.ae, or uses APP_KEY to forge session/cookie payloads.
Impact

Full DB compromise if MySQL is reachable. SMTP password enables sending mail as info@alist.ae (phishing/BEC) and possibly broader Google account access. The APP_KEY enables forging signed cookies and decrypting/forging encrypted sessions, defeating authentication.

Remediation
Rotate immediately: the MySQL password, the Gmail/Google account password (revoke app passwords, enable 2FA), and regenerate APP_KEY. Remove the config/database.php hardcoded fallback (use bare env()). Purge .env_4-May-2020 from history (filter-repo/BFG). Tighten .gitignore to .env* and add Gitleaks/TruffleHog pre-commit + CI gates.
Verifier note

Confirmed all values present in .env_4-May-2020 and the fallback in config/database.php:53. File tracked and present since initial commit.

ALD-07

Ignition RCE (CVE-2021-3129) — facade/ignition 1.16.3 with committed APP_DEBUG=true in production

P0 A06:2021 CWE-502 CVSS 9.8 CVE-2021-3129 EPSS ~0.94 · CISA KEV confirmed
Description

facade/ignition <2.5.2 has an unauthenticated RCE: the execute-solution endpoint plus php://filter log-file manipulation allows writing attacker-controlled content and including it. Exploitation requires only debug mode, which the committed production env enables.

Evidence
composer.lock facade/ignition v1.16.3 (fix is >=2.5.2) composer.json:29 require-dev facade/ignition ^1.4 (Laravel 6 default debug handler) .env_4-May-2020:2,4 APP_ENV=production + APP_DEBUG=true; config/app.php:42 'debug'=>env('APP_DEBUG',false) a-list-supervisorconf.config references live deploy path /home/parhaman/public_html/development-alist/
Attack scenario
If a host serves this code with APP_DEBUG=true, an attacker hits the Ignition execute-solution endpoint with a php://filter chain to write and include a PHP payload, gaining RCE without credentials.
Impact

Unauthenticated remote code execution as the web user on any internet-facing instance using this env. Combined with the committed DB/SMTP secrets, full data exfiltration and mail abuse follow. Mature weaponized exploits exist (Laravel-RCE / phpggc).

Remediation
Set APP_DEBUG=false in every deployed environment immediately (closes the RCE alone). Upgrade ignition to >=2.5.2 (needs Laravel 8+) or decommission this EOL stack. Confirm no live host serves this code; rotate the exposed APP_KEY.
Verifier note

Confirmed ignition 1.16.3 in composer.lock and APP_DEBUG=true in committed env. Exploitability conditional on a live host (status unknown) — confidence 'confirmed' for the vulnerable configuration, realistic exposure gated on deployment.

ALD-08

Google OAuth client_secret, API key, and long-lived refresh token committed in .config.json

P0 A02:2021 CWE-522 CVSS 9.1 EPSS n/a confirmed
Description

Repo-root .config.json holds a complete Google OAuth2 credential set: client_id, client_secret, an AIza API key, and a long-lived offline refresh token. The refresh token plus client_id/secret mint fresh access tokens indefinitely with no user interaction. The unauthenticated /contacts route additionally echoes a refresh token to whoever completes the OAuth dance.

Evidence
.config.json:2 clientID 633504907894-...apps.googleusercontent.com .config.json:3 clientSecret TZd1qi89-x5QoXK3_b5IDdoQ .config.json:5 developerKey AIzaSyBy1f8ZFZFYDOPvNhx8Q68va2dE4YA588o (AIza key) .config.json:6 refreshToken 1//042NoXccnvrBTCg... (1// = non-expiring offline refresh token) Consumed via RapidWeb\GoogleOAuth2Handler in AdminController.php:8; git-tracked (not in .gitignore) routes/web.php:31-55 GET /contacts echoes the refresh_token to the browser; /authorise-application prints the OAuth URL — both unauthenticated
Attack scenario
Attacker exchanges the committed refresh token with client_id/secret at Google's token endpoint to mint live access tokens and reads/modifies the linked Google Contacts directory.
Impact

Programmatic read/modify of the A-List Google Contacts/People directory (influencer/vendor PII), OAuth client impersonation via the secret, and API-key abuse against enabled Google APIs (quota/billing).

Remediation
Revoke the refresh token now and reset the OAuth client secret + regenerate/scope-restrict the API key in Google Cloud Console. Move values to env/secrets manager. Purge .config.json from history; add *.config.json to .gitignore. Remove or auth-gate the /contacts and /authorise-application routes and replace echo/var_dump with logging.
Verifier note

Confirmed .config.json contents and that the file is tracked. /contacts echo confirmed in routes/web.php:53.

ALD-09

EOL runtime and framework — PHP 7.2 and Laravel 6 with no security patches

P1 A06:2021 CWE-1104 CVSS 8.1 EPSS n/a confirmed
Description

The app targets PHP 7.2 (EOL) and Laravel 6 (support ended), so the runtime, framework, and bundled Symfony 4.4/5.1 components receive no security fixes. Framework constraints cap all dependencies at long-unmaintained versions — a structural supply-chain risk.

Evidence
composer.json:11 "php":"^7.2"; :19 "laravel/framework":"^6.2" composer.lock resolves laravel/framework v6.20.2 php.ini / .user.ini / .htaccess reference ea-php72 PHP 7.2 EOL 2019-11-30; Laravel 6 LTS security support ended 2022
Attack scenario
An attacker fingerprints the stack (trivially, via committed composer.lock and phpinfo) and applies any post-2022 Laravel 6 / PHP 7.2 / Symfony CVE that will never be patched here.
Impact

Cumulative, ever-growing exposure to disclosed CVEs in runtime/framework/components with no upstream remediation path. Any newly weaponized PHP 7.2 / Laravel 6 / Symfony 4.4 bug is exploitable indefinitely on a deployed instance.

Remediation
Decommission/archive this frozen fork (last commit 2020-11-02). If functionality is still needed, migrate to the maintained alist-portal line (Laravel 11 / PHP 8.2). Do not stand up new hosts from this repo.
Verifier note

Confirmed versions in composer.json/composer.lock and ea-php72 handler references.

ALD-10

Embedded full secondary app backup (parhamandco.zip) bundling vulnerable vendor tree and live secrets

P1 A05:2021 CWE-540 CVSS 8.6 CVE-2021-3129 EPSS ~0.94 confirmed
Description

A 24.5 MB archive at the repo root is a full backup of a separate client Laravel app with its own .env (APP_KEY, Mailtrap MAIL_PASSWORD), composer.lock and bundled vendor tree, including an even older CVE-2021-3129-vulnerable Ignition (1.11.2). Per the permissive .htaccess (ALD-11) it is directly downloadable if any host serves the repo.

Evidence
parhamandco.zip (24.5 MB), git-tracked unzip -l shows parhamandco/.env, /composer.lock, /composer.json, full /vendor tree Embedded .env: APP_KEY=base64:ZyxVIifwJnqUqKAwlCUxhuQo421X7JaWLwf+npdjd9E=, APP_DEBUG=true, MAIL_PASSWORD=74e0152b129a5e (Mailtrap) Embedded composer.lock pins laravel/framework v6.4.1, facade/ignition 1.11.2 — vulnerable to CVE-2021-3129
Attack scenario
Attacker downloads the zip (from clone or web root), extracts the embedded .env to forge sessions for the parhamandco app and reuse its Mailtrap token.
Impact

Leakage of a second project's APP_KEY (session/cookie forgery for that app) and Mailtrap mail credential, plus a ready-to-exploit second vulnerable codebase. Also bloats clone surface.

Remediation
Remove parhamandco.zip from the repo and purge from history (filter-repo/BFG). Rotate the embedded app's APP_KEY and Mailtrap credential. Confirm that app is decommissioned or separately remediated.
Verifier note

Confirmed via unzip: embedded .env present with APP_KEY/APP_DEBUG/Mailtrap MAIL_PASSWORD. Raw finding overstated DB_PASSWORD and PUSHER_APP_SECRET as populated — both are EMPTY in the embedded .env; corrected here. CVSS lowered to 8.6 / I:L accordingly.

ALD-11

Committed secrets, dumps, and dependency manifest web-reachable via permissive .htaccess (!-f bypass)

P1 A05:2021 CWE-538 CVSS 7.5 EPSS n/a confirmed
Description

The Laravel-without-docroot .htaccess rewrites only non-existent paths into /public/. Files that physically exist at the app root (.env_4-May-2020, .config.json, composer.lock, parhamandco.zip/.sql, error_log, info.php) are served verbatim because !-f skips them and there are no deny rules. composer.lock alone hands attackers the full SBOM.

Evidence
.htaccess:13-20 RewriteCond %{REQUEST_URI} !^/public/ + !-d + !-f then RewriteRule ^(.*)$ /public/$1 — existing root files bypass the rewrite and are served directly No <Files>/<FilesMatch> deny rules for .env*, .config.json, composer.lock, *.sql, *.zip, *.php diagnostic, error_log composer.lock (316 KB) enumerates exact versions of all packages — precise CVE-targeting map Root info.php / phpinfo.php and public/info.php all contain phpinfo()
Attack scenario
Attacker fetches https://host/composer.lock to map exact dependency versions, then https://host/.config.json and https://host/.env_4-May-2020 to harvest secrets directly.
Impact

Unauthenticated disclosure of the software bill of materials (removing the recon barrier for ALD-07/09/12) plus direct download of committed secret/backup/log files. Lowers attacker cost for every dependency CVE.

Remediation
Serve only /public/ (move app out of docroot) or add explicit <FilesMatch> deny rules for .env*/.json/.lock/.zip/.sql/.log and phpinfo scripts. Remove the committed secret/diagnostic files and rotate their secrets.
Verifier note

Confirmed .htaccess rewrite logic and that all named files are tracked at root. Exposure conditional on a live host.

ALD-12

Outdated dependencies with public CVEs reachable from code (dompdf, phpspreadsheet, guzzle, flysystem)

P1 A06:2021 CWE-1395 CVSS 8.1 CVE-2022-28368 EPSS ~0.10 confirmed
Description

Several directly/transitively used packages are pinned below patched versions and are reachable from controller/job code. dompdf 0.8.6 (CVE-2022-28368 RCE via CSS @font-face when rendering attacker-influenced HTML — directly relevant given ALD-05, and CVE-2021-3838 SSRF) is invoked from CreatePdfJob; phpspreadsheet 1.15.0 is exposed to XXE/formula-injection on user-supplied imports; guzzle/psr7 carry header-parsing/credential-leak-on-redirect CVEs; flysystem 1.1.3 has a path-traversal flaw. All are capped by the EOL framework.

Evidence
composer.lock dompdf/dompdf v0.8.6 — reachable via app/Jobs/CreatePdfJob.php (loadView) and AuditController (PDF) composer.lock phpoffice/phpspreadsheet 1.15.0 via maatwebsite/excel — wired into UsersExport, VenueOffer/Offer/Venues controllers composer.lock guzzlehttp/guzzle 6.5.5 (fixed 6.5.8), guzzlehttp/psr7 1.7.0 (fixed 1.8.4) composer.lock league/flysystem 1.1.3 (CVE-2021-32708, fixed 1.1.4); opis/closure 3.6.0 (historical deser gadget)
Attack scenario
Attacker supplies crafted CSS in pdf_content (ALD-05) to trigger dompdf CVE-2022-28368, or uploads a malicious spreadsheet to an Excel-import endpoint to trigger an XXE in phpspreadsheet.
Impact

Where attacker data reaches PDF generation, RCE/SSRF chains are plausible; spreadsheet import can yield XXE/file disclosure; HTTP-client CVEs can leak credentials on redirect. None can be cleanly patched in place.

Remediation
Independently bump where possible (dompdf>=2.0.1, phpspreadsheet>=1.18, guzzle 6.5.8, psr7 1.8.4, flysystem 1.1.4) — but the clean fix is migrating this functionality to alist-portal and retiring the fork. Treat AuditController/CreatePdfJob HTML and Excel imports as untrusted.
Verifier note

Confirmed all versions in composer.lock and reachability of dompdf/phpspreadsheet from controllers/jobs. Related CVEs: CVE-2021-3838 (dompdf SSRF), CVE-2022-31090/31091 (guzzle), CVE-2022-24775 (psr7), CVE-2021-32708 (flysystem).

ALD-13

Live Slack incoming-webhook URLs and Monday.com write-scoped API token hardcoded in config/thirdparty.php

P1 A02:2021 CWE-798 CVSS 7.5 EPSS n/a confirmed
Description

config/thirdparty.php (tracked) hardcodes four production Slack incoming-webhook URLs and a Monday.com API token with me:write scope; both are referenced by application code, confirming they are in-use. config/thirdparty.php_bkp commits an additional set.

Evidence
config/thirdparty.php:7-11 four live https://hooks.slack.com/services/THXCPKFC1/... webhook URLs (signup, approve_decline, vendor, offers) config/thirdparty.php:16 monday token eyJhbGciOiJIUzI1NiJ9... JWT payload {tid:36588732,uid:12463638,per:me:write} config/thirdparty.php_bkp also tracked with additional Slack webhooks Consumed: app/FoodOffers.php, app/Signup.php, app/Venues.php, AdminController.php config('thirdparty.monday.token')
Attack scenario
Attacker POSTs to the committed Slack webhook to inject phishing into team channels, and uses the Monday JWT against api.monday.com/v2 to exfiltrate/modify applicant PII boards.
Impact

Slack webhook leak permits posting spoofed/phishing messages into signup/approval/vendor channels. The Monday token permits read/modify of boards storing applicant PII via the Monday GraphQL API. Both usable directly from the committed repo.

Remediation
Regenerate all four Slack webhooks and the Monday token (and the _bkp ones), move to env() references, delete config/thirdparty.php_bkp, purge values from history, and add CI scanning for hooks.slack.com URLs and JWTs.
Verifier note

Confirmed by reading config/thirdparty.php; the offers/signup/approve webhooks share one URL and one ('offers' original) is commented out — corrected to note the active set. CVSS lowered slightly (S:U, no integrity-of-other-component beyond messaging) vs raw 8.6.

ALD-14

DB dump with admin bcrypt hash and signup PII (parhamandco.sql) committed and web-reachable

P1 A02:2021 CWE-538 CVSS 7.5 EPSS n/a confirmed
Description

A phpMyAdmin SQL dump committed at the repo root contains the bcrypt hash for admin@parhamandco.com and signup PII (names, emails, phones, Instagram handles, nationality).

Evidence
parhamandco.sql:134 INSERT users ... '$2y$10$5QrlbpMuyav0MpwRUPK6ZO6XUb9Cz.S2gKNPgIiGqUMdd5UBN7Vi.' (admin@parhamandco.com bcrypt hash) parhamandco.sql signups section with names, emails, mobile, instagram, nationality (PII) git-tracked; per .htaccess (!-f) served directly if host is live
Attack scenario
Attacker downloads parhamandco.sql, extracts the admin bcrypt hash, cracks it offline, and logs into the admin panel.
Impact

The admin bcrypt hash can be cracked offline; a weak password yields admin login. The PII is a privacy/GDPR exposure and a phishing target list. Harvestable by anyone who clones the repo or downloads from a live host.

Remediation
Remove parhamandco.sql from the repo and history (filter-repo/BFG). Rotate the admin credential. Handle the leaked PII per data-breach policy. Never commit DB dumps; add *.sql to .gitignore.
Verifier note

Confirmed bcrypt hash and PII rows present at parhamandco.sql:134 and surrounding.

ALD-15

Unauthenticated state-changing offer/user endpoints accept object IDs from request (no auth/ownership)

P1 A01:2021 CWE-862 CVSS 8.1 EPSS n/a confirmed
Description

VenueOfferController runs under the 'web' group (session/CSRF only) with no auth middleware. Its GET routes are state-changing yet take object IDs straight from request input. deleteOfferUsers deletes invitations, resets user-to-offer bindings, and emails cancellations for attacker-chosen IDs; proceedOffer/unBlockOffer similarly mutate state. No ownership or authentication check exists.

Evidence
app/Http/Controllers/VenueOfferController.php:36 class declaration with NO __construct/middleware (grep confirmed) routes/web.php:135-138 GET unBlockOffer, deleteOfferUsers, proceedOffer — all unauthenticated VenueOfferController.php:2245-2322 deleteOfferUsers(): reads primUser/offerid from request, DELETEs food_offers_invitations rows, UPDATEs food_offers_users SET block=null,user_id=null, dispatches cancellation emails — no caller authorization VenueOfferController.php:2324+ proceedOffer() reads primUser/offerid and mutates offer state
Attack scenario
Attacker iterates small integer offerid/primUser values against GET /deleteOfferUsers to corrupt offer assignments and trigger cancellation emails to arbitrary users, all without authentication.
Impact

Unauthenticated integrity/availability impact on the offers subsystem: arbitrary cancellation/reassignment of offer claims, clearing user bindings (releasing/redistributing offer codes), and spamming arbitrary recipients with cancellation emails. Business-logic abuse + DoS against legitimate claims.

Remediation
Move these actions behind authentication and per-object authorization (verify the caller owns the offer/invitation). Convert destructive operations from GET to POST/DELETE with CSRF and validate object ownership before mutating.
Verifier note

Confirmed VenueOfferController has no constructor/middleware and the routes are in the unauthenticated web group; deleteOfferUsers/proceedOffer bodies confirmed.

ALD-16

IDOR on offer/review endpoints protected only by reversible default-salt Hashids (no ownership checks)

P1 A01:2021 CWE-639 CVSS 8.1 EPSS n/a confirmed
Description

Public, unauthenticated routes resolve sensitive objects from URL/body identifiers obfuscated with Hashids instantiated using the default empty salt — publicly reversible/forgeable, effectively sequential integers in disguise. No endpoint verifies the requester owns the referenced user_id/offer_id. reviewSave persists a review on behalf of any user; /offerView uses base64('prive') as a guessable access token.

Evidence
app/Http/Controllers/UserReviewController.php:34,156,221 new Hashids() (no salt); index/getUserReview/store resolve user_id/offer_id from URL/body with no owner check app/Http/Controllers/OfferController.php:44 new Hashids(); decodeHex(segment(2)) controls which FoodOffers row loads app/helpers.php:41 new Hashids() (no salt) + ~30 other call sites routes/web.php:81-84 userReview/{id}, reviewSave, review/{id} all unauthenticated OfferController.php:48-67 segment(3)=='admin' or base64_decode=='prive' used as access flags
Attack scenario
Attacker generates Hashids for sequential user/offer IDs with the default-salt library and walks every review record, or POSTs reviewSave with arbitrary userid/offerid to forge reviews.
Impact

Horizontal IDOR: enumerate/disclose any influencer's review submissions and screenshots, forge reviews on behalf of arbitrary users, and view non-published offers via the static base64('prive') token. The default salt lets an attacker mint valid tokens for arbitrary IDs offline.

Remediation
Do not treat Hashids as access control. Require authentication and verify object ownership before read/write. Configure Hashids with a secret salt as defense-in-depth, and replace the base64('prive') flag with a Laravel signed, expiring URL.
Verifier note

Confirmed new Hashids() with no salt at the cited sites and unauthenticated routes; base64('prive') and 'admin' gates confirmed in OfferController.php:48-67.

ALD-17

SQL injection in admin venue autocomplete (venueDropListAuto keyword in LIKE)

P1 A03:2021 CWE-89 CVSS 7.6 EPSS n/a confirmed
Description

venueDropListAuto concatenates the unsanitized keyword into a single-quoted LIKE clause of a raw DB::select and returns the result as JSON, giving an in-band UNION/error-based extraction channel.

Evidence
app/Http/Controllers/Admin/FoodOffersController.php:1298 $seacrh=$request->keyword FoodOffersController.php:1299 DB::select("SELECT id,venue_title,category_id FROM venues WHERE venue_title LIKE '%".$seacrh."%' ORDER BY id ASC") Returns response()->json($query); route routes/admin.php:59; controller middleware('auth') 46-48
Attack scenario
Attacker sends keyword=%' UNION SELECT id,CONCAT(email,0x3a,password),3 FROM users-- and reads credentials from the JSON.
Impact

In-band UNION extraction of any table (PII in signups, credentials, offers) returned directly in the JSON response. Requires only a low-privilege authenticated session (obtainable via ALD-03).

Remediation
Bind the LIKE term: DB::select('... WHERE venue_title LIKE ? ...', ['%'.$seacrh.'%']) or use the Query Builder; escape LIKE wildcards.
Verifier note

Confirmed at FoodOffersController.php:1298-1300. CVSS adjusted to 7.6 (A:N — read/write but no direct availability impact in this query shape).

ALD-18

SQL injection in admin venueOffers via numeric vid parameter (no quote required)

P1 A03:2021 CWE-89 CVSS 7.6 EPSS n/a confirmed
Description

venueOffers concatenates request vid into a raw DB::select in an unquoted numeric context, so no quote-breakout is needed. Injected rows are iterated and reflected into HTML output, enabling UNION extraction and second-order XSS.

Evidence
app/Http/Controllers/Admin/VenuesController.php:403 $vid=$request->input('vid') VenuesController.php:404 DB::select("SELECT id,offer_date,offer_usage FROM food_offers WHERE restaurant_name = ".$vid." AND offer_status='Draft' ORDER BY offer_date asc") Route routes/admin.php:101; controller middleware('auth') 39-41; results reflected to HTML output
Attack scenario
Attacker sends vid=0 UNION SELECT id,CONCAT(email,0x3a,password),offer_usage FROM signups-- and reads PII.
Impact

Arbitrary DB read via UNION/boolean/time-based injection; disclosure of all DB contents including PII and credentials. Low-privilege authenticated session required.

Remediation
Bind and integer-cast: DB::select('... WHERE restaurant_name = ? AND offer_status = ? ...', [(int)$vid,'Draft']) or use Eloquent.
Verifier note

Confirmed at VenuesController.php:403-404.

ALD-19

SQL injection in admin listCategoryVenues via numeric cid parameter

P1 A03:2021 CWE-89 CVSS 7.6 EPSS n/a confirmed
Description

listCategoryVenues concatenates request cid into a raw DB::select in an unquoted numeric context with no validation; rows are reflected as JSON, a clean in-band channel.

Evidence
app/Http/Controllers/Admin/AuditController.php:78 $category=$request->cid AuditController.php:85 DB::select("SELECT id,venue_title FROM venues WHERE category_id =".$category." ORDER BY venue_title ASC") Returned as JSON line 88; route routes/admin.php:121; controller middleware('auth') 62-64
Attack scenario
Attacker sends cid=0 UNION SELECT id,CONCAT(name,0x3a,email,0x3a,mobile_number) FROM signups-- and reads PII from the JSON.
Impact

In-band UNION extraction of any table returned in JSON, including signups PII and credentials. Low-privilege authenticated session required.

Remediation
Bind, integer-cast: DB::select('... WHERE category_id = ? ...', [(int)$category]) or Query Builder.
Verifier note

Confirmed at AuditController.php:78-88.

ALD-20

SQL injection in admin userActionHistory via numeric id parameter

P1 A03:2021 CWE-89 CVSS 7.6 EPSS n/a confirmed
Description

userActionHistory concatenates request id into a raw DB::select in an unquoted numeric context. SELECT * plus downstream iteration makes UNION column-matching straightforward.

Evidence
app/Http/Controllers/Admin/AdminController.php:975 $id=$request->id AdminController.php:976 DB::select("SELECT * FROM users_actions where user_id=".$id) Adjacent commented sink at :942 confirms repeated pattern; route routes/admin.php:155; controller middleware('auth') 25-28
Attack scenario
Attacker sends id=0 UNION SELECT 1,email,password,... FROM users-- to extract credentials.
Impact

Arbitrary DB read via UNION/boolean/time-based injection, disclosing PII and credentials. Low-privilege authenticated session required.

Remediation
Bind and integer-cast: DB::select('SELECT * FROM users_actions where user_id = ?', [(int)$id]) or Eloquent.
Verifier note

Confirmed at AdminController.php:975-976.

ALD-21

phpinfo() endpoints, APP_DEBUG=true, and committed error_log expose server config and paths

P1 A05:2021 CWE-200 CVSS 7.5 EPSS n/a confirmed
Description

Three phpinfo() scripts and two committed error_log files exist; the .htaccess rewrite serves existing files verbatim. APP_DEBUG=true in a production env yields verbose Laravel error pages.

Evidence
info.php / phpinfo.php / public/info.php all contain phpinfo() (verified by cat) git ls-files lists root error_log and public/error_log as tracked .env_4-May-2020:2,4 APP_ENV=production + APP_DEBUG=true .htaccess (!-f) serves existing root files directly
Attack scenario
Attacker requests /info.php to dump PHP config/env and /error_log to read absolute paths and the cPanel account, then chains to the Ignition RCE given APP_DEBUG=true.
Impact

phpinfo() discloses full PHP configuration, paths, and environment (which on cPanel can include secrets) — a complete recon map. Committed logs add filesystem layout and the cPanel account name; APP_DEBUG=true adds stack traces and is the precondition for the Ignition RCE (ALD-07).

Remediation
Delete info.php/phpinfo.php/public/info.php and both error_log files from the repo and any live host. Set APP_DEBUG=false outside local. Configure the web server to deny dotfiles and *.log.
Verifier note

Confirmed all three phpinfo files and APP_DEBUG=true. Overlaps ALD-07/ALD-11; kept distinct as the info-exposure cluster.

ALD-22

CSRF: sensitive state-changing admin actions exposed over GET with SameSite unset

P1 A01:2021 CWE-352 CVSS 7.1 EPSS n/a confirmed
Description

Numerous sensitive admin operations are GET routes; Laravel's CSRF middleware never inspects GET, and the session cookie has no SameSite restriction, so it is sent on cross-site GETs. An attacker page can force a logged-in admin's browser to perform these actions via <img> or auto-followed links.

Evidence
routes/admin.php:24-25 GET approve_signup/decline_signup; :33 user_verification; :135 deleteOffer; :108 deleteOfferCode; :85 resetUser; :154 blockORsuspend; :177 venue-approve/{id} Laravel VerifyCsrfToken validates only POST/PUT/PATCH/DELETE; GET is never CSRF-checked config/session.php:197 same_site=null; :169 secure=>env('SESSION_SECURE_COOKIE',false)
Attack scenario
Admin opens a malicious page containing <img src="https://host/admin/blockORsuspend?...">; the browser sends the session cookie cross-site and the action executes.
Impact

An authenticated admin who visits an attacker page can be silently made to approve/decline accounts, block/suspend users, delete offers/codes, reset users, and approve venues — integrity/availability impact without consent.

Remediation
Convert state-changing endpoints to POST/PUT/DELETE (CSRF auto-verified). Set session same_site to 'lax'/'strict' and SESSION_SECURE_COOKIE=true. Validate Origin/Referer on sensitive actions.
Verifier note

Confirmed the GET routes in admin.php and same_site=null/secure=false in config/session.php. Realistic only against an authenticated admin (UI:R).

ALD-23

Host header injection / password-reset poisoning (no TrustHosts, APP_URL=localhost)

P1 A05:2021 CWE-644 CVSS 6.5 EPSS n/a likely
Description

Stock Laravel password reset with no trusted-host pin and APP_URL=localhost: the framework derives reset-email URLs from the incoming Host header. With no host allowlist it accepts an arbitrary Host and emits reset links pointing to it.

Evidence
routes/web.php:16 Auth::routes(['register'=>false]); stock password-reset (SendsPasswordResetEmails) .env_4-May-2020:5 APP_URL=http://localhost (not the real domain) No TrustHosts middleware / trusted_hosts config exists (Laravel 6 ships none by default) app/Http/Middleware/TrustProxies.php:22 $headers=HEADER_X_FORWARDED_ALL but $proxies is null
Attack scenario
Attacker submits a password-reset for the admin with Host: evil.com; the admin receives a reset link to evil.com and, if clicked, the token is captured by the attacker.
Impact

An attacker who triggers a reset for a victim with a forged Host can produce a reset email whose link points to attacker-controlled host; if the victim clicks, the reset token leaks, enabling account (including admin) takeover. Also enables cache poisoning of host-derived links.

Remediation
Add a TrustHosts-style allowlist validating Host against the production domain. Set APP_URL to the canonical https domain and generate reset links from config, not the request Host. Keep $proxies tightly scoped.
Verifier note

Likely (not confirmed): TrustProxies $headers is the broad ALL set BUT $proxies is null, so X-Forwarded-Host is NOT trusted by default — exploitation relies on the Host header path, which is real for stock reset with no TrustHosts. AC:H/UI:R reflects the click + Host-control requirement.

ALD-24

APP_DEBUG=true in production environment (verbose errors + Ignition RCE precondition)

P1 A05:2021 CWE-489 CVSS 7.2 EPSS see ALD-07 confirmed
Description

The committed production env sets APP_DEBUG=true, so Laravel renders Ignition/Whoops error pages with full stack traces, env vars (including the live DB/SMTP passwords in this same env), and paths on any unhandled exception — and satisfies the CVE-2021-3129 RCE precondition.

Evidence
.env_4-May-2020:2 APP_ENV=production; :4 APP_DEBUG=true config/app.php:42 'debug'=>env('APP_DEBUG',false); 'env'=>env('APP_ENV','production') composer.json require-dev facade/ignition ^1.4 File git-tracked (documented deployed config)
Attack scenario
Attacker triggers any error (e.g. malformed input) and reads a full stack trace including environment secrets; chains to ALD-07 for RCE.
Impact

Unauthenticated disclosure of secrets, source paths, and stack traces on any error; with the EOL Ignition version, unauthenticated RCE (CVE-2021-3129).

Remediation
Set APP_DEBUG=false in all non-local environments. Remove facade/ignition from production or patch; ideally decommission the EOL stack. Purge committed env files and rotate secrets.
Verifier note

Confirmed. Distinct from ALD-07 (the dependency vuln) and ALD-21 (the info-exposure cluster); this is the misconfiguration itself. CVSS 7.2 (info-disclosure with debug, RCE tracked separately in ALD-07).

ALD-25

Weak/predictable primitives used as authorization: sha1(date) token, 'admin'/base64('prive') gates, default-salt Hashids

P2 A07:2021 CWE-639 CVSS 6.5 EPSS n/a confirmed
Description

The app uses non-cryptographic primitives as authorization tokens: daily offer access keyed on sha1(today's date) with no secret (publicly computable), the literal 'admin' as an admin flag in a URL segment, base64('prive') as a privilege gate, and default-salt Hashids (publicly reversible). Upload filenames use md5(time()).

Evidence
app/Http/Controllers/VenueOfferController.php:113-115 $todaydate=sha1($todaydate); if($date_enc==$todaydate){error=0} — no secret OfferController.php:48-58 segment(3)=='admin' branch; base64_decode(segment)=='prive' gate VenueOfferController.php:83-89 base64_decode(segment(5))=='prive' as privilege gate Hashids with no salt: app/helpers.php:41 + 30+ controller sites VendorSignupController.php:187 $fileName=md5(time()).'.'.$ext predictable upload name
Attack scenario
Attacker computes sha1('Y-m-d') for today and supplies it as the offer access token, and base64('prive')=cHJpdmU= to reach prive-tier offers — all unauthenticated.
Impact

Unauthenticated authorization bypass: forge sha1(date) to view restricted offer pages, set segment=admin to reach the admin-scoped query branch, supply base64('prive') for prive-tier offers. Default-salt Hashids enables ID enumeration/IDOR; predictable upload names allow guessing/overwriting.

Remediation
Replace sha1(date) gates with server-side authorization tied to authenticated identity, or HMAC with a secret key + expiry. Never use URL literals or base64 markers for authorization. Configure Hashids with a per-app secret salt. Generate upload filenames with random_bytes()/Str::random().
Verifier note

Confirmed sha1(date) at VenueOfferController.php:113-115, base64('prive')/'admin' gates in OfferController/VenueOfferController, no-salt Hashids, and md5(time()) filename. OWASP recategorized to A07 (auth failures) as the core issue is weak authn/authz tokens, not pure crypto.

ALD-26

Unauthenticated path traversal / arbitrary file move via location_file_path in vendor signup

P2 A01:2021 CWE-22 CVSS 6.5 EPSS n/a confirmed
Description

In the unauthenticated registerVendor handler, the client-supplied location_file_path is concatenated into public_path() for both source and destination of rename() with no basename()/realpath()/'..' filtering.

Evidence
app/Http/Controllers/VendorSignupController.php:150 if(file_exists('vendor_temp_stoarge/'.$request->location_file_path)) VendorSignupController.php:151 rename(public_path('vendor_temp_stoarge/'.$request->location_file_path), public_path('vendor_csv_uploads/').$request->location_file_path) Handler ajaxAddVendor (line 44); route routes/web.php:143 POST registerVendor — unauthenticated Sibling upload at :187 uses a safe md5 name, but this rename trusts the raw client path
Attack scenario
Attacker stages a temp file then POSTs location_file_path=../../public/shell.php to relocate it to a web-executable path.
Impact

Arbitrary file move/overwrite within (and potentially outside) the web root by an unauthenticated attacker — defacement, overwriting application files, or relocating an uploaded file to a web-executable path as a step toward RCE. High integrity impact.

Remediation
Sanitize with basename() and validate against the md5().ext allow-list produced by the upload step. Reject any value with path separators or '..'. Resolve and confirm both source and destination stay inside the intended directory via realpath() prefix checks before rename().
Verifier note

Confirmed at VendorSignupController.php:148-157, unauthenticated route in web.php:143. CVSS A set to N (move/overwrite is integrity, not direct availability); requires a pre-staged temp file so practical RCE is a chain.

ALD-27

ChatAgent area: inconsistent middleware and a session-flag presence check leave an endpoint unprotected

P2 A01:2021 CWE-287 CVSS 6.5 EPSS n/a confirmed
Description

The chat-agent area mixes three strategies: a custom middleware that only checks a session-key's presence, the proper auth:chat_agent guard on some routes, and no protection on getAjaxOfferDetails (line 38). The session-flag check is weaker than guard-based auth.

Evidence
app/Http/Middleware/chatAgentAuthValidate.php:19 if(!session::has('chat_agent_userusername')) — checks only key presence, not a guard routes/chat_agent.php:38 GET getAjaxOfferDetails — NO middleware routes/chat_agent.php: mixed enforcement (chatAgentAuthValidate group vs auth:chat_agent vs none) app/Http/Kernel.php:53-61 'chat_agent' group has no Authenticate
Attack scenario
Attacker calls GET /chat_agent/getAjaxOfferDetails directly without any session and retrieves pending-offer details.
Impact

Under-authenticated access to chat-agent offer data via the unprotected getAjaxOfferDetails endpoint, and a fragile model prone to gaps as routes are added.

Remediation
Apply auth:chat_agent consistently to ALL chat_agent routes including getAjaxOfferDetails. Add Authenticate to the 'chat_agent' middleware group. Replace the session-key presence check with Auth::guard('chat_agent')->check().
Verifier note

Confirmed: chat_agent.php:38 has no middleware; chatAgentAuthValidate only checks session::has(). Impact depends on what getAjaxOfferDetails returns — reads as a confidentiality issue.

ALD-28

Insecure session cookie configuration (SameSite=null, Secure=false)

P2 A05:2021 CWE-1275 CVSS 5.4 EPSS n/a confirmed
Description

The session cookie has no SameSite attribute and is not Secure by default. SameSite=null sends the cookie on cross-site requests (enabling the GET-based CSRF of ALD-22); Secure=false allows transmission over plaintext HTTP.

Evidence
config/session.php:197 same_site=null config/session.php:169 secure=>env('SESSION_SECURE_COOKIE',false) and no SESSION_SECURE_COOKIE in env config/session.php:49 encrypt=false
Attack scenario
On a plaintext-capable host an on-path attacker captures the session cookie; cross-site, the null SameSite lets the cookie ride CSRF requests.
Impact

Facilitates CSRF (cookie attached cross-site) and session cookie interception over HTTP, contributing to session hijacking.

Remediation
Set session same_site to 'lax' (or 'strict' for admin) and SESSION_SECURE_COOKIE=true; enforce HTTPS with HSTS. Consider session.encrypt=true.
Verifier note

Confirmed all three values in config/session.php. Largely an amplifier for ALD-22.

ALD-29

Unauthenticated Artisan command trigger via GET /run_queue

P2 A05:2021 CWE-749 CVSS 5.3 EPSS n/a confirmed
Description

A public GET endpoint invokes Artisan queue:work synchronously inside the web request, exposing internal operational functionality and enabling worker exhaustion.

Evidence
routes/web.php:105-108 Route::get('/run_queue', function(){ $exitCode=Artisan::call('queue:work'); return 'Processed'; }); — no auth, GET, web group
Attack scenario
Attacker repeatedly requests GET /run_queue to drive synchronous queue processing and exhaust workers.
Impact

Unauthenticated control over queue processing and a low-effort DoS vector (synchronous long-running jobs, including the 3600s-timeout CreatePdfJob, tie up PHP-FPM workers).

Remediation
Remove this route; run the queue worker via Supervisor (already configured in a-list-supervisorconf.config). If an HTTP trigger is unavoidable, gate it behind auth + a signed route and dispatch asynchronously.
Verifier note

Confirmed at routes/web.php:105-108.

ALD-30

Dangerous PHP runtime directives (allow_url_include On) and missing security response headers

P3 A05:2021 CWE-16 CVSS 4.0 EPSS n/a confirmed
Description

allow_url_include=On permits remote URL inclusion, amplifying any file-inclusion/SSRF sink. The app emits no security response headers and configures no HTTPS/HSTS; enable_dl=On further weakens the runtime.

Evidence
php.ini:6-7 allow_url_fopen=On and allow_url_include=On php.ini:9 enable_dl=On No CSP/X-Frame-Options/X-Content-Type-Options/HSTS anywhere (grep across app/config/routes/.htaccess returns nothing) config/app.php url=env('APP_URL','http://localhost'); env APP_URL=http://localhost (no HTTPS/HSTS)
Attack scenario
If any file-inclusion sink is reachable, allow_url_include lets the attacker include a remote PHP payload; absent CSP/X-Frame-Options, served pages are clickjackable.
Impact

Increases blast radius of inclusion/SSRF bugs (ALD-05) and exposes users to clickjacking, MIME-sniffing, and protocol downgrade/MITM. Low standalone severity.

Remediation
Set allow_url_include=Off and enable_dl=Off; keep allow_url_fopen=Off unless required. Add CSP, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy, and HSTS via middleware or web server. Enforce HTTPS and set a correct APP_URL.
Verifier note

Confirmed php.ini directives and absence of security headers.

ALD-31

Second-order SQL injection risk in whereRaw INTERVAL clause via DB-sourced avail_days

P3 A03:2021 CWE-89 CVSS 3.7 EPSS n/a theoretical
Description

Multiple public controllers build a whereRaw INTERVAL fragment by concatenating $offer->avail_days, read from venues.avail_days rather than parameterized. The source is currently a DB column, but no store path enforces strict integer validation, so the trust boundary is fragile.

Evidence
app/Http/Controllers/OfferController.php:220 whereRaw('DATE_ADD(food_offers_users.updated_at, INTERVAL '.$offer->avail_days.' DAY) > ?', [Carbon::now()]) (repeated 397,845,971) VenueOfferController.php:497 same pattern (repeated 671,1197,1650,2147) $offer->avail_days originates from venues.avail_days (DB column), not direct request input
Attack scenario
If an admin/vendor form later writes avail_days without integer validation, a stored payload would later execute in these INTERVAL clauses.
Impact

If avail_days ever becomes attacker-influenceable, a stored payload would execute in the INTERVAL clause of these public queries (boolean/time-based blind SQLi on unauthenticated endpoints). Currently low/theoretical because the value is DB-managed.

Remediation
Cast to integer at the point of use: whereRaw('... INTERVAL ? DAY ...', [(int)$offer->avail_days, Carbon::now()]) and enforce strict integer validation/column typing wherever avail_days is written.
Verifier note

Confirmed concatenation at OfferController.php:220 and the DB-sourced origin; theoretical because no current write path injects request data into avail_days.

ALD-32

Dormant SSRF / insecure cURL in Instagram validation rule (TLS verify off, follow-redirects on)

P3 A10:2021 CWE-918 CVSS 4.3 EPSS n/a theoretical
Description

The Instagram validation rule builds a cURL request from unsanitized user input with TLS verification disabled and redirect-following enabled. If wired into a validator, crafted input plus follow-redirects could coerce server-side requests; disabled TLS verification removes a defense. The rule is not currently referenced, so exploitability today is limited.

Evidence
app/Rules/Instagram.php:29 $insta_url = "https://www.instagram.com/".$value."/?__a=1" (user input in path) Instagram.php:35-39 CURLOPT_SSL_VERIFYPEER=FALSE; CURLOPT_FOLLOWLOCATION=true; CURLOPT_URL=$insta_url No 'new Instagram'/Rules\Instagram usage wired into any validator (grep across app/) — rule defined but not invoked AuditController getphoto($name) similar but only called from commented-out code
Attack scenario
If the rule is later wired into a validator, an attacker supplies a value that, via @-userinfo or redirect chains, coerces the server into requesting an internal URL.
Impact

If connected to a form: partial SSRF / outbound request control and MITM exposure. Currently dormant, low immediate risk.

Remediation
Validate the handle against ^[A-Za-z0-9._]{1,30}$ before use, re-enable CURLOPT_SSL_VERIFYPEER, disable CURLOPT_FOLLOWLOCATION (or bound + re-validate each hop), and remove the dead die()/echo statements. Delete the unused getphoto() helper.
Verifier note

Confirmed Instagram.php contents and that the rule is not wired in; remains theoretical/dormant. Also note the rule has die()/echo debug statements that would break validation in practice.

Remediation roadmap

Immediate
Stop the bleeding — assume breach, rotate everything, take any live host offline.
  • Confirm whether development-alist (cPanel accounts parhaman / frinkae, plus demo.alist.ae / dev.alist.ae DNS) still serves this code. If yes, take it offline today (closes ALD-01/03/05/07 exposure).
  • Rotate the MySQL password, the Gmail/Google account password for info@alist.ae (revoke app passwords, enable 2FA), and regenerate the Laravel APP_KEY (ALD-06).
  • Revoke the Google OAuth refresh token, reset the client secret, and rotate the AIza API key in Google Cloud Console (ALD-08).
  • Regenerate all four Slack webhooks and the Monday.com token, including the _bkp set (ALD-13). Rotate the embedded parhamandco app's APP_KEY + Mailtrap creds (ALD-10).
  • Rotate the admin@parhamandco.com credential whose bcrypt hash leaked, and handle the leaked signup PII per data-breach policy (ALD-14).
  • If the host must stay up briefly, set APP_DEBUG=false everywhere — it single-handedly closes the CVE-2021-3129 RCE (ALD-07/24).
This week
Close the unauthenticated and injection paths if the code will run at all.
  • Add Authenticate to the 'admin' middleware group and an adminAuthValidate middleware to the admin route group; introduce a real admin guard/role and remove Auth::routes() from inside /admin (ALD-01/02/03).
  • Parameterize every raw DB::select sink (UsersPreviews, FoodOffers, Venues, Audit, Admin controllers) with bound placeholders and integer casts (ALD-04/17/18/19/20).
  • Set isRemoteEnabled=false in CreatePdfJob and stop rendering user HTML via {!! !!} (ALD-05). Set allow_url_fopen/allow_url_include=Off (ALD-30).
  • Authenticate and add ownership checks to the unauth state-changing offer endpoints and review/IDOR routes; stop treating Hashids/sha1(date)/base64('prive') as authorization (ALD-15/16/25).
  • Remove committed secret/diagnostic files (.env_4-May-2020, .config.json, config/thirdparty.php_bkp, parhamandco.sql, parhamandco.zip, info.php/phpinfo.php/public/info.php, error_log) and add <FilesMatch> deny rules (ALD-10/11/14/21).
This month
Decommission the fork and migrate functionality to the maintained line.
  • Archive this frozen fork; migrate any still-needed functionality to alist-portal (Laravel 11 / PHP 8.2) rather than patching the EOL stack (ALD-09/12).
  • Purge all committed secrets/dumps/zips from git history with git filter-repo / BFG, then add a tightened .gitignore (.env*, *.sql, *.zip, *.config.json).
  • Convert all state-changing admin/offer actions from GET to POST/DELETE with CSRF; set session same_site=lax and SESSION_SECURE_COOKIE=true (ALD-22/28).
  • Add a TrustHosts allowlist, set a canonical https APP_URL, and remove the /run_queue route (ALD-23/29).
  • Apply auth:chat_agent consistently across the chat-agent routes and replace the session-key presence check with a guard (ALD-27).
Hardening
Defense-in-depth and prevention (if any successor stays in service).
  • Add Gitleaks/TruffleHog pre-commit and CI gates (scan for hooks.slack.com URLs, JWTs, AIza keys, base64 APP_KEYs).
  • Add CSP, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy, and HSTS; enforce HTTPS (ALD-30).
  • Configure Hashids with a per-app secret salt and generate upload filenames with random_bytes()/Str::random() (ALD-25).
  • Integer-cast the DB-sourced avail_days in the whereRaw INTERVAL clauses and fix the dormant insecure-cURL Instagram rule before any reuse (ALD-31/32).
  • Add route-coverage tests asserting unauthenticated access to admin/state-changing routes is rejected, and an SBOM/dependency-scan step in CI.

Code quality

Run / deploy

Local setup

# You almost certainly should NOT bring this up. PHP 7.2 is EOL.
# If you must, do it on an isolated VM with no network egress.

# 1. Install PHP 7.2 (Homebrew no longer ships it — use shivammathur/php tap or Docker php:7.2-fpm)
brew tap shivammathur/php
brew install shivammathur/php/php@7.2

# 2. Install Composer deps
composer install --ignore-platform-reqs

# 3. Configure env
cp .env.example .env
php artisan key:generate
# Edit .env: set DB_DATABASE / DB_USERNAME / DB_PASSWORD to a LOCAL throwaway DB
# Set APP_DEBUG=false unless you specifically need Ignition

# 4. Migrate
php artisan migrate
# Do NOT run db:seed blindly — see P2 finding about step*.php scripts

# 5. Serve
php artisan serve

# 6. Queue worker (optional, see a-list-supervisorconf.config)
php artisan queue:listen --tries=2 --timeout=7200

Environment

Deployment hints

Historical deployment was on cPanel hosting. Two distinct cPanel accounts appear in committed artifacts: frinkae (per error_log paths) and parhaman (per a-list-supervisorconf.config). Both targeted a directory named Development-Alist / development-alist. .htaccess rewrites every URL through /public/index.php, which is the standard "Laravel without docroot remap" cPanel pattern — and the same !-f rewrite is what makes the committed secret/diagnostic files web-reachable (ALD-11). .user.ini / php.ini override memory_limit to 6 GB and upload_max_filesize to 4 GB — clearly tuned for a specific upload feature. No CI pipeline is committed (no bitbucket-pipelines.yml, no GitHub Actions, no Jenkinsfile). Releases appear to have been manual pushes ("code pushed to live repository", commit dcf20e9 on 2020-10-30).

What to know before editing