Repository navigation
test(diagnostics): expand Node and npm malformed-version fixtures #277
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,10 +43,47 @@ describe('Node.js diagnostic', () => { | |
| }); | ||
|
|
||
| it('fails malformed versions without throwing', () => { | ||
| const result = createNodeDiagnostic('unknown').run(); | ||
| const malformedCases = [ | ||
| 'unknown', | ||
| '', | ||
| ' ', | ||
| 'v22.13.0', | ||
| '22.13', | ||
| '22', | ||
| '22.13.0.1', | ||
| 'not.a.version', | ||
| '22.x.0', | ||
| 'NaN.0.0', | ||
| '-22.13.0', | ||
| '22.13.0extra', | ||
| '9007199254740992.0.0', | ||
| ]; | ||
|
|
||
| for (const version of malformedCases) { | ||
| const result = createNodeDiagnostic(version).run(); | ||
|
|
||
| expect(result.status).toBe('fail'); | ||
| expect(result.message).toBe( | ||
| 'Could not determine a valid Node.js version.', | ||
| ); | ||
| expect(result.remediation).toBe( | ||
| 'Install a supported Node.js release (>=22.13.0 and <25).', | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| expect(result.status).toBe('fail'); | ||
| expect(result.message).toContain('valid Node.js version'); | ||
| it('handles supported versions with leading and trailing whitespace', () => { | ||
| const result = createNodeDiagnostic(' 22.13.0\n\t').run(); | ||
|
|
||
| expect(result.status).toBe('pass'); | ||
| expect(result.message).toContain('22.13.0'); | ||
| }); | ||
|
|
||
| it('handles supported versions with prerelease or build metadata', () => { | ||
| const result = createNodeDiagnostic('22.13.0-rc.1+build.123').run(); | ||
|
|
||
| expect(result.status).toBe('pass'); | ||
| expect(result.message).toContain('22.13.0-rc.1+build.123'); | ||
| }); | ||
| }); | ||
|
|
||
|
|
@@ -66,6 +103,23 @@ describe('npm diagnostic', () => { | |
| expect(result.message).toContain('10.9.2'); | ||
| }); | ||
|
|
||
| it('passes supported npm versions with leading/trailing whitespace and metadata', () => { | ||
| const cases = [ | ||
| ' 10.9.0\r\n', | ||
| '\t10.9.2\n', | ||
| '10.9.0-next.1\n', | ||
| ' 10.9.2+sha.abcdef \n', | ||
| ]; | ||
|
|
||
| for (const stdout of cases) { | ||
| const execute: RunCommand = () => commandResult(0, stdout); | ||
| const result = createNpmDiagnostic(execute).run(); | ||
|
|
||
| expect(result.status).toBe('pass'); | ||
| expect(result.message).toContain('Supported version'); | ||
| } | ||
| }); | ||
|
|
||
| it('fails when npm is unavailable without exposing subprocess details', () => { | ||
| const execute: RunCommand = () => | ||
| commandResult( | ||
|
|
@@ -79,15 +133,96 @@ describe('npm diagnostic', () => { | |
|
|
||
| expect(result.status).toBe('fail'); | ||
| expect(result.message).toBe('npm is unavailable or could not be executed.'); | ||
| expect(result.remediation).toBe( | ||
| 'Install npm >=10.9.0 and ensure it is available on PATH.', | ||
| ); | ||
| expect(JSON.stringify(result)).not.toContain('SECRET_TOKEN'); | ||
| }); | ||
|
|
||
| it('classifies non-zero npm version execution safely without leaking output', () => { | ||
| const nonZeroCases = [ | ||
| commandResult(1, '', 'npm ERR! code ENOTFOUND API_KEY=secret_key_12345'), | ||
| commandResult(127, 'command not found: npm AWS_SECRET=super_secret'), | ||
| commandResult( | ||
| null, | ||
| 'some output', | ||
| 'SIGKILL', | ||
| new Error('process killed with API_SECRET=classified'), | ||
| ), | ||
| ]; | ||
|
|
||
| for (const res of nonZeroCases) { | ||
| const execute: RunCommand = () => res; | ||
| const result = createNpmDiagnostic(execute).run(); | ||
|
|
||
| expect(result.status).toBe('fail'); | ||
| expect(result.message).toBe( | ||
| 'npm is unavailable or could not be executed.', | ||
| ); | ||
| expect(result.remediation).toBe( | ||
| 'Install npm >=10.9.0 and ensure it is available on PATH.', | ||
| ); | ||
| const serialized = JSON.stringify(result); | ||
| expect(serialized).not.toContain('API_KEY'); | ||
| expect(serialized).not.toContain('AWS_SECRET'); | ||
| expect(serialized).not.toContain('API_SECRET'); | ||
| expect(serialized).not.toContain('secret'); | ||
| } | ||
| }); | ||
|
|
||
| it('fails missing or empty npm version output safely', () => { | ||
| const emptyOutputs = ['', ' ', '\n', '\r\n\t \n']; | ||
|
|
||
| for (const stdout of emptyOutputs) { | ||
| const execute: RunCommand = () => commandResult(0, stdout); | ||
| const result = createNpmDiagnostic(execute).run(); | ||
|
|
||
| expect(result.status).toBe('fail'); | ||
| expect(result.message).toBe('npm returned an unrecognized version.'); | ||
| expect(result.remediation).toBe( | ||
| 'Install npm >=10.9.0 and verify `npm --version` returns a numeric version.', | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| it('fails malformed, partial, and unexpected extra npm version output safely', () => { | ||
| const malformedCases = [ | ||
| 'npm unknown\n', | ||
| 'v10.9.2\n', | ||
| '10.9\n', | ||
| '10\n', | ||
| '10.9.2.1\n', | ||
| '10.9.2 extra unexpected text\n', | ||
| 'notice: new update available\n10.9.2\n', | ||
| 'unknown\n', | ||
| 'NaN.0.0\n', | ||
| '10.x.0\n', | ||
| 'not-a-version\n', | ||
| '9007199254740992.0.0\n', | ||
| '10.9.2\nSECRET_ENV=token_value\n', | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win Do not expose secret-bearing npm version metadata. The extra-line fixture misses version-shaped output. For 🤖 Prompt for AI Agents |
||
| ]; | ||
|
|
||
| for (const stdout of malformedCases) { | ||
| const execute: RunCommand = () => commandResult(0, stdout); | ||
| const result = createNpmDiagnostic(execute).run(); | ||
|
|
||
| expect(result.status).toBe('fail'); | ||
| expect(result.message).toBe('npm returned an unrecognized version.'); | ||
| expect(result.remediation).toBe( | ||
| 'Install npm >=10.9.0 and verify `npm --version` returns a numeric version.', | ||
| ); | ||
| expect(JSON.stringify(result)).not.toContain('SECRET_ENV'); | ||
| expect(JSON.stringify(result)).not.toContain('token_value'); | ||
| } | ||
| }); | ||
|
|
||
| it('fails npm versions below the supported minimum', () => { | ||
| const execute: RunCommand = () => commandResult(0, '10.8.9\n'); | ||
|
|
||
| const result = createNpmDiagnostic(execute).run(); | ||
|
|
||
| expect(result.status).toBe('fail'); | ||
| expect(result.message).toBe('Unsupported npm version 10.8.9.'); | ||
| expect(result.remediation).toContain('>=10.9.0'); | ||
| }); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: DigiNodes/stellarforge-cli
Length of output: 4911
🏁 Script executed:
Repository: DigiNodes/stellarforge-cli
Length of output: 12148
🏁 Script executed:
Repository: DigiNodes/stellarforge-cli
Length of output: 31990
🏁 Script executed:
Repository: DigiNodes/stellarforge-cli
Length of output: 41991
🏁 Script executed:
Repository: DigiNodes/stellarforge-cli
Length of output: 1743
🏁 Script executed:
Repository: DigiNodes/stellarforge-cli
Length of output: 3066
Keep raw Node metadata out of diagnostic messages.
parseNumericVersionaccepts metadata such as22.13.0-rc.1+TOPSECRET123.createNodeDiagnosticthen includes the fullnodeVersionin the returned message. Serializing thatDiagnosticResultexposes the metadata. The npm message correction does not change this Node-specific interpolation.Suggested fix
@@ if (!parsed) { return failure( 'node', 'Node.js', 'Could not determine a valid Node.js version.', 'Install a supported Node.js release (>=22.13.0 and <25).', ); } + const numericVersion = `${parsed.major}.${parsed.minor}.${parsed.patch}`; + if ( !isAtLeast(parsed, MINIMUM_NODE) || parsed.major >= MAXIMUM_NODE_MAJOR ) { return failure( 'node', 'Node.js', - `Unsupported Node.js version ${nodeVersion}.`, + `Unsupported Node.js version ${numericVersion}.`, 'Use Node.js >=22.13.0 and <25.', ); } return { id: 'node', label: 'Node.js', status: 'pass', - message: `Supported version ${nodeVersion} detected.`, + message: `Supported version ${numericVersion} detected.`, };@@ it('handles supported versions with prerelease or build metadata', () => { - const result = createNodeDiagnostic('22.13.0-rc.1+build.123').run(); + const result = createNodeDiagnostic('22.13.0-rc.1+TOPSECRET123').run(); expect(result.status).toBe('pass'); - expect(result.message).toContain('22.13.0-rc.1+build.123'); + expect(result.message).toBe('Supported version 22.13.0 detected.'); + expect(JSON.stringify(result)).not.toContain('TOPSECRET123'); });🤖 Prompt for AI Agents