Skip to content

Conversation

@mallachari
Copy link
Contributor

@mallachari mallachari commented Nov 19, 2024

What/Why/How?

CLI preview command was failing on Windows with specific Node versions.
Turns out there is a breaking change in Node API that prevents passing .cmd files to spawn function without using shell: true option. Since we're using npx.cmd shell option is now necessary.

Using shell: true brings an actual shell which introduces a risk of shell injection. This means that the arguments should be sanitized before passing. In our case those that require to be sanitized are strings not validated by yargs itself: paths and locale. For these values there are sanitize functions restricting input to the set of allowed characters, which significantly limits the possible risk (but also reduces characters that can be used in regular paths).

To limit the risk exposure shell: true is used only on windows platform.

Other options considered:

  • using execFile instead of spawn - execFile doesn't spawn a shell which makes it safer, however it behaves differently and doesn't handle long running processes and user interaction like spawn does. eject use case is fine with execFile, but preview and translate won't work in this case
  • escaping arguments - it would be fine if we were using single platform/shell, but in this case we need to support both unix and windows. Within windows we also have multiple shells (cmd, powershell, git-bash) each having different way of escaping commands. I didn't find any library that could cover all these cases and writing it ourselves would require shell detection on windows which might be hard to do (and not always work)
  • checking if path exists - checking a path with fs.existsSync wouldn't prevent injections since a malformed path might be created (there are no limitations in characters)
  • validating path format with regex - that still would need to include the whitelisted character set so it wouldn't bring additional protection

Reference

https://github.com/Redocly/redocly/issues/11284

Testing

Tested on Windows and MacOS with Node versions from before and after the change.

Screenshots (optional)

Check yourself

  • Code changed? - Tested with redoc/reference-docs/workflows (internal)
  • All new/updated code is covered with tests
  • New package installed? - Tested in different environments (browser/node)

Security

  • Security impact of change has been considered
  • Code follows company security practices and guidelines

@mallachari mallachari requested review from a team as code owners November 19, 2024 16:54
@changeset-bot
Copy link

changeset-bot bot commented Nov 19, 2024

🦋 Changeset detected

Latest commit: 2381283

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@redocly/cli Patch
@redocly/openapi-core Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Contributor

github-actions bot commented Nov 19, 2024

Command Mean [ms] Min [ms] Max [ms] Relative
redocly lint packages/core/src/benchmark/benches/rebilly.yaml 966.8 ± 11.1 947.4 983.6 1.00
redocly-next lint packages/core/src/benchmark/benches/rebilly.yaml 971.1 ± 13.5 953.7 996.1 1.00 ± 0.02

@github-actions
Copy link
Contributor

github-actions bot commented Nov 19, 2024

Coverage report

St.
Category Percentage Covered / Total
🟡 Statements 78.62% 5039/6409
🟡 Branches 67.26% 2052/3051
🟡 Functions 73.16% 834/1140
🟡 Lines 78.89% 4751/6022
Show new covered files 🐣
St.
File Statements Branches Functions Lines
🟡
... / platform.ts
78.57% 25% 75% 75%

Test suite run success

832 tests passing in 121 suites.

Report generated by 🧪jest coverage report action from 2381283

@mallachari mallachari marked this pull request as draft November 19, 2024 17:04
@mallachari
Copy link
Contributor Author

Converting back to draft as I'd like to add some input sanitization

@mallachari mallachari requested a review from tatomyr November 21, 2024 13:58
@mallachari mallachari marked this pull request as ready for review November 21, 2024 13:59
Copy link
Collaborator

@tatomyr tatomyr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left one comment.
Regarding the test failure -- the coverage just doesn't meet the threshold. Either add more tests or lower the threshold.

@mallachari mallachari requested a review from tatomyr December 2, 2024 15:44
return input.replace(/[^a-zA-Z0-9@._-]/g, '');
};

export function getPlatformArgs(argv: { path?: string; locale?: string; 'project-dir'?: string }) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These properties are specific to individual commands, whilst utils are meant to be context-agnostic. Could you move them out to make the getter more general? I’d recommend returning sanitizers instead, so they can be used directly in the command handlers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made it more agnostic by returning generic sanitize function.

npxExecutableName,
['-y', '@redocly/realm', 'translate', argv.locale, `-d=${argv['project-dir']}`],
{ stdio: 'inherit' }
['-y', '@redocly/realm', 'translate', locale ?? '', `-d=${projectDir}`],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you move the ?? '' into the sanitizer itself? For me it looks like it makes sense to transform all empty arguments into empty spaces.

Comment on lines 1 to 5
export const sanitizePath = (input: string): string => {
return input.replace(/[^a-zA-Z0-9 ._\-:\\/@]/g, '');
};

export const sanitizeLocale = (input: string): string => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you leave a comment with an explanation regarding what is being sanitized? Later, it could be hard to figure out the difference between the sanitizers 😄
Or perhaps you could come up with more descriptive names?

@mallachari mallachari requested a review from tatomyr December 6, 2024 12:04
Copy link
Collaborator

@tatomyr tatomyr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for your input!

@mallachari mallachari merged commit b151753 into main Dec 6, 2024
40 checks passed
@mallachari mallachari deleted the fix/windows-failing-cli branch December 6, 2024 16:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants