← All repos

alist-vendor-portal

LEGACY Vue 3 + Vite UI prototype for the vendor portal — static mockup, no backend, no auth. Superseded by alist-vendors (Next.js).

Primary stackVue 3 + Vite (frontend only) Last commit2024-05-15 Repo size13MB Files211 Branchmaster Overall risk High — archive, do not extend

Executive summary

Tech stack

Vue 3 Vite 5.2 vue-router 4.3 Bootstrap 5.3 SCSS FilePond vue-qrcode-reader ApexCharts video.js Swiper Vitest ESLint + Prettier
CategoryTechnologyVersionNotes
FrameworkVue3.4.23 (Composition API, <script setup>)One file (App.vue) mixes Composition + Options API; EOL Vue 2.7.16 also nested via an abandoned dep (see AVP-03)
BuildVite5.2.9 (declared ^5.2.8)Plus @vitejs/plugin-vue-jsx and vite-plugin-vue-devtools (registered unconditionally); 5.2.x carries the 2024-2025 dev-server CVE family (AVP-04)
Routingvue-router4.3.212 routes, history mode, no auth guardbeforeEach only scrolls to top
StatenoneNo Pinia/Vuex. Single localStorage write in CardDetails.vue (UI state only, no token)
UI kitBootstrap5.3.3Loaded async via import('bootstrap/dist/js/bootstrap.bundle.js') in main.js
StylingSCSSsass 1.75 (Dart)Tokens in src/assets/styles/; custom ts-* class prefix. Note: a phantom npm scss@0.2.4 dep is unused (AVP-11)
File uploadvue-filepond7.0.4 / filepond 4.315 components wire FilePond with server="/api" (a dead endpoint); label-idle is a raw-HTML innerHTML sink — latent only (AVP-06)
QR / vouchervue-qrcode-reader5.5.4Used by ScanVoucherView; @detect handler is undefined (dead QR path) so validation is a client-side string compare (AVP-02)
Chartsapexcharts / vue3-apexcharts3.49 / 1.5All chart data hard-coded in component templates
Media / miscvideo.js, @fancyapps/ui, swiper8.12 / 5.0 / 11.1Galleries and post previews on campaign pages
TestsVitest + @vue/test-utils + jsdom1.4 / 2.4 / 24Exactly one spec: HelloWorld.spec.js (untouched template)
Lint / formatESLint + Prettier8.57 / 3.2Config present, no CI hook discovered (AVP-13)

Architecture

Classic Vite SPA shell: main.js creates the Vue app, installs the router, and mounts to #app. App.vue renders a Navbar + Sidebar chrome around <RouterView />, suppressing the chrome on /signin and /Billing. There is no API client, no store, no auth layer, and no environment-driven configuration — this is a presentation-only codebase. The absence of a server trust boundary is the single biggest mitigant on every finding below: there is nothing behind the UI to actually defraud today.

alist-vendor-portal/ ├─ package.json # "alistbussinessdashboard" (typo), declares vue-file-upload + phantom scss/ometa ├─ vite.config.js # alias "@" → src/, vue + vueJsx + VueDevTools plugins (unguarded) ├─ index.html # single entry, mounts <div id="app"> — NO Content-Security-Policy ├─ public/ # static assets — ~70 Greycliff CF font files dominate size ├─ src/ │ ├─ main.js # createApp, router, async Bootstrap JS import │ ├─ App.vue # shell; toggles Navbar/Sidebar; mixes setup + Options API │ ├─ router/index.js # 12 routes, beforeEach only scrolls — NO auth guard │ ├─ views/ # LoginView, HomeView, CampaignsView, CampaignsDetailsView, │ │ # CampaignsDeliveryCreationView, ScanVoucherView, BillingView (764L), │ │ # TeamManagementView, PlansView, AddOnsView, PlanCardsView, AboutView │ ├─ Layout/ # NavbarView.vue (role hard-coded 'Admin'), SidebarView.vue (inert logout) │ ├─ components/ │ │ ├─ Home/MainView.vue # dashboard with FilePond widgets (server="/api") │ │ ├─ Campaigns/ │ │ │ ├─ List/Details/{RedemptionsView, CampaignPosts, ...} │ │ │ └─ Create/{DeliveryView, DineInView} # FilePond uploaders, no backend │ │ ├─ Subscriptions/{PackagesGroup, AddOn/, Packages/CardDetails (Stripe-shaped UI, no SDK)} │ │ ├─ icons/ # 40+ inline-SVG icon components │ │ └─ __tests__/HelloWorld.spec.js # template test, not real coverage │ └─ assets/styles/ # main.scss + partials, Greycliff font faces, ts-* tokens └─ (no) .env, Dockerfile, CI config, server code, composer.json

Security assessment — methodology

This is a white-box static review of alist-vendor-portal conducted as part of the May 2026 A-List engagement. Four frontend assessment dimensions were applied — client-side secret exposure, client auth/token handling, client-side injection (XSS), and dependency / supply-chain — each grounded in the anthropic-cybersecurity-skills playbooks (which encode OWASP WSTG / API Top 10 / CWE detection methodology) and adapted to source review. Overlapping scanner findings were deduplicated into canonical entries, then adversarially re-verified against the actual source and lockfile; every finding below is rated confirmed, scored with CVSS 3.1, and mapped to OWASP Top 10 2021 + CWE.

OWASP Top 10 2021 OWASP WSTG OWASP API Top 10 2023 CWE CVSS 3.1 NIST CSF 2.0
Playbooks applied to this repo (4 dimensions):
Client-side secret & data exposure: implementing-secret-scanning-with-gitleaks testing-for-sensitive-data-exposure performing-cryptographic-audit-of-application
Auth & token handling (client): testing-jwt-token-security testing-api-authentication-weaknesses testing-oauth2-implementation-flaws
XSS & client-side injection: testing-for-xss-vulnerabilities exploiting-prototype-pollution-in-javascript testing-for-open-redirect-vulnerabilities
Dependencies, supply chain & CI/CD: performing-sca-dependency-scanning-with-snyk analyzing-sbom-for-supply-chain-vulnerabilities detecting-supply-chain-attacks-in-ci-cd prioritizing-vulnerabilities-with-cvss-scoring

Risk scorecard

Risk narrative

alist-vendor-portal is a LEGACY, backend-less Vue 3 + Vite UI prototype (last commit 2024-05-15, superseded by the active alist-vendors Next.js portal) — there is no server, no API client, no database, and no real data in this codebase, which substantially caps real-world exploitability. The dominant design issues are an entirely fake authentication layer (the "Sign in" button is a RouterLink, credentials are never bound or transmitted, and no route is guarded) and a voucher "redemption" that is a hard-coded client-side string-equality check against the shipped secret ALISTY2K002; both are confirmed and would be serious if this pattern or these values were carried into the successor or if a static build were hosted unprotected. The supply chain is the other concern: an abandoned, unused vue-file-upload@0.1.12 permanently drags EOL Vue 2.7.16 + @vue/compiler-sfc 2.7.16 into the install tree, and the build toolchain (Vite 5.2.9, esbuild 0.20.2, braces 3.0.2, nanoid 3.3.7) carries known 2024-2025 CVEs — all developer/build-time rather than production exposure, since the shipped artifact is a static Vue 3 bundle. Remaining findings (hard-coded promo code and beneficiary banking details, console-logged voucher input, unguarded VueDevTools, latent FilePond innerHTML sink, phantom scss/ometa deps, no CI/SCA) are hygiene/regression-prevention items. Overall risk is rated high on the strength of the secrets-in-git and design-auth findings per the engagement weighting, but the practical urgency is mitigated by the repo being retired — the correct disposition is to archive it and ensure none of these patterns are inherited by alist-vendors.

Overall risk
High
0P0 3P1 2P2 8P3

OWASP Top 10 2021 coverage

CategoryStatusNotes
A01:2021 Broken Access ControlVulnerableNo route guards and no auth state; every route directly addressable (folded into AVP-01).
A02:2021 Cryptographic FailuresVulnerableHard-coded secrets shipped in bundle/git: voucher code (AVP-02), promo code (AVP-07), beneficiary banking details (AVP-09).
A03:2021 InjectionPartialFilePond label-idle is an innerHTML sink but all values are static literals — latent only, no live injection path (AVP-06); no v-html/eval anywhere in src/.
A04:2021 Insecure DesignPartialClient-side-only trust decisions (fake auth, client voucher check) are insecure-by-design patterns; captured under AVP-01/AVP-02. No threat model for a prototype.
A05:2021 Security MisconfigurationVulnerableVueDevTools registered unconditionally (AVP-10); no Content-Security-Policy in index.html (verified absent).
A06:2021 Vulnerable and Outdated ComponentsVulnerableEOL Vue 2.7.16 via abandoned vue-file-upload (AVP-03); Vite 5.2.9 (AVP-04); esbuild 0.20.2 (AVP-05); braces/nanoid (AVP-12); phantom scss/ometa (AVP-11); no SCA gate (AVP-13).
A07:2021 Identification and Authentication FailuresVulnerableNo authentication whatsoever — decorative login, no session/token lifecycle, inert logout (AVP-01); client-side voucher "auth" (AVP-02).
A08:2021 Software and Data Integrity FailuresPartialInstall-time scripts (esbuild hasInstallScript) run unpinned with npm install and no npm ci/lockfile-integrity gate (noted in AVP-05/AVP-13); no SRI on assets, but no external CDN script tags in index.html either.
A09:2021 Security Logging and Monitoring FailuresVulnerableLeftover console.log statements leak the entered voucher code and payment state to the browser console (AVP-08); no real logging/monitoring (expected for a frontend prototype).
A10:2021 Server-Side Request Forgery (SSRF)N/ANo server, no backend, and zero outbound HTTP calls in src/ (no fetch/axios/XHR) — no SSRF surface.

Detailed findings

P0 critical · 0 P1 high · 3 P2 medium · 2 P3 low / info · 8
AVP-01

No authentication or access control: login is a decorative RouterLink and no route is guarded

P1 A07:2021 Identification and Authentication Failures CWE-287 CVSS 7.5 confirmed
Description

The application has no authentication layer of any kind. The login screen is purely cosmetic: the credential inputs are unbound (no v-model) and the "Sign in" button is a vue-router <RouterLink to="/"> that simply navigates to the dashboard. There is no auth state, identity provider, token issuance, session, or storage anywhere. The single global navigation guard only resets scroll and always calls next(), so every route (/, /Billing, /team, /scan-voucher, /campaigns, /plans, ...) is directly addressable by any anonymous visitor. The displayed "Admin" role and the "Sign out" controls are static template artifacts with no backing logic. This is a backend-less static prototype, so there is no server trust boundary to compensate — but equally there is no real data or backend to compromise; the risk materializes only if a built bundle is hosted and (in a successor) wired to a real API.

Evidence
src/views/LoginView.vue:33-38 — the 'Sign in' control is <RouterLink class="ts-btn ts-btn__primary ..." to="/">Sign in</RouterLink>; the surrounding <form> has no @submit handler src/views/LoginView.vue:14-31 — username/password <input> fields both carry id="firstName" (duplicated) and have NO v-model; their values are never read, hashed, or transmitted src/router/index.js:85-88 — the only router.beforeEach calls window.scrollTo(0,0) then unconditionally next(); no route record (src/router/index.js:16-82) carries meta.requiresAuth grep across src/ for fetch(/axios/XMLHttpRequest = 0 hits; no token in localStorage/sessionStorage/cookie (only CardDetails.vue:86 localStorage.setItem('selectedMonth') UI state) src/Layout/NavbarView.vue:35 user role hard-coded as 'Admin' string; NavbarView.vue:44-46 and SidebarView.vue:62-67 'Sign out' is an inert RouterLink to /signin with no @click/state clear
Attack scenario
An attacker discovers the static-hosted bundle URL (or it is shared for a demo). Without supplying any credentials, they click "Sign in" (or type /team, /Billing directly) and land in the full vendor dashboard. They read the bank-transfer beneficiary details, the promo code, and the voucher success UI, and can drive every flow the UI exposes — there is no session, guard, or server check to stop them.
Impact

If a vite build of this prototype is hosted on a reachable URL, every "protected" vendor screen (team management, billing/bank-transfer flow, campaign creation, voucher redemption UI) is open to any anonymous user with no credential whatsoever. Because the app is frontend-only with hard-coded mock data, no real records are exposed today; the principal danger is that the no-auth pattern (and its UI implying an auth flow that does not exist) gets carried into the active alist-vendors successor or misleads stakeholders during demos. Rated P1 as a design-level auth failure rather than P0 because there is no live backend behind it.

Remediation
Do not rely on this prototype for any authenticated functionality and remove/redirect any hosted instance. In the active alist-vendors (Next.js) portal: bind credentials to state, POST to a server auth endpoint over TLS, issue a short-lived session from the server, store it in an HttpOnly+Secure+SameSite cookie (never localStorage), add a global router guard that checks a server-validated session and redirects unauthenticated users to /signin, tag protected routes with meta.requiresAuth, and — critically — enforce authn/authz server-side on every data endpoint (never trust a client-rendered "Admin" label or client route guard).
Verifier note

Merged three overlapping scanner findings (no-auth login, no route guards, no session/token lifecycle) into one canonical auth-failure entry; the no-guard and inert-logout observations are folded in as evidence because they share the same root cause (no auth state to enforce). CVSS vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N/E:H/RL:U/RC:C.

AVP-02

Voucher redemption is a hard-coded client-side string-equality check against 'ALISTY2K002'

P1 A07:2021 Identification and Authentication Failures CWE-798 CVSS 6.5 confirmed
Description

Voucher redemption is "authenticated" by comparing the user-entered code to a string literal (ALISTY2K002) compiled into the client bundle. This is a shared secret embedded in shipped JavaScript: anyone who views source or DevTools (or guesses the short 11-char value) knows the valid code, and the success decision is made entirely in the browser with no server confirmation. The QR-scanner UI (vue-qrcode-reader) is wired to an undefined @detect handler and is therefore dead code, leaving the manual literal compare as the only working path. The associated redeemer identity USER-ALIST2023 is also hard-coded.

Evidence
src/views/ScanVoucherView.vue:33 — if (voucherCode.value === 'ALISTY2K002') { showSuccessCard() } — the only validation, performed entirely in the browser src/views/ScanVoucherView.vue:55-62 — watch(voucherCode,...) auto-invokes checkVoucherCode() once input length >= 11, so the literal compare fires on input with no server round-trip src/views/ScanVoucherView.vue:88 — success card hard-codes the redeemer identity 'USER-ALIST2023' src/views/ScanVoucherView.vue:111 vs 169-189 — template binds @detect="onDetect" but onDetect is never defined in <script setup> or the Options-API <script> (only a dead onDecode at :185), so the QR path is non-functional and the manual string compare is the sole path
Attack scenario
An attacker opens the deployed bundle, searches the JS for the string compare (or reads ScanVoucherView.vue), obtains ALISTY2K002, and types it into the voucher field. The length>=11 watcher fires checkVoucherCode(), the equality passes, and the UI shows "Voucher Redeemed! USER-ALIST2023" — with no server ever consulted. In a successor backed by a real ledger this becomes repeatable free redemption.
Impact

The voucher code provides zero authentication value because it is a single static secret shipped to every client; every instance redeems the same code, and the "Voucher Redeemed!" result cannot be trusted. In this backend-less prototype nothing is actually fulfilled, but if this exact value or pattern were carried into a deployed/successor system it would enable unlimited free redemptions and value fraud. The literal also constitutes a secret-in-git, consistent with the engagement's secrets-in-repo priority.

Remediation
Remove the hard-coded code from the client. Redemption must be validated server-side: the client submits the scanned/entered code to an authenticated API that checks validity, single-use/atomic-consume status, expiry, and vendor ownership, rate-limits attempts, and returns the authoritative result. Never ship redemption secrets in distributed frontend bundles. Retire/rotate ALISTY2K002 if it was ever used against any real backend.
Verifier note

Merged the three scanner findings about ALISTY2K002 (client_secrets P2, auth_token P1, redemption) into one canonical entry; CVSS normalized to 6.5 — the value is a hard-coded shared secret (C:L) and the decision can be forged client-side (I:L), but in this repo there is no backend to defraud, so not the 7.5 some scanners assigned. CVSS vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N/E:H/RL:U/RC:C.

AVP-03

EOL Vue 2.7.16 + @vue/compiler-sfc 2.7.16 pulled into the build tree by abandoned, unused vue-file-upload@0.1.12

P1 A06:2021 Vulnerable and Outdated Components CWE-1104 CVSS 7.5 confirmed
Description

The app is a Vue 3 SPA (vue@3.4.23) but declares a DIRECT dependency on vue-file-upload@0.1.12, an abandoned package (last published ~2017) whose only dependency is Vue 2 (^2.4.2). The lockfile resolves this to the EOL vue@2.7.16 and @vue/compiler-sfc@2.7.16 nested under node_modules/vue-file-upload/, which npm itself marks deprecated. Vue 2 reached end-of-life on 2023-12-31 and receives no security patches; the Vue-2 SFC/template-compiler line carries the historical vue-template-compiler ReDoS / template-injection XSS class that will never be fixed. vue-file-upload is dead and unused, so it provides zero functionality while permanently pinning an EOL framework into the install/build surface and confusing SBOM/SCA tooling (two Vue majors for one app).

Evidence
package.json:26 — "vue-file-upload": "^0.1.12" declared as a DIRECT dependency package-lock.json node_modules/vue-file-upload@0.1.12 deps {vue:^2.4.2}, resolved registry.npmjs.org/vue-file-upload-0.1.12.tgz package-lock.json node_modules/vue-file-upload/node_modules/vue == 2.7.16 with npm 'deprecated' flag: 'Vue 2 has reached EOL and is no longer actively maintained.' (confirmed via lockfile parse) package-lock.json node_modules/vue-file-upload/node_modules/@vue/compiler-sfc == 2.7.16 nested alongside the app's own vue@3.4.23 / @vue/compiler-sfc@3.4.23 grep -rn 'vue-file-upload' src/ → 0 hits (never imported anywhere; the app uses vue-filepond/filepond for uploads)
Attack scenario
A future CVE is disclosed in the Vue 2 SFC/template compiler (ReDoS or template-injection XSS class). Because Vue 2 is EOL and vue-file-upload hard-pins vue@^2.4.2 → 2.7.16, the project cannot patch by upgrade; any developer/CI that clones and npm installs runs the vulnerable, unpatchable compiler in its toolchain.
Impact

Pure unmitigated supply-chain footprint with no offsetting benefit: any future CVE in the Vue 2 compiler/runtime line cannot be remediated by upgrade because the version is hard-pinned via an abandoned package. Exposure is build/install-time on developer and CI machines (the shipped artifact is the Vue 3 bundle, which does not include Vue 2), so availability/integrity impact is theoretical rather than a live production exposure — but it is trivially removable. Kept at P1 per the engagement's vulnerable-component weighting; real-world urgency is tempered by the repo being abandoned/superseded.

Remediation
Remove vue-file-upload from package.json (it is unused) and regenerate package-lock.json, which eliminates vue@2.7.16 and @vue/compiler-sfc@2.7.16 entirely. Add an SCA gate (npm audit / Snyk) that fails the build on EOL/deprecated packages, and run depcheck to find other phantom direct deps.
Verifier note

Confirmed via lockfile parse: vue@2.7.16 and @vue/compiler-sfc@2.7.16 are present and nested under vue-file-upload, with the npm EOL deprecation flag set; the package is never imported in src/. CVSS vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H.

AVP-04

Vite 5.2.9 dev server affected by the 2024-2025 server.fs.deny path-traversal / arbitrary-file-read CVE family

P2 A06:2021 Vulnerable and Outdated Components CWE-22 CVSS 5.3 CVE-2025-30208 CVE-2025-31125 +5 CVEs confirmed
Description

vite@5.2.9 is affected by the 2024-2025 dev-server file-read / server.fs.deny bypass family: CVE-2024-45811/45812 (XSS / fs.allow bypass) and the 2025 path-traversal set CVE-2025-30208 (the "?raw??" / "?import&raw??" trailing-separator bypass), CVE-2025-31125, CVE-2025-31486, CVE-2025-32395, and CVE-2025-46565, all permitting arbitrary file read outside the project root via the dev server. All are present in the 5.2.x branch and fixed in later 5.4.x / 6.x / 7.x releases. The repo's documented workflow runs npm run dev (vite). vite.config.js sets no custom server.host, so the dev server binds to localhost by default, limiting exposure to local/adjacent attackers and malicious websites probing the dev port; the impact is confidentiality-only (info disclosure of files reachable by the dev process).

Evidence
package.json:42 — "vite": "^5.2.8" package-lock.json node_modules/vite == 5.2.9 (confirmed via lockfile parse), deps {esbuild:^0.20.1, postcss:^8.4.38, rollup:^4.13.0} vite.config.js sets no server.host / server.fs.allow / server.fs.deny / proxy → defaults apply; dev server binds localhost by default Full CVE set: CVE-2025-30208, CVE-2025-31125, CVE-2025-31486, CVE-2025-32395, CVE-2025-46565, CVE-2024-45811, CVE-2024-45812
Attack scenario
A developer runs npm run dev. While it is up, they visit a malicious page that issues crafted "?raw??" / trailing-separator requests to localhost:5173, bypassing server.fs.deny and reading arbitrary files (source, dotfiles, local secrets) reachable by the dev process, then exfiltrates them cross-origin.
Impact

If a developer runs the Vite dev server, a malicious web page the developer visits (or anyone on the LAN if --host is ever passed) can read arbitrary files reachable by the dev process via crafted path-traversal requests, exfiltrating source and local secrets. This is a developer-workstation risk, not a production exposure (the shipped artifact is a static vite build). Because the build tooling is on the abandoned 5.2.x line, future Vite CVEs also go unpatched.

Remediation
Upgrade vite to a patched 5.4.x (or 7.x) that fixes the server.fs.deny bypass family. Never run the dev server with --host on untrusted networks. Since the project is superseded, at minimum document that npm run dev should not be used, and pin a patched Vite if the prototype must still build.
Verifier note

Vite 5.2.9 version confirmed in lockfile; CVE list matches the known 2024-2025 dev-server advisory family. Dev-time/local exposure only — kept at P2/CVSS 5.3. CVSS vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N.

AVP-05

esbuild 0.20.2 (transitive via Vite) dev-server CORS wildcard allows cross-origin source theft

P2 A06:2021 Vulnerable and Outdated Components CWE-942 CVSS 5.3 GHSA-67mh-4wv8-2f99 confirmed
Description

esbuild@0.20.2 is pulled in transitively by vite@5.2.9. esbuild versions <= 0.24.2 are affected by GHSA-67mh-4wv8-2f99: the esbuild dev/serve server sets Access-Control-Allow-Origin: * on all responses, so any website the developer visits can issue cross-origin requests to the local esbuild server and read the responses, including transpiled application source. Fixed in esbuild 0.25.0. The lockfile also marks esbuild hasInstallScript:true (a post-install step that places the platform binary), which npm ci/npm install executes automatically — the canonical install-time code-execution surface if the package were ever compromised upstream.

Evidence
package-lock.json node_modules/esbuild == 0.20.2 with hasInstallScript: true (confirmed via lockfile parse) package-lock.json node_modules/vite (5.2.9) deps {esbuild:^0.20.1,...} → esbuild is a transitive build dependency package-lock.json node_modules/esbuild engines {node:>=12}
Attack scenario
A developer runs Vite/esbuild dev or serve mode. A malicious site they visit sends cross-origin fetch() requests to the local esbuild server; because every response carries Access-Control-Allow-Origin: *, the attacker page reads back the transpiled source and exfiltrates it.
Impact

On a developer workstation running the dev/serve tooling, a malicious page can exfiltrate the application's transpiled source via the permissive CORS policy. Confidentiality-only, local/adjacent, no production impact (built artifact is static). The install-script behavior is benign for the official package but is the standard vector exploited when a build dependency is trojanized upstream.

Remediation
Upgrade Vite to a release that depends on esbuild >= 0.25.0. Use npm ci against a reviewed lockfile and consider npm config set ignore-scripts true for CI install steps that do not require install scripts (verify/pin binaries instead). Add SCA scanning to surface transitive advisories like this.
Verifier note

esbuild 0.20.2 and hasInstallScript:true both confirmed in lockfile; advisory GHSA-67mh-4wv8-2f99 applies to <=0.24.2. Dev-time only — P2. CVSS vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N.

AVP-06

FilePond label-idle is a raw-HTML (innerHTML) sink — latent XSS if dynamic data is ever interpolated

P3 A03:2021 Injection CWE-79 CVSS 0.0 confirmed
Description

Five FilePond uploader instances configure label-idle with a raw HTML/SVG string, and FilePond renders labelIdle via innerHTML, making it an HTML sink by design. Verification confirms every label-idle in this repo is a hard-coded, developer-authored constant with NO interpolation of user, route, localStorage, API, or QR-scan data, and there is no v-html or other DOM-injection sink anywhere in src/. It is therefore NOT exploitable today. It is recorded as a latent/regression-only hardening concern: the same components set server="/api" (a currently dead upload endpoint), so a natural future evolution — wiring uploads and rendering a filename, error, or API-returned label through this attribute — would turn it into a stored/reflected XSS sink.

Evidence
src/components/Home/MainView.vue (two file-pond instances, label-idle raw HTML/SVG), server="/api" at MainView.vue:104 and :129 Identical static raw-HTML label-idle present in src/views/BillingView.vue:423-430 (server="/api" at :433), src/views/TeamManagementView.vue:55-57, src/components/Campaigns/Create/DeliveryView.vue:79-81, src/components/Campaigns/Create/DineInView.vue:84-86 Verified: grep for :label-idle / v-bind:label-idle / labelIdle = 0 hits (every label-idle is a static literal string, no interpolation); grep for v-html / innerHTML / eval / new Function / document.write across src/ = 0 hits
Attack scenario
Regression-only: a future change feeds a server-returned filename or error message into a FilePond HTML label. An attacker uploads a file whose name (or a crafted server error) contains markup; FilePond renders it via innerHTML, executing attacker script in the vendor's authenticated session.
Impact

No impact in the current code (no untrusted data reaches the sink and there is no backend). The risk is purely a future-regression one: if a maintainer interpolates dynamic/server/user data into label-idle/labelIdle (or relies on FilePond rendering server error text), DOM/stored XSS could be introduced in an authenticated vendor context.

Remediation
When this prototype is revived or its patterns are ported to alist-vendors: (1) never interpolate dynamic data into label-idle/labelIdle or other FilePond HTML-rendering options — keep them static or build from a fixed template; (2) if dynamic labels are required, sanitize with DOMPurify or pass plain text; (3) add a strict Content-Security-Policy (default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self') — currently absent from index.html — as defense-in-depth; (4) treat any future /api FilePond response strings as untrusted before display.
Verifier note

Confirmed latent only — all five label-idle values are static literals (no dynamic binding), and there is no v-html/innerHTML/eval anywhere in src/. CVSS 0 / P3 is correct. CVSS vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:N/A:N.

AVP-07

Hard-coded promo/discount code 'ALIST2024RLONGPROMOCODE' shipped in BillingView (default ref + template comment)

P3 A02:2021 Cryptographic Failures CWE-798 CVSS 3.1 confirmed
Description

A promotional/discount code is hard-coded as the default reactive value of promoCode and additionally left in an HTML comment, so it ships in the client bundle. In this prototype it only toggles a visual showDiscount flag (non-emptiness check) with no server validation, but it is a marketing/discount value exposed to every bundle reader and baked into git history.

Evidence
src/views/BillingView.vue:51 — const promoCode = ref('ALIST2024RLONGPROMOCODE') src/views/BillingView.vue:174 — <!-- value="ALIST2024RLONGPROMOCODE" --> (same value also left in a template comment) src/views/BillingView.vue:57-58 — handlePromoCodeChange() only toggles showDiscount based on non-emptiness; no server validation
Attack scenario
A user views source / DevTools on a deployed bundle, reads ALIST2024RLONGPROMOCODE, and applies it in any successor system that reuses the value, obtaining an unauthorized discount.
Impact

Low: discloses an internal promo code. If the same value were reused against a real discount engine in the successor it could let users apply an unauthorized discount. No effect in this backend-less prototype.

Remediation
Do not seed components with real promo codes; validate any discount code server-side and never embed it as a default value or comment. Remove the value from both the ref initializer (line 51) and the template comment (line 174).
Verifier note

Confirmed at BillingView.vue:51 and :174. CVSS vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N.

AVP-08

Voucher code and payment/route state written to the browser console via leftover console.log debug statements

P3 A09:2021 Security Logging and Monitoring Failures CWE-532 CVSS 2.4 confirmed
Description

Leftover debug console.log statements print the user-entered voucher code (itself the shared redemption secret) to the browser console on every change and on validation, and log the selected payment method. Console output is captured by browser extensions, screen recordings, and client-side error/telemetry tooling. 16 console.* calls remain across src/ and there is no build-time console-stripping configured.

Evidence
src/views/ScanVoucherView.vue:32 — console.log('Checking voucher code:', voucherCode.value) src/views/ScanVoucherView.vue:56 — console.log('Voucher code changed:', newValue) src/views/BillingView.vue:47 — console.log(type) logs the selected payment transferType 16 console.* statements remain across src/ (confirmed count); no vite build console-drop configured
Attack scenario
A browser extension or screen-recording tool running in the user's session captures the console output containing the typed voucher code, surfacing the redemption secret to a third party.
Impact

Low: leaks the entered voucher value into client logs/telemetry, reinforcing the weak voucher control (AVP-02); also discloses routing/payment-method state. No PII beyond the voucher string is logged in this prototype.

Remediation
Strip all console.* debug statements from production builds (e.g. esbuild drop:['console'] / vite-plugin-remove-console, or eslint no-console). Never log redemption codes, payment details, or any user input.
Verifier note

Confirmed; 16 console.* across src/ verified by grep count. CVSS vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N.

AVP-09

Hard-coded org beneficiary banking details (IBAN, account no, SWIFT, VAT) and sample vendor PII in Billing template

P3 A02:2021 Cryptographic Failures CWE-200 CVSS 2.0 confirmed
Description

The bank-transfer step renders A-List's (Metasphere Technologies / Emirates NBD) beneficiary account number, IBAN, SWIFT/BIC and Tax/VAT number as static template text rather than fetching them from config/API. These are the org's own receiving-account details intended to be shown to paying vendors, so they are semi-public by design, but committing them as source constants bakes real organizational financial identifiers permanently into the repo, git history, and the shipped bundle. A sample vendor email (emily@fujiyamasushi.com) is similarly hard-coded.

Evidence
src/views/BillingView.vue:408-414 — Metasphere Technologies / Emirates NBD / SWIFT EBILAEAD / Account No 6605788370801 / IBAN AE910260006605788370801 / Tax-VAT 100559645500003, all as static template text src/views/BillingView.vue:195 — value="emily@fujiyamasushi.com" (hard-coded sample vendor email, similar PII repeated in TeamManagementView.vue)
Attack scenario
An attacker harvests A-List's exact beneficiary name, IBAN, SWIFT and VAT from the public bundle/git history and uses them to craft a convincing invoice-fraud / vendor-impersonation email referencing the legitimate banking details.
Impact

Minimal confidentiality impact since beneficiary details are disclosed to payers anyway; flagged for hygiene because real financial identifiers live permanently in git history and the bundle and could aid social-engineering / invoice-fraud (an attacker spoofing A-List's known banking details). The hard-coded sample email is low-grade PII / test-data hygiene.

Remediation
Serve beneficiary details from runtime config/API rather than hardcoding them in the SPA; treat them as data, not source. Replace the hard-coded sample email/PII with obvious placeholders (e.g. user@example.com). Confirm these account/VAT numbers are the intended public payment details and not a different/internal account.
Verifier note

Confirmed at BillingView.vue:408-414 and :195. Kept P3; this is hygiene/info-disclosure, not a direct exploit. CVSS vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N.

AVP-10

VueDevTools registered unconditionally in vite.config.js (inspection surface / bloat in production builds)

P3 A05:2021 Security Misconfiguration CWE-489 CVSS 2.0 confirmed
Description

vite.config.js registers VueDevTools() unconditionally, with no command === 'serve' or mode === 'development' guard. While modern versions of vite-plugin-vue-devtools largely self-disable in build mode, relying on that implicit behavior is fragile; the intent of the config is to enable a component-inspection/debug surface, and an unguarded registration risks shipping devtools tooling (bundle bloat and an internal inspection surface) in any production bundle built from this repo.

Evidence
vite.config.js:6,13 — import VueDevTools from 'vite-plugin-vue-devtools' and plugins: [ vue(), vueJsx(), VueDevTools() ] with no mode/command guard
Attack scenario
A production build retains the devtools tooling; an attacker visiting the deployed app inspects component tree/state to map the application internals for further attacks.
Impact

Low: potential bundle bloat and a component-inspection surface exposed to anyone visiting a deployed build, and a leak of internal component structure for reconnaissance. No direct data compromise.

Remediation
Wrap the plugin so it only loads for the dev server, e.g. register VueDevTools() only when command === 'serve' (via the function form of defineConfig), or remove it for production builds. Verify the produced dist/ does not include devtools assets.
Verifier note

Folded in from the prior audit.html (not in the raw scanner set). Confirmed unguarded at vite.config.js:13. Kept P3 because current plugin versions may self-disable in build — the finding is a hardening/verify-the-bundle note. CVSS vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N.

AVP-11

Abandoned phantom direct dependencies scss@0.2.4 + ometa@0.2.2 (2013, never imported)

P3 A06:2021 Vulnerable and Outdated Components CWE-1104 CVSS 3.1 confirmed
Description

package.json declares a DIRECT dependency on the npm package scss@0.2.4, an experimental OMeta-based SCSS parser last published ~2013, which pins ometa@0.2.2 (a 2013 PEG/OMeta library). Neither is imported anywhere in source — actual SCSS compilation is done by Dart sass@1.75.0. The scss package was almost certainly installed by name-confusion with sass. These are dead, decade-old packages occupying the dependency/install surface with no functional purpose; they still resolve cleanly with integrity hashes (no active tampering), so the risk is latent.

Evidence
package.json:23 — "scss": "^0.2.4" declared as a DIRECT dependency package-lock.json node_modules/scss == 0.2.4 (deps {ometa:0.2.2}) and node_modules/ometa == 0.2.2, both confirmed via lockfile parse grep -rnE "(from|require\() *['\"]scss['\"]" src/ → 0 hits (the actual .scss stylesheet imports are handled by Dart sass@1.75.0, not the npm 'scss' package)
Attack scenario
An attacker takes over the dormant scss/ometa maintainer accounts, publishes a malicious patch within the ^0.2.4 / 0.2.2 range, and any developer/CI that reinstalls the prototype executes the trojanized code at install time.
Impact

Latent supply-chain footprint: abandoned packages can be hijacked (maintainer-account takeover / expired-domain email reclaim) and weaponized, and their decade-old code runs at install time on developer/CI machines. They also pollute SBOM/SCA output and signal poor dependency hygiene. No exploitable behavior in the running app (never imported).

Remediation
Remove scss from package.json (it is unused; Dart sass already handles all .scss files) and regenerate the lockfile, which also drops ometa. Run depcheck to audit declared dependencies against actual imports and remove other phantom packages (e.g. AVP-03's vue-file-upload).
Verifier note

Both packages confirmed present in lockfile and absent from src/ imports. Latent — P3. CVSS vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:L/A:N.

AVP-12

Vulnerable transitive build deps: braces 3.0.2 (CVE-2024-4068 ReDoS) and nanoid 3.3.7 (CVE-2024-55565 predictable IDs)

P3 A06:2021 Vulnerable and Outdated Components CWE-1333 CVSS 3.1 CVE-2024-4068 CVE-2024-55565 confirmed
Description

braces@3.0.2 is present transitively (micromatch/chokidar → file watching used by Vite/Vitest) and is affected by CVE-2024-4068 (uncontrolled resource consumption / ReDoS-style memory exhaustion on crafted brace patterns), fixed in 3.0.3. nanoid@3.3.7 is present transitively and is affected by CVE-2024-55565 (predictable/non-uniform output when called with a non-integer length), fixed in 3.3.8. Both are exercised only by build/test tooling in this frontend-only prototype, not in any request-handling path, and never process attacker-controlled input in the shipped static bundle, so real-world exploitability here is minimal.

Evidence
package-lock.json node_modules/braces == 3.0.2 (fixed in 3.0.3 — CVE-2024-4068 uncontrolled resource consumption), confirmed via lockfile parse package-lock.json node_modules/fill-range == 7.0.1 (braces dependency, part of the micromatch/chokidar chain used by Vite/Vitest file watching) package-lock.json node_modules/nanoid == 3.3.7 (fixed in 3.3.8 — CVE-2024-55565 predictable output for non-integer length), confirmed via lockfile parse
Attack scenario
Largely non-exploitable here: the theoretical path is a hostile glob pattern triggering braces ReDoS during a build, or nanoid producing weak IDs if called with a non-integer length — neither occurs in this repo's code.
Impact

Low. Build-time/test-time dependencies in a static-SPA prototype with no server. braces ReDoS could in theory slow a build given a hostile glob; nanoid's weakness matters only if used to mint security tokens with attacker-influenced length (not done here). Primary value is hygiene and preventing these advisories from propagating to tooling that copies this dependency set.

Remediation
Run npm audit fix / refresh the lockfile so braces >= 3.0.3 and nanoid >= 3.3.8 resolve. Typically auto-resolved by regenerating the lockfile against current registry metadata; no API changes required.
Verifier note

braces 3.0.2 and nanoid 3.3.7 confirmed in lockfile. Build-time only — P3. CVSS vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L.

AVP-13

No CI/CD pipeline and no automated dependency/SCA gating

P3 A06:2021 Vulnerable and Outdated Components CWE-1357 CVSS 2.0 confirmed
Description

There is no CI/CD configuration anywhere in the repo or its history, and no automated dependency scanning, lockfile-integrity, or SCA quality gate. On the positive side this means no pipeline secrets, no force-push-to-mirror steps, no unpinned third-party actions, and no expression-injection risk to flag. The downside is that the EOL/vulnerable components (AVP-03/04/05/11/12) would never be surfaced by a gate, and installs run unpinned install scripts (esbuild hasInstallScript) on whatever Node version a developer happens to have.

Evidence
No .github/, bitbucket-pipelines.yml, .gitlab-ci.yml, Jenkinsfile, .circleci/, or azure-pipelines.yml in the working tree (and none in git history per the scanner) origin = git@bitbucket.org:ParhamandCo/alist-vendor-portal.git (single 'master' branch); last commit 5471afd 2024-05-15 No .npmrc / .yarnrc and no engines pin in package.json → install scripts (esbuild, fsevents) run by default on any Node version; build uses `npm install`/`npm run build` rather than verified `npm ci`
Attack scenario
No automated control detects the vulnerable/EOL components above, so they persist silently; a developer reinstalling runs unpinned install-time scripts — the path that would be exploited if any of those build packages were compromised upstream.
Impact

No active CI attack surface (good), but the absence of any SCA gate means vulnerable/EOL components accumulate undetected. The repo is abandoned (last commit 2024-05-15) and superseded by alist-vendors, so operational risk is contained to anyone who still clones/builds the prototype.

Remediation
Treat as archived/read-only and document EOL status. If it must remain buildable, add a minimal SCA gate (npm audit --audit-level=high or Snyk), use npm ci against the reviewed lockfile with --ignore-scripts where feasible, pin engines.node, and SHA-pin any actions if a pipeline is ever added.
Verifier note

Confirmed: no CI config in tree; package.json has no engines pin and no .npmrc. Hygiene — P3. CVSS vector: CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:N/I:L/A:N.

Remediation roadmap

Immediate
Contain the legacy footprint before anything is reused.
  • Remove or redirect any hosted instance of this prototype; do not rely on it for any authenticated functionality (AVP-01).
  • Treat the repo as archived/read-only and add a README banner pointing at alist-vendors (AVP-13).
  • Rotate ALISTY2K002 if it was ever used against a real backend, and correlate the value across sibling repos (AVP-02).
  • Confirm the Billing beneficiary IBAN/SWIFT/VAT (AVP-09) are the intended public payment details and not an internal account.
This week
Strip the EOL/abandoned packages and dead secrets from the install + bundle surface.
  • Remove unused vue-file-upload from package.json and regenerate the lockfile to eliminate EOL vue@2.7.16 / @vue/compiler-sfc@2.7.16 (AVP-03).
  • Remove the phantom scss dependency (drops ometa); run depcheck for other phantom direct deps (AVP-11).
  • Run npm audit fix / refresh lockfile so braces >= 3.0.3 and nanoid >= 3.3.8 resolve (AVP-12).
  • Remove the hard-coded promo code from the ref initializer and template comment (AVP-07); strip leftover console.* debug statements (AVP-08).
This month
Patch the build toolchain if the prototype must remain buildable.
  • Upgrade Vite to a patched 5.4.x / 7.x that fixes the server.fs.deny path-traversal family; never run dev with --host on untrusted networks (AVP-04).
  • Upgrade Vite to a release depending on esbuild >= 0.25.0 to close the dev-server CORS wildcard (AVP-05).
  • Guard VueDevTools() behind command === 'serve' and verify dist/ excludes devtools assets (AVP-10).
  • Add a minimal SCA gate (npm audit --audit-level=high / Snyk), pin engines.node, and use npm ci --ignore-scripts where feasible (AVP-13).
Hardening (successor)
Ensure none of these client-trust patterns are inherited by alist-vendors.
  • Enforce real auth server-side: short-lived session in an HttpOnly+Secure+SameSite cookie, a global router guard checking a server-validated session, meta.requiresAuth on protected routes; never trust a client "Admin" label (AVP-01).
  • Validate voucher redemption server-side (validity, single-use/atomic-consume, expiry, vendor ownership, rate-limiting); never ship redemption secrets in the bundle (AVP-02).
  • Never interpolate dynamic data into FilePond label-idle; sanitize with DOMPurify if dynamic labels are required (AVP-06).
  • Add a strict Content-Security-Policy (default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self') — absent from index.html today (AVP-06).
  • Serve beneficiary banking details from runtime config/API; replace sample PII with placeholders (AVP-09).

Code quality

Run / deploy

Local setup

git clone <bitbucket-url>
cd alist-vendor-portal
npm install
npm run dev       # vite dev server, default http://localhost:5173 — see AVP-04/AVP-05
npm run build     # static bundle into dist/
npm run preview   # serve the built dist/
npm run test:unit # vitest (one trivial spec)
npm run lint      # eslint --fix

Environment

Deployment hints

No Dockerfile, no bitbucket-pipelines.yml, no Vercel/Netlify config, no IaC in the repo. Most likely deployed (if at all) as a static vite build output to a CDN bucket configured outside this codebase. Confirm with the team where (and whether) this was ever published before spending time on hosting — a publicly reachable build means every "protected" screen is anonymously accessible (AVP-01).

What to know before editing