From 6aa11aaa501bac54e4200b046cac7c39f1fd28f3 Mon Sep 17 00:00:00 2001 From: David Svoboda Date: Thu, 8 Jan 2026 09:59:26 -0500 Subject: [PATCH 1/5] Add guideline: Prevent OS Command Injection --- .../gui_J3K3ZqC8qoOn.rst.inc | 101 ++++++++++++++++++ .../index.rst | 1 + 2 files changed, 102 insertions(+) create mode 100644 src/coding-guidelines/program-structure-and-compilation/gui_J3K3ZqC8qoOn.rst.inc diff --git a/src/coding-guidelines/program-structure-and-compilation/gui_J3K3ZqC8qoOn.rst.inc b/src/coding-guidelines/program-structure-and-compilation/gui_J3K3ZqC8qoOn.rst.inc new file mode 100644 index 00000000..94dc0480 --- /dev/null +++ b/src/coding-guidelines/program-structure-and-compilation/gui_J3K3ZqC8qoOn.rst.inc @@ -0,0 +1,101 @@ +.. SPDX-License-Identifier: MIT OR Apache-2.0 + SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors + +.. guideline:: Prevent OS Command Injection + :id: gui_J3K3ZqC8qoOn + :category: mandatory + :status: draft + :release: 1.0.0-latest + :fls: fls_hdwwrsyunir + :decidability: undecidable + :scope: module + :tags: injection,sanitization + + Commands that are passed to an external OS command interpreter, like ``std::process::Command``, should not allow untrusted input to be parsed as part of the command syntax. + + Instead, an untrusted input should be passed as a single argument. + + .. rationale:: + :id: rat_aOGrRsi4ASsV + :status: draft + + When preparing a command to be executed by the operating system, untrusted input should be sanitized to make sure it does not alter the syntax of the command to be executed. The easiest way to do this is to avoid string concatenation or formatting (a la ``format!()``), and provide the untrusted data as a lone argument. + + .. non_compliant_example:: + :id: non_compl_ex_p7ZC43gDSy0u + :status: draft + + The following code lists the contents the directory provided in the ``dir`` variable.. However, since this variable is untrusted, a ``dir`` such as ``dummy && echo BOO`` will cause the command to be executed. Thus, the program prints “BOO”. + + .. rust-example:: + + use std::process::{Command, Output}; + use std::io; + + fn files(dir: &str) -> io::Result { + return Command::new("sh") + .arg("-c") + .arg(format!("ls {dir}")) + .output(); + } + + fn main() { + let _ = files("dummy & echo BOO"); // Program prints "BOO" + } + + + .. compliant_example:: + :id: compl_ex_pzNJ0Uc5pTtX + :status: draft + + An untrusted input should be passed as a single argument. This prevents any spaces or other shell punctuation in the input from being misinterpreted by the OS command interpreter. + + .. rust-example:: + + use std::process::{Command, Output}; + use std::io; + + fn files(dir: &str) -> io::Result { + return Command::new("ls") + .arg(dir) + .output(); + } + + fn main() { + let _ = files("dummy & echo BOO"); // Command is invalid, but does not print BOO + } + + + .. compliant_example:: + :id: compl_ex_Eb86EFTC52lj + :status: draft + + A better approach is to avoid OS commands and use a specific API (in this case ``fs::read_dir()``) to achieve the desired result. + + .. rust-example:: + + use std::fs; + use std::io; + + fn files(dir: &str) -> io::Result> { + return fs::read_dir(dir)? + .map(|res| res.map(|e| e.file_name())) + .collect(); + } + + fn main() { + let _ = files("dummy & echo BOO"); // Command is invalid, but does not print BOO + } + + + .. bibliography:: + :id: bib_sGT6VkRzri7W + :status: draft + + .. list-table:: + :header-rows: 0 + :widths: auto + :class: bibliography-table + + * - :bibentry:`gui_J3K3ZqC8qoOn:CERT-J-IDS07` + - SEI CERT Java. "IDS07-J. Sanitize untrusted data passed to the Runtime.exec() method." https://wiki.sei.cmu.edu/confluence/x/xTdGBQ diff --git a/src/coding-guidelines/program-structure-and-compilation/index.rst b/src/coding-guidelines/program-structure-and-compilation/index.rst index 047de02f..7ee71e7d 100644 --- a/src/coding-guidelines/program-structure-and-compilation/index.rst +++ b/src/coding-guidelines/program-structure-and-compilation/index.rst @@ -6,3 +6,4 @@ Program Structure And Compilation ================================= +.. include:: gui_J3K3ZqC8qoOn.rst.inc From 5b419845f563eb2ad20e5549b6614d441a6133fa Mon Sep 17 00:00:00 2001 From: David Svoboda Date: Mon, 12 Jan 2026 11:03:29 -0500 Subject: [PATCH 2/5] review feedback --- .../gui_J3K3ZqC8qoOn.rst.inc | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/coding-guidelines/program-structure-and-compilation/gui_J3K3ZqC8qoOn.rst.inc b/src/coding-guidelines/program-structure-and-compilation/gui_J3K3ZqC8qoOn.rst.inc index 94dc0480..1436a5c8 100644 --- a/src/coding-guidelines/program-structure-and-compilation/gui_J3K3ZqC8qoOn.rst.inc +++ b/src/coding-guidelines/program-structure-and-compilation/gui_J3K3ZqC8qoOn.rst.inc @@ -2,7 +2,7 @@ SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors .. guideline:: Prevent OS Command Injection - :id: gui_J3K3ZqC8qoOn + :id: gui_WFZBA2S0kZtm :category: mandatory :status: draft :release: 1.0.0-latest @@ -16,13 +16,15 @@ Instead, an untrusted input should be passed as a single argument. .. rationale:: - :id: rat_aOGrRsi4ASsV + :id: rat_yWW4m41lE5iD :status: draft - When preparing a command to be executed by the operating system, untrusted input should be sanitized to make sure it does not alter the syntax of the command to be executed. The easiest way to do this is to avoid string concatenation or formatting (a la ``format!()``), and provide the untrusted data as a lone argument. + When preparing a command to be executed by the operating system, untrusted input should be sanitized to make sure it does not alter the syntax of the command to be executed. The easiest way to do this is to avoid mixing untrusted data with trusted data via concatenation or or formatting (a la ``format!()``), Instead provide the untrusted data as a lone argument. The Command::new() constructor makes this easy by accepting the pre-tokenized arguments as a list of strings. + + Traditionally untrusted data should be one argument (aka command-line token). OS command injection occurs when a malicious data fools the command tokenizer into interpreting it as multiple arguments, or even multiple commands. Complexity in the command tokenizer can exacerbate this problem, leading to vulnerabilities such as :cite:`gui_WFZBA2S0kZtm:CVE-2024-24576`. See :cite:`gui_WFZBA2S0kZtm:RUST-WIN-ARG-SPLIT` and :cite:`gui_WFZBA2S0kZtm:SEI-BATBADBUT` for more information. .. non_compliant_example:: - :id: non_compl_ex_p7ZC43gDSy0u + :id: non_compl_ex_qetQ1ceOey6f :status: draft The following code lists the contents the directory provided in the ``dir`` variable.. However, since this variable is untrusted, a ``dir`` such as ``dummy && echo BOO`` will cause the command to be executed. Thus, the program prints “BOO”. @@ -40,12 +42,14 @@ } fn main() { - let _ = files("dummy & echo BOO"); // Program prints "BOO" + if cfg!(unix) { + let _ = files("dummy & echo BOO"); // Program prints "BOO" + } } .. compliant_example:: - :id: compl_ex_pzNJ0Uc5pTtX + :id: compl_ex_xE9vKlt36VJ6 :status: draft An untrusted input should be passed as a single argument. This prevents any spaces or other shell punctuation in the input from being misinterpreted by the OS command interpreter. @@ -62,12 +66,14 @@ } fn main() { - let _ = files("dummy & echo BOO"); // Command is invalid, but does not print BOO + if cfg!(unix) { + let _ = files("dummy & echo BOO"); // Command is invalid, but does not print BOO + } } .. compliant_example:: - :id: compl_ex_Eb86EFTC52lj + :id: compl_ex_2HRkYN00oj9f :status: draft A better approach is to avoid OS commands and use a specific API (in this case ``fs::read_dir()``) to achieve the desired result. @@ -84,12 +90,14 @@ } fn main() { - let _ = files("dummy & echo BOO"); // Command is invalid, but does not print BOO + if cfg!(unix) { + let _ = files("dummy & echo BOO"); // Command is invalid, but does not print BOO + } } .. bibliography:: - :id: bib_sGT6VkRzri7W + :id: bib_tChNunO9ciob :status: draft .. list-table:: @@ -97,5 +105,11 @@ :widths: auto :class: bibliography-table - * - :bibentry:`gui_J3K3ZqC8qoOn:CERT-J-IDS07` + * - :bibentry:`gui_WFZBA2S0kZtm:CERT-J-IDS07` - SEI CERT Java. "IDS07-J. Sanitize untrusted data passed to the Runtime.exec() method." https://wiki.sei.cmu.edu/confluence/x/xTdGBQ + * - :bibentry:`gui_WFZBA2S0kZtm:RUST-WIN-ARG-SPLIT` + - Module process. "Windows Argument Splitting." https://doc.rust-lang.org/std/process/index.html#windows-argument-splitting + * - :bibentry:`gui_WFZBA2S0kZtm:SEI-BATBADBUT` + - SEI Blog. "What Recent Vulnerabilities Mean to Rust | “BatBadBut” Command Injection with Windows’ cmd.exe (CVE-2024-24576)." https://www.sei.cmu.edu/blog/what-recent-vulnerabilities-mean-to-rust/ + * - :bibentry:`gui_WFZBA2S0kZtm:CVE-2024-24576` + - MITRE. "CVE-2024-24576." https://nvd.nist.gov/vuln/detail/CVE-2024-24576 From b338384384b7584f0d6fb6feaccd0e7239a04009 Mon Sep 17 00:00:00 2001 From: David Svoboda Date: Tue, 13 Jan 2026 15:13:39 -0500 Subject: [PATCH 3/5] more feedback tweaks --- .../gui_J3K3ZqC8qoOn.rst.inc | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/coding-guidelines/program-structure-and-compilation/gui_J3K3ZqC8qoOn.rst.inc b/src/coding-guidelines/program-structure-and-compilation/gui_J3K3ZqC8qoOn.rst.inc index 1436a5c8..4dd1c954 100644 --- a/src/coding-guidelines/program-structure-and-compilation/gui_J3K3ZqC8qoOn.rst.inc +++ b/src/coding-guidelines/program-structure-and-compilation/gui_J3K3ZqC8qoOn.rst.inc @@ -2,7 +2,7 @@ SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors .. guideline:: Prevent OS Command Injection - :id: gui_WFZBA2S0kZtm + :id: gui_wecXqgaj3xgj :category: mandatory :status: draft :release: 1.0.0-latest @@ -16,18 +16,18 @@ Instead, an untrusted input should be passed as a single argument. .. rationale:: - :id: rat_yWW4m41lE5iD + :id: rat_pfOjnJc5Don3 :status: draft - When preparing a command to be executed by the operating system, untrusted input should be sanitized to make sure it does not alter the syntax of the command to be executed. The easiest way to do this is to avoid mixing untrusted data with trusted data via concatenation or or formatting (a la ``format!()``), Instead provide the untrusted data as a lone argument. The Command::new() constructor makes this easy by accepting the pre-tokenized arguments as a list of strings. + When preparing a command to be executed by the operating system, untrusted input should be sanitized to make sure it does not alter the syntax of the command to be executed. For commands that do not tokenize their arguments, such as ``sh``, the easiest way to do this is to avoid mixing untrusted data with trusted data via concatenation or formatting (a la ``format!()``). Instead provide the untrusted data as a lone argument. The ``Command::new()`` constructor makes this easy by accepting the pre-tokenized arguments as a list of strings. - Traditionally untrusted data should be one argument (aka command-line token). OS command injection occurs when a malicious data fools the command tokenizer into interpreting it as multiple arguments, or even multiple commands. Complexity in the command tokenizer can exacerbate this problem, leading to vulnerabilities such as :cite:`gui_WFZBA2S0kZtm:CVE-2024-24576`. See :cite:`gui_WFZBA2S0kZtm:RUST-WIN-ARG-SPLIT` and :cite:`gui_WFZBA2S0kZtm:SEI-BATBADBUT` for more information. + Traditionally untrusted data should be one argument (aka command-line token). OS command injection occurs when a malicious data fools the command tokenizer into interpreting it as multiple arguments, or even multiple commands. Complexity in the command tokenizer can exacerbate this problem, leading to vulnerabilities such as :cite:`gui_wecXqgaj3xgj:CVE-2024-24576`. See :cite:`gui_wecXqgaj3xgj:RUST-WIN-ARG-SPLIT` and :cite:`gui_wecXqgaj3xgj:SEI-BATBADBUT` for more information. .. non_compliant_example:: - :id: non_compl_ex_qetQ1ceOey6f + :id: non_compl_ex_EDPSvnIyDgMO :status: draft - The following code lists the contents the directory provided in the ``dir`` variable.. However, since this variable is untrusted, a ``dir`` such as ``dummy && echo BOO`` will cause the command to be executed. Thus, the program prints “BOO”. + The following code lists the contents the directory provided in the ``dir`` variable. However, since this variable is untrusted, a ``dir`` such as ``dummy | echo BOO`` will cause the command to be executed. Thus, the program prints “BOO”. .. rust-example:: @@ -43,13 +43,13 @@ fn main() { if cfg!(unix) { - let _ = files("dummy & echo BOO"); // Program prints "BOO" + let _ = files("dummy | echo BOO"); // Program prints "BOO" } } .. compliant_example:: - :id: compl_ex_xE9vKlt36VJ6 + :id: compl_ex_Gi1zmSEMepG2 :status: draft An untrusted input should be passed as a single argument. This prevents any spaces or other shell punctuation in the input from being misinterpreted by the OS command interpreter. @@ -67,13 +67,13 @@ fn main() { if cfg!(unix) { - let _ = files("dummy & echo BOO"); // Command is invalid, but does not print BOO + let _ = files("dummy | echo BOO"); // Command is invalid, but does not print BOO } } .. compliant_example:: - :id: compl_ex_2HRkYN00oj9f + :id: compl_ex_oIfhLciG9rsK :status: draft A better approach is to avoid OS commands and use a specific API (in this case ``fs::read_dir()``) to achieve the desired result. @@ -91,13 +91,13 @@ fn main() { if cfg!(unix) { - let _ = files("dummy & echo BOO"); // Command is invalid, but does not print BOO + let _ = files("dummy | echo BOO"); // Command is invalid, but does not print BOO } } .. bibliography:: - :id: bib_tChNunO9ciob + :id: bib_G7HkGiWvGowP :status: draft .. list-table:: @@ -105,11 +105,11 @@ :widths: auto :class: bibliography-table - * - :bibentry:`gui_WFZBA2S0kZtm:CERT-J-IDS07` + * - :bibentry:`gui_wecXqgaj3xgj:CERT-J-IDS07` - SEI CERT Java. "IDS07-J. Sanitize untrusted data passed to the Runtime.exec() method." https://wiki.sei.cmu.edu/confluence/x/xTdGBQ - * - :bibentry:`gui_WFZBA2S0kZtm:RUST-WIN-ARG-SPLIT` + * - :bibentry:`gui_wecXqgaj3xgj:RUST-WIN-ARG-SPLIT` - Module process. "Windows Argument Splitting." https://doc.rust-lang.org/std/process/index.html#windows-argument-splitting - * - :bibentry:`gui_WFZBA2S0kZtm:SEI-BATBADBUT` + * - :bibentry:`gui_wecXqgaj3xgj:SEI-BATBADBUT` - SEI Blog. "What Recent Vulnerabilities Mean to Rust | “BatBadBut” Command Injection with Windows’ cmd.exe (CVE-2024-24576)." https://www.sei.cmu.edu/blog/what-recent-vulnerabilities-mean-to-rust/ - * - :bibentry:`gui_WFZBA2S0kZtm:CVE-2024-24576` + * - :bibentry:`gui_wecXqgaj3xgj:CVE-2024-24576` - MITRE. "CVE-2024-24576." https://nvd.nist.gov/vuln/detail/CVE-2024-24576 From 2c0c53b71ae6023e8b34933882e885d22254626a Mon Sep 17 00:00:00 2001 From: David Svoboda Date: Tue, 13 Jan 2026 16:06:32 -0500 Subject: [PATCH 4/5] New tags --- src/conf.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/conf.py b/src/conf.py index d8aabf13..5bd276ff 100644 --- a/src/conf.py +++ b/src/conf.py @@ -134,6 +134,9 @@ dict(name="defect", description="Guideline associated with the defect-prevention profile"), dict(name="unsafe", description="Guidelines that interact with or involve the unsafe keyword"), + + dict(name="injection", description="Guidelines about various kinds of injections"), + dict(name="sanitization", description="Guidelines about sanitizing untrusted input"), ] needs_categories = [ From bcac33db9d5b0f4411bde7f70207bbc99c68415c Mon Sep 17 00:00:00 2001 From: David Svoboda Date: Tue, 13 Jan 2026 16:35:02 -0500 Subject: [PATCH 5/5] Cite IDS07-J --- .../gui_J3K3ZqC8qoOn.rst.inc | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/coding-guidelines/program-structure-and-compilation/gui_J3K3ZqC8qoOn.rst.inc b/src/coding-guidelines/program-structure-and-compilation/gui_J3K3ZqC8qoOn.rst.inc index 4dd1c954..ecfa754f 100644 --- a/src/coding-guidelines/program-structure-and-compilation/gui_J3K3ZqC8qoOn.rst.inc +++ b/src/coding-guidelines/program-structure-and-compilation/gui_J3K3ZqC8qoOn.rst.inc @@ -2,7 +2,7 @@ SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors .. guideline:: Prevent OS Command Injection - :id: gui_wecXqgaj3xgj + :id: gui_a3PpM90Fppwh :category: mandatory :status: draft :release: 1.0.0-latest @@ -16,15 +16,17 @@ Instead, an untrusted input should be passed as a single argument. .. rationale:: - :id: rat_pfOjnJc5Don3 + :id: rat_IaAZISFOmAt0 :status: draft + This rule was inspired by :cite:`gui_a3PpM90Fppwh:CERT-J-IDS07`. + When preparing a command to be executed by the operating system, untrusted input should be sanitized to make sure it does not alter the syntax of the command to be executed. For commands that do not tokenize their arguments, such as ``sh``, the easiest way to do this is to avoid mixing untrusted data with trusted data via concatenation or formatting (a la ``format!()``). Instead provide the untrusted data as a lone argument. The ``Command::new()`` constructor makes this easy by accepting the pre-tokenized arguments as a list of strings. - Traditionally untrusted data should be one argument (aka command-line token). OS command injection occurs when a malicious data fools the command tokenizer into interpreting it as multiple arguments, or even multiple commands. Complexity in the command tokenizer can exacerbate this problem, leading to vulnerabilities such as :cite:`gui_wecXqgaj3xgj:CVE-2024-24576`. See :cite:`gui_wecXqgaj3xgj:RUST-WIN-ARG-SPLIT` and :cite:`gui_wecXqgaj3xgj:SEI-BATBADBUT` for more information. + Traditionally untrusted data should be one argument (aka command-line token). OS command injection occurs when a malicious data fools the command tokenizer into interpreting it as multiple arguments, or even multiple commands. Complexity in the command tokenizer can exacerbate this problem, leading to vulnerabilities such as :cite:`gui_a3PpM90Fppwh:CVE-2024-24576`. See :cite:`gui_a3PpM90Fppwh:RUST-WIN-ARG-SPLIT` and :cite:`gui_a3PpM90Fppwh:SEI-BATBADBUT` for more information. .. non_compliant_example:: - :id: non_compl_ex_EDPSvnIyDgMO + :id: non_compl_ex_Owe2nVInv90z :status: draft The following code lists the contents the directory provided in the ``dir`` variable. However, since this variable is untrusted, a ``dir`` such as ``dummy | echo BOO`` will cause the command to be executed. Thus, the program prints “BOO”. @@ -49,7 +51,7 @@ .. compliant_example:: - :id: compl_ex_Gi1zmSEMepG2 + :id: compl_ex_rJeLKhdopITN :status: draft An untrusted input should be passed as a single argument. This prevents any spaces or other shell punctuation in the input from being misinterpreted by the OS command interpreter. @@ -73,7 +75,7 @@ .. compliant_example:: - :id: compl_ex_oIfhLciG9rsK + :id: compl_ex_BSjAFOLfL4Rk :status: draft A better approach is to avoid OS commands and use a specific API (in this case ``fs::read_dir()``) to achieve the desired result. @@ -97,7 +99,7 @@ .. bibliography:: - :id: bib_G7HkGiWvGowP + :id: bib_CNrst9CcDVQJ :status: draft .. list-table:: @@ -105,11 +107,11 @@ :widths: auto :class: bibliography-table - * - :bibentry:`gui_wecXqgaj3xgj:CERT-J-IDS07` + * - :bibentry:`gui_a3PpM90Fppwh:CERT-J-IDS07` - SEI CERT Java. "IDS07-J. Sanitize untrusted data passed to the Runtime.exec() method." https://wiki.sei.cmu.edu/confluence/x/xTdGBQ - * - :bibentry:`gui_wecXqgaj3xgj:RUST-WIN-ARG-SPLIT` + * - :bibentry:`gui_a3PpM90Fppwh:RUST-WIN-ARG-SPLIT` - Module process. "Windows Argument Splitting." https://doc.rust-lang.org/std/process/index.html#windows-argument-splitting - * - :bibentry:`gui_wecXqgaj3xgj:SEI-BATBADBUT` + * - :bibentry:`gui_a3PpM90Fppwh:SEI-BATBADBUT` - SEI Blog. "What Recent Vulnerabilities Mean to Rust | “BatBadBut” Command Injection with Windows’ cmd.exe (CVE-2024-24576)." https://www.sei.cmu.edu/blog/what-recent-vulnerabilities-mean-to-rust/ - * - :bibentry:`gui_wecXqgaj3xgj:CVE-2024-24576` + * - :bibentry:`gui_a3PpM90Fppwh:CVE-2024-24576` - MITRE. "CVE-2024-24576." https://nvd.nist.gov/vuln/detail/CVE-2024-24576