Quasar Framework: Path Traversal / Arbitrary File Write via crafted Icon Genie profile
## Vulnerability Details **File**: `icongenie/lib/utils/get-assets-files.js` (line 35, `absoluteName: join(appDir, asset.folder, asset.name)`) **Validation gap**: `icongenie/lib/utils/validate-profile-object.js` (`assetsSchema`) — `folder`/`name` only checked with `Joi.string().required().min(1)`, no restriction on `..` sequences or absolute paths **Entry point**: `icongenie/lib/runner/generate.js` (`generate(argv)`) — `profile.assets = userProfile.assets`, loaded verbatim from a user-supplied JSON file via `--profile ` ### Root Cause `icongenie generate --profile ` loads a JSON "profile" describing icon/splashscreen assets to generate, where each asset entry has a `folder`/`name` describing where the generated file should be written relative to the Quasar project directory (`appDir`). `getAssetsFiles()` builds the write target with `join(appDir, asset.folder, asset.name)`. Node's `path.join` normalizes `..` segments arithmetically and does not clamp the result to stay inside `appDir`. The only validation before this (`validateProfileObject` → Joi `assetsSchema`) checks that `folder`/`name` are non-empty strings, with no `..` rejection and no containment check against `appDir`. A profile setting `folder: "../../../../../../tmp/pwned-by-icongenie"` sails through validation unmodified, and the generator writes attacker-influenced icon/splashscreen content to that path via a direct `writeFile`/`sharp().toFile()` call. ### Attack Scenario 1. Attacker publishes a "ready-made Icon
- Products
- @quasar/icongenie
- Affected versions
- Not specified
- Fixed versions
- 6.1.1
- CVEs
- CVE-2026-106103
- Confirmed fixed CVEs
- Not specified
- Updated in this feed
- 28 Oct 2026, 04:59 UTC · Revision 1
Source evidence
Quasar Framework: Path Traversal / Arbitrary File Write via crafted Icon Genie profile
core-osv · Original sourcePublished 7 Oct 2026, 16:14 UTC
## Vulnerability Details **File**: `icongenie/lib/utils/get-assets-files.js` (line 35, `absoluteName: join(appDir, asset.folder, asset.name)`) **Validation gap**: `icongenie/lib/utils/validate-profile-object.js` (`assetsSchema`) — `folder`/`name` only checked with `Joi.string().required().min(1)`, no restriction on `..` sequences or absolute paths **Entry point**: `icongenie/lib/runner/generate.js` (`generate(argv)`) — `profile.assets = userProfile.assets`, loaded verbatim from a user-supplied JSON file via `--profile ` ### Root Cause `icongenie generate --profile ` loads a JSON "profile" describing icon/splashscreen assets to generate, where each asset entry has a `folder`/`name` describing where the generated file should be written relative to the Quasar project directory (`appDir`). `getAssetsFiles()` builds the write target with `join(appDir, asset.folder, asset.name)`. Node's `path.join` normalizes `..` segments arithmetically and does not clamp the result to stay inside `appDir`. The only validation before this (`validateProfileObject` → Joi `assetsSchema`) checks that `folder`/`name` are non-empty strings, with no `..` rejection and no containment check against `appDir`. A profile setting `folder: "../../../../../../tmp/pwned-by-icongenie"` sails through validation unmodified, and the generator writes attacker-influenced icon/splashscreen content to that path via a direct `writeFile`/`sharp().toFile()` call. ### Attack Scenario 1. Attacker publishes a "ready-made Icon
osv:GHSA-wmpw-j6qv-mw88
@quasar/icongenie
Fixed versions: 6.1.1
Complete source record
{
"id": "4f55c7cc7a631761cd22b6b9bd85428ee64221c8cc5ea2616d24353f87113b1e",
"url": "https://osv.dev/vulnerability/GHSA-wmpw-j6qv-mw88",
"method": "structured",
"payload": {
"kind": "advisory",
"title": "Quasar Framework: Path Traversal / Arbitrary File Write via crafted Icon Genie profile",
"summary": "## Vulnerability Details **File**: `icongenie/lib/utils/get-assets-files.js` (line 35, `absoluteName: join(appDir, asset.folder, asset.name)`) **Validation gap**: `icongenie/lib/utils/validate-profile-object.js` (`assetsSchema`) — `folder`/`name` only checked with `Joi.string().required().min(1)`, no restriction on `..` sequences or absolute paths **Entry point**: `icongenie/lib/runner/generate.js` (`generate(argv)`) — `profile.assets = userProfile.assets`, loaded verbatim from a user-supplied JSON file via `--profile ` ### Root Cause `icongenie generate --profile ` loads a JSON \"profile\" describing icon/splashscreen assets to generate, where each asset entry has a `folder`/`name` describing where the generated file should be written relative to the Quasar project directory (`appDir`). `getAssetsFiles()` builds the write target with `join(appDir, asset.folder, asset.name)`. Node's `path.join` normalizes `..` segments arithmetically and does not clamp the result to stay inside `appDir`. The only validation before this (`validateProfileObject` → Joi `assetsSchema`) checks that `folder`/`name` are non-empty strings, with no `..` rejection and no containment check against `appDir`. A profile setting `folder: \"../../../../../../tmp/pwned-by-icongenie\"` sails through validation unmodified, and the generator writes attacker-influenced icon/splashscreen content to that path via a direct `writeFile`/`sharp().toFile()` call. ### Attack Scenario 1. Attacker publishes a \"ready-made Icon ",
"evidence": "osv:GHSA-wmpw-j6qv-mw88",
"products": [
{
"name": {
"value": "@quasar/icongenie",
"evidence": "/affected/0/package/name"
},
"prerequisites": [],
"fixed_versions": [
{
"value": "6.1.1",
"evidence": "/affected/0/ranges"
}
],
"affected_versions": []
}
],
"severity": "high",
"structured": {
"id": "GHSA-wmpw-j6qv-mw88",
"aliases": [
"CVE-2026-106103",
"GHSA-wmpw-j6qv-mw88"
],
"metrics": [
{
"type": "CVSS_V3",
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H"
}
],
"affected": [
{
"ranges": [
{
"type": "SEMVER",
"events": [
{
"introduced": "0"
},
{
"fixed": "6.1.1"
}
]
}
],
"package": {
"name": "@quasar/icongenie",
"purl": "pkg:npm/%40quasar/icongenie",
"ecosystem": "npm"
},
"database_specific": {
"source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-wmpw-j6qv-mw88/GHSA-wmpw-j6qv-mw88.json"
}
}
],
"modified": "2026-10-07T16:30:04.720529512Z",
"provider": "osv",
"references": [
"https://github.com/quasarframework/quasar/security/advisories/GHSA-wmpw-j6qv-mw88",
"https://nvd.nist.gov/vuln/detail/CVE-2026-106103",
"https://github.com/quasarframework/quasar/commit/87c89a80ec84f1eb7dbe258e5367161b0598ceb3",
"https://github.com/quasarframework/quasar",
"https://github.com/quasarframework/quasar/releases/tag/@quasar/icongenie-v6.1.1"
],
"provider_metadata": {
"cwe_ids": [
"CWE-22",
"CWE-73"
],
"severity": "HIGH",
"github_reviewed": true,
"nvd_published_at": "2026-10-06T18:16:51Z",
"github_reviewed_at": "2026-10-07T16:14:14Z"
}
},
"related_cves": [
{
"value": "CVE-2026-106103",
"evidence": "/aliases"
}
],
"security_relevant": true,
"severity_evidence": "/database_specific/severity"
},
"source_id": "core-osv",
"document_id": 95152,
"published_at": "2026-10-07T16:14:14+00:00"
}