Skip to content
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

src: add NODE_SECURITY_REVERT environment variable #52365

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

tniessen
Copy link
Member

@tniessen tniessen commented Apr 4, 2024

Some vendors do not allow passing custom command-line flags to the node executable. There are concerns around allowing --security-revert in NODE_OPTIONS because it might be inherited by child processes unintentionally.

This patch introduces a new environment variable that, if set, is unset immediately unless it ends with "+sticky". Aside from that optional suffix, its value is a comma-separated list of CVE identifiers for which the respective security patches should be reverted.

This is not a particularly elegant approach, but since this should only be used under exceptional circumstances, I am not too worried about that.

Closes: #52017


@nodejs-github-bot
Copy link
Collaborator

nodejs-github-bot commented Apr 4, 2024

Review requested:

  • @nodejs/startup
  • @nodejs/security-wg
  • @nodejs/security

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. needs-ci PRs that need a full CI run. labels Apr 4, 2024
@tniessen tniessen added the security Issues and PRs related to security. label Apr 4, 2024
@RafaelGSS
Copy link
Member

Some context in #52090 (comment).

@GeoffreyBooth
Copy link
Member

cc @anonrig

If this can’t be defined in a .env file that’s passed to --env-file, I think the documentation for --env-file needs to mention that (or you allow it to be defined in a file). I think unless there’s a security reason to disallow specifying this in a .env file, we should allow it there.

@mhdawson
Copy link
Member

mhdawson commented Apr 4, 2024

I know we don't have any testing at this point, but I think definining a "dummy" security revert that can be used for tests could make sense.

src/node.cc Outdated Show resolved Hide resolved
src/node.cc Outdated
// Ignore the environment variable if the CLI argument was set.
if (per_process::reverted_cve == 0) {
std::string revert_error;
for (const std::string_view& cve : SplitString(security_revert, ",")) {
Copy link
Member

Choose a reason for hiding this comment

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

Nit:

Suggested change
for (const std::string_view& cve : SplitString(security_revert, ",")) {
for (const auto& cve : SplitString(security_revert, ",")) {

src/node.cc Outdated Show resolved Hide resolved
src/node.cc Outdated Show resolved Hide resolved
src/node.cc Outdated Show resolved Hide resolved
@tniessen
Copy link
Member Author

tniessen commented Apr 5, 2024

@anonrig Thank you for reviewing the implementation, I appreciate it. I admittedly threw this together during the collab summit rather quickly to discuss the idea, and if folks think this is the right approach, I'll be happy to fix up the implementation and mark it as ready for review :)

@GeoffreyBooth I am leaning towards allowing it in dotenv files, but I wasn't quite sure where to put this code in that case. I was hoping to keep it close to the code that currently implements --security-revert and ideally want to apply it before we initialize any V8 isolate. I admittedly also don't fully understand the flow of how dotenv files are loaded and didn't find much documentation or comments — it seems to me that NODE_OPTIONS is handled specially and the rest of the environment is populated later. Maybe @anonrig has a tip for keeping this close to the handling of --security-revert while still using dotenv properly :)

@tniessen tniessen marked this pull request as ready for review April 6, 2024 21:41
@tniessen
Copy link
Member Author

tniessen commented Apr 6, 2024

@GeoffreyBooth I added support for setting the variable through dotenv files.

@mhdawson I added a test case.

Copy link
Member

@RafaelGSS RafaelGSS left a comment

Choose a reason for hiding this comment

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

LGTM

@RafaelGSS RafaelGSS added the request-ci Add this label to start a Jenkins CI on a PR. label Apr 7, 2024
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Apr 7, 2024
@nodejs-github-bot

This comment was marked as outdated.

@tniessen tniessen force-pushed the env-security-revert branch 2 times, most recently from 1279a29 to 06bf754 Compare April 8, 2024 11:21
@tniessen
Copy link
Member Author

tniessen commented Apr 8, 2024

Added more tests to cover precedence rules.

@tniessen tniessen requested a review from jasnell April 8, 2024 14:38

{
// Ignore the environment variable if the CLI argument was set.
Mutex::ScopedLock lock(per_process::cli_options_mutex);
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we need to make them exclusive. I think it is already possible to revert a CVE twice from the command line so not sure we need to only allow one or the other?

Copy link
Member

@mhdawson mhdawson left a comment

Choose a reason for hiding this comment

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

LGTM

@anonrig anonrig added the request-ci Add this label to start a Jenkins CI on a PR. label Apr 16, 2024
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Apr 16, 2024
@nodejs-github-bot

This comment was marked as off-topic.

@tniessen
Copy link
Member Author

The conflict should be resolved by #52543.

@RafaelGSS
Copy link
Member

Can you rebase @tniessen?

@RafaelGSS RafaelGSS added the semver-minor PRs that contain new features and should be released in the next minor version. label Apr 18, 2024
Some vendors do not allow passing custom command-line flags to the node
executable. There are concerns around allowing --security-revert in
NODE_OPTIONS because it might be inherited by child processes
unintentionally.

This patch introduces a new environment variable that, if set, is unset
immediately unless it ends with "+sticky". Aside from that optional
suffix, its value is a comma-separated list of CVE identifiers for which
the respective security patches should be reverted.

Closes: nodejs#52017
@RafaelGSS RafaelGSS added the request-ci Add this label to start a Jenkins CI on a PR. label Apr 19, 2024
@RafaelGSS RafaelGSS added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Apr 19, 2024
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Apr 19, 2024
@nodejs-github-bot
Copy link
Collaborator

Copy link
Member

@mhdawson mhdawson left a comment

Choose a reason for hiding this comment

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

Requesting changes until we resolve the coversation happening in #52090

@aduh95 aduh95 removed the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label May 12, 2024
@lwr
Copy link

lwr commented Aug 29, 2024

It is big problem #52554 which is locked so we can only discuss here.

yes we know we can use shell: true instead of execFile, the question is, how can we process (escape) the input args instead? there is no any simple way to do this?

or else SHOULD WE have to translate this c routine into javascript?

WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target) {

or else following this article? https://learn.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way

@tniessen
Copy link
Member Author

the question is, how can we process (escape) the input args instead?

FWIW, unlike any other operating system, Windows does not have a singular escaping mechanism for command-line arguments. Each executable potentially parses them differently. You can find a summary in golang/go#1849:

Microsoft's C startup code defines a standard way for C programs to split the command line into separate arguments and go's syscall.StartProcess does the correct escaping for those cases.

However, some programs - and most notably the cmd.exe shell - do their own command line parameter parsing.

In other words, there is no single correct escaping algorithm for executing arbitrary child processes on Windows. libuv internally follows the default behavior of C applications that have been compiled using MSVC (essentially reverse-engineering CommandLineToArgvW()), but this behavior is not correct for cmd.exe (and thus not for any .bat or .cmd file). There are also plenty of Windows applications that use GetCommandLineW() directly, and who knows what escaping rules they apply.

or else following this article? learn.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way

That looks mostly correct, except it will also fail when some executable does not follow the conventions of CommandLineToArgvW(). The only correct solution is to know the escaping rules of every executable that you are going to execute, including the shell itself, and to then apply these escaping rules to arguments before invoking the shell with the escaped arguments.

Unfortunately, this is a fundamental design mistake in Windows, and there is not much that Node.js can do about it.

@aleen42
Copy link

aleen42 commented Sep 12, 2024

According to https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2

Due to the improper handling of batch files in child_process.spawn / child_process.spawnSync, a malicious command line argument can inject arbitrary commands and achieve code execution even if the shell option is not enabled. ...

Why not solve the problem of the "improper handling"? Is it so complicated that a broken behaviour is the only solution?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. needs-ci PRs that need a full CI run. security Issues and PRs related to security. semver-minor PRs that contain new features and should be released in the next minor version.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Enable --security-revert to be used in NODE_OPTIONS environment variable
9 participants