fix(security): address multiple security vulnerabilities

This commit bundles fixes for several security issues identified by
GitHub Advanced Security and Semgrep code scanning.

## Workflow Permissions (CodeQL)
- Add explicit permissions blocks to GitHub Actions workflows
- Restrict GITHUB_TOKEN to minimum required permissions
- Affected files: automated-releases.yml, build-and-test-v3.yml,
  publish-npm.yml, test-simple.yml

## Path Traversal (CodeQL)
- Fix directory traversal vulnerability in screen example
- Add path validation using filepath.Clean and containment checks
- Affected file: v3/examples/screen/main.go

## Rollup XSS Vulnerability (Semgrep)
- Update rollup from 3.28.0 to 3.29.5
- Fixes CVE-2024-47068 (Cross-site Scripting)
- Affected file: v3/examples/dev/frontend/package-lock.json

Note: The setup wizard command injection alert was reviewed and determined
to be a false positive - commands originate from backend package manager
detection, not user input. Added clarifying documentation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Lea Anthony 2026-01-22 06:17:26 +11:00
commit 228e5745d7
8 changed files with 163 additions and 63 deletions

View file

@ -20,6 +20,10 @@ on:
env:
GO_VERSION: '1.24'
# Restrict default GITHUB_TOKEN permissions
permissions:
contents: read
jobs:
check-permissions:
name: Check Release Permissions
@ -45,6 +49,8 @@ jobs:
detect-v2-changes:
name: Detect v2 Changes
runs-on: ubuntu-latest
permissions:
contents: read
needs: check-permissions
if: needs.check-permissions.outputs.authorized == 'true'
outputs:
@ -90,8 +96,10 @@ jobs:
fi
detect-v3-changes:
name: Detect v3-alpha Changes
name: Detect v3-alpha Changes
runs-on: ubuntu-latest
permissions:
contents: read
needs: check-permissions
if: needs.check-permissions.outputs.authorized == 'true'
outputs:
@ -140,6 +148,8 @@ jobs:
release-v2:
name: Create v2 Release
runs-on: ubuntu-latest
permissions:
contents: write
needs: [check-permissions, detect-v2-changes]
if: |
needs.check-permissions.outputs.authorized == 'true' &&
@ -232,6 +242,8 @@ jobs:
release-v3:
name: Create v3-alpha Release
runs-on: ubuntu-latest
permissions:
contents: write
needs: [check-permissions, detect-v3-changes]
if: |
needs.check-permissions.outputs.authorized == 'true' &&
@ -326,6 +338,7 @@ jobs:
summary:
name: Release Summary
runs-on: ubuntu-latest
permissions: {}
needs: [check-permissions, detect-v2-changes, detect-v3-changes, release-v2, release-v3]
if: always() && needs.check-permissions.outputs.authorized == 'true'
steps:

View file

@ -10,10 +10,15 @@ on:
branches:
- v3-alpha
# Restrict default GITHUB_TOKEN permissions
permissions:
contents: read
jobs:
check_approval:
name: Check PR Approval
runs-on: ubuntu-latest
permissions: {}
if: github.base_ref == 'v3-alpha'
outputs:
approved: ${{ steps.check.outputs.approved }}
@ -31,6 +36,8 @@ jobs:
name: Run JS Tests
needs: check_approval
runs-on: ubuntu-latest
permissions:
contents: read
if: github.base_ref == 'v3-alpha'
strategy:
matrix:
@ -100,6 +107,8 @@ jobs:
name: Run Go Tests v3
needs: [check_approval, test_js]
runs-on: ${{ matrix.os }}
permissions:
contents: read
if: github.base_ref == 'v3-alpha'
strategy:
fail-fast: false
@ -175,6 +184,7 @@ jobs:
if: always()
needs: [test_js, test_go, test_templates]
runs-on: ubuntu-latest
permissions: {}
steps:
- uses: geekyeggo/delete-artifact@v5
with:
@ -187,6 +197,8 @@ jobs:
name: Test Templates
needs: [test_js, test_go]
runs-on: ${{ matrix.os }}
permissions:
contents: read
if: github.base_ref == 'v3-alpha'
strategy:
fail-fast: false
@ -263,6 +275,7 @@ jobs:
build_results:
if: ${{ always() }}
runs-on: ubuntu-latest
permissions: {}
name: v3 Build Results
needs: [test_go, test_js, test_templates]
steps:

View file

@ -3,6 +3,10 @@ on:
branches: ['v3-alpha']
workflow_dispatch:
# Restrict default GITHUB_TOKEN permissions
permissions:
contents: read
concurrency:
group: publish-npm-v3
cancel-in-progress: true
@ -11,6 +15,8 @@ jobs:
detect:
name: Detect committed changes
if: github.event_name != 'workflow_dispatch'
permissions:
contents: read
outputs:
changed: ${{ steps.package-json-changes.outputs.any_modified == 'true' || steps.source-changes.outputs.any_modified == 'true' }}
runs-on: ubuntu-latest

View file

@ -3,9 +3,13 @@ name: Test Simple
on:
workflow_dispatch:
# Restrict default GITHUB_TOKEN permissions
permissions: {}
jobs:
test:
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Test
run: echo "Hello World"

View file

@ -33,6 +33,9 @@ After processing, the content will be moved to the main changelog and this file
## Security
<!-- Security-related changes -->
- Restrict GITHUB_TOKEN permissions in workflow files to follow principle of least privilege
- Fix path traversal vulnerability in screen example asset middleware
- Update rollup to 3.29.5 to fix XSS vulnerability (CVE-2024-47068)
---

View file

@ -21,6 +21,7 @@
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
@ -37,6 +38,7 @@
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
@ -53,6 +55,7 @@
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
@ -69,6 +72,7 @@
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
@ -85,6 +89,7 @@
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
@ -101,6 +106,7 @@
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
@ -117,6 +123,7 @@
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
@ -133,6 +140,7 @@
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@ -149,6 +157,7 @@
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@ -165,6 +174,7 @@
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@ -181,6 +191,7 @@
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@ -197,6 +208,7 @@
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@ -213,6 +225,7 @@
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@ -229,6 +242,7 @@
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@ -245,6 +259,7 @@
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@ -261,6 +276,7 @@
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@ -277,6 +293,7 @@
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
@ -293,6 +310,7 @@
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
@ -309,6 +327,7 @@
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
@ -325,6 +344,7 @@
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
@ -341,6 +361,7 @@
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
@ -357,6 +378,7 @@
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
@ -366,22 +388,25 @@
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
"dev": true
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT"
},
"node_modules/@sveltejs/vite-plugin-svelte": {
"version": "2.4.5",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.4.5.tgz",
"integrity": "sha512-UJKsFNwhzCVuiZd06jM/psscyNJNDwjQC+qIeb7GBJK9iWeQCcIyfcPWDvbCudfcJggY9jtxJeeaZH7uny93FQ==",
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.5.3.tgz",
"integrity": "sha512-erhNtXxE5/6xGZz/M9eXsmI7Pxa6MS7jyTy06zN3Ck++ldrppOnOlJwHHTsMC7DHDQdgUp4NAc4cDNQ9eGdB/w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@sveltejs/vite-plugin-svelte-inspector": "^1.0.3",
"@sveltejs/vite-plugin-svelte-inspector": "^1.0.4",
"debug": "^4.3.4",
"deepmerge": "^4.3.1",
"kleur": "^4.1.5",
"magic-string": "^0.30.2",
"magic-string": "^0.30.3",
"svelte-hmr": "^0.15.3",
"vitefu": "^0.2.4"
},
@ -389,15 +414,16 @@
"node": "^14.18.0 || >= 16"
},
"peerDependencies": {
"svelte": "^3.54.0 || ^4.0.0",
"svelte": "^3.54.0 || ^4.0.0 || ^5.0.0-next.0",
"vite": "^4.0.0"
}
},
"node_modules/@sveltejs/vite-plugin-svelte-inspector": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-1.0.3.tgz",
"integrity": "sha512-Khdl5jmmPN6SUsVuqSXatKpQTMIifoQPDanaxC84m9JxIibWvSABJyHpyys0Z+1yYrxY5TTEQm+6elh0XCMaOA==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-1.0.4.tgz",
"integrity": "sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "^4.3.4"
},
@ -411,12 +437,13 @@
}
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "2.1.2"
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
@ -432,6 +459,7 @@
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@ -442,6 +470,7 @@
"integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
@ -474,11 +503,12 @@
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
@ -492,32 +522,32 @@
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
"integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/magic-string": {
"version": "0.30.2",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.2.tgz",
"integrity": "sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==",
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
},
"engines": {
"node": ">=12"
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
@ -525,6 +555,7 @@
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@ -533,15 +564,16 @@
}
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"dev": true
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
"license": "ISC"
},
"node_modules/postcss": {
"version": "8.4.28",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.28.tgz",
"integrity": "sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==",
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dev": true,
"funding": [
{
@ -557,20 +589,22 @@
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.6",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/rollup": {
"version": "3.28.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.28.0.tgz",
"integrity": "sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==",
"version": "3.29.5",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz",
"integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==",
"dev": true,
"license": "MIT",
"bin": {
"rollup": "dist/bin/rollup"
},
@ -583,10 +617,11 @@
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
@ -596,6 +631,8 @@
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.59.2.tgz",
"integrity": "sha512-vzSyuGr3eEoAtT/A6bmajosJZIUWySzY2CzB3w2pgPvnkUjGqlDnsNnA0PMO+mMAhuyMul6C2uuZzY6ELSkzyA==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 8"
}
@ -605,6 +642,7 @@
"resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.3.tgz",
"integrity": "sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==",
"dev": true,
"license": "ISC",
"engines": {
"node": "^12.20 || ^14.13.1 || >= 16"
},
@ -613,10 +651,12 @@
}
},
"node_modules/vite": {
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.5.tgz",
"integrity": "sha512-ifW3Lb2sMdX+WU91s3R0FyQlAyLxOzCSCP37ujw0+r5POeHPwe6udWVIElKQq8gk3t7b8rkmvqC6IHBpCff4GQ==",
"version": "4.5.14",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.14.tgz",
"integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.18.10",
"postcss": "^8.4.27",
@ -668,12 +708,13 @@
}
},
"node_modules/vitefu": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.4.tgz",
"integrity": "sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==",
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz",
"integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"vite": "^3.0.0 || ^4.0.0"
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0"
},
"peerDependenciesMeta": {
"vite": {

View file

@ -8,6 +8,7 @@ import (
"os"
"path/filepath"
"runtime"
"strings"
"github.com/wailsapp/wails/v3/pkg/application"
)
@ -44,12 +45,22 @@ func main() {
_, filename, _, _ := runtime.Caller(0)
dir := filepath.Dir(filename)
url := r.URL.Path
path := dir + "/assets" + url
assetsDir := filepath.Join(dir, "assets")
if _, err := os.Stat(path); err == nil {
// Clean and validate the path to prevent directory traversal
cleanPath := filepath.Clean(r.URL.Path)
fullPath := filepath.Join(assetsDir, cleanPath)
// Ensure the resolved path is still within the assets directory
if !strings.HasPrefix(fullPath, assetsDir+string(filepath.Separator)) && fullPath != assetsDir {
// Path traversal attempt detected, fall back to default handler
next.ServeHTTP(w, r)
return
}
if _, err := os.Stat(fullPath); err == nil {
// Serve file from disk to make testing easy
http.ServeFile(w, r, path)
http.ServeFile(w, r, fullPath)
} else {
// Passthrough to the default asset handler if file not found on disk
next.ServeHTTP(w, r)

View file

@ -544,6 +544,15 @@ type InstallResponse struct {
Error string `json:"error,omitempty"`
}
// handleInstallDependency executes dependency installation commands.
//
// Security note: This endpoint executes commands that originate from the backend's
// package manager detection (see packagemanager.InstallCommand). The commands are
// generated server-side based on the detected OS package manager, not from arbitrary
// user input. This is a local development tool where the explicit purpose is to help
// users install dependencies on their own machine. The frontend currently only uses
// this data to copy commands to clipboard - this endpoint exists for potential future
// use or automation scenarios.
func (w *Wizard) handleInstallDependency(rw http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(rw, "Method not allowed", http.StatusMethodNotAllowed)
@ -569,7 +578,7 @@ func (w *Wizard) handleInstallDependency(rw http.ResponseWriter, r *http.Request
return
}
cmd := exec.Command(parts[0], parts[1:]...)
cmd := exec.Command(parts[0], parts[1:]...) // #nosec G204 -- commands originate from backend package manager detection, not user input
output, err := cmd.CombinedOutput()
if err != nil {