From cb4be6fd68152991dfa17b9be4c32964d1a75e93 Mon Sep 17 00:00:00 2001 From: sxm588 Date: Tue, 19 Sep 2023 13:11:44 -0500 Subject: [PATCH 1/4] Rust innersourcing --- .../Insufficient-protected-credentials.yml | 20 ++++++++ .../command-injection-process-builder.yaml | 26 ++++++++++ .../security/audit/crypto/deprecated-ssl.rs | 17 +++++++ .../security/audit/crypto/deprecated-ssl.yaml | 50 +++++++++++++++++++ .../lang/security/audit/crypto/math-random.rs | 15 ++++++ .../security/audit/crypto/math-random.yaml | 30 +++++++++++ .../security/audit/crypto/reversible-hash.rs | 33 ++++++++++++ .../audit/crypto/reversible-hash.yaml | 21 ++++++++ .../audit/crypto/use-of-weak-rsa-key.yaml | 21 ++++++++ .../audit/dangerous-command-injection.yml | 18 +++++++ .../audit/dangerous-exec-command.yaml | 32 ++++++++++++ .../security/audit/dangerous-system-call.yaml | 18 +++++++ .../security/audit/deserialize-untrusted.rs | 46 +++++++++++++++++ .../security/audit/deserialize-untrusted.yaml | 16 ++++++ .../security/audit/exposure-dir-listing.yaml | 31 ++++++++++++ .../audit/exposure-of-private-info.yaml | 44 ++++++++++++++++ .../audit/exposure-via-error-msg.yaml | 29 +++++++++++ .../audit/improper-null-neutralization.yaml | 33 ++++++++++++ .../incorrect-permission-assignment.yaml | 16 ++++++ rust/lang/security/audit/ldap-query.yml | 18 +++++++ .../audit/path/path-injection-cli-arg.yaml | 35 +++++++++++++ .../audit/path/path-injection-env-var.yaml | 46 +++++++++++++++++ .../audit/sensitive-data-exposure.yml | 22 ++++++++ .../temporary-insecure-file-permissions.yaml | 19 +++++++ .../audit/unprotected-credentials.yaml | 27 ++++++++++ rust/lang/security/audit/xss-templates.yaml | 24 +++++++++ ...roper-neutralization-special-elements.yaml | 20 ++++++++ rust/lang/security/injection/mysql-sqli.yaml | 43 ++++++++++++++++ .../injection/resource-injection.yaml | 29 +++++++++++ rust/lang/security/injection/sql-string.yaml | 32 ++++++++++++ rust/lang/security/injection/sqlx-sqli.yaml | 43 ++++++++++++++++ .../injection/xml-entity-injection.yaml | 33 ++++++++++++ .../security/injection/xpath-injection.yaml | 23 +++++++++ rust/lang/security/net/bind-all.yaml | 17 +++++++ .../security/net/cookie-missing-httponly.yaml | 36 +++++++++++++ .../security/net/cookie-missing-secure.yaml | 34 +++++++++++++ .../security/net/disabled-cert-validation.rs | 29 +++++++++++ .../net/disabled-cert-validation.yaml | 10 ++++ .../security/net/unsecure-transmission.rs | 26 ++++++++++ .../security/net/unsecure-transmission.yaml | 14 ++++++ rust/lang/security/net/xsrf.rs | 34 +++++++++++++ rust/lang/security/net/xsrf.yaml | 33 ++++++++++++ 42 files changed, 1163 insertions(+) create mode 100644 rust/lang/security/audit/Insufficient-protected-credentials.yml create mode 100644 rust/lang/security/audit/command-injection-process-builder.yaml create mode 100644 rust/lang/security/audit/crypto/deprecated-ssl.rs create mode 100644 rust/lang/security/audit/crypto/deprecated-ssl.yaml create mode 100644 rust/lang/security/audit/crypto/math-random.rs create mode 100644 rust/lang/security/audit/crypto/math-random.yaml create mode 100644 rust/lang/security/audit/crypto/reversible-hash.rs create mode 100644 rust/lang/security/audit/crypto/reversible-hash.yaml create mode 100644 rust/lang/security/audit/crypto/use-of-weak-rsa-key.yaml create mode 100644 rust/lang/security/audit/dangerous-command-injection.yml create mode 100644 rust/lang/security/audit/dangerous-exec-command.yaml create mode 100644 rust/lang/security/audit/dangerous-system-call.yaml create mode 100644 rust/lang/security/audit/deserialize-untrusted.rs create mode 100644 rust/lang/security/audit/deserialize-untrusted.yaml create mode 100644 rust/lang/security/audit/exposure-dir-listing.yaml create mode 100644 rust/lang/security/audit/exposure-of-private-info.yaml create mode 100644 rust/lang/security/audit/exposure-via-error-msg.yaml create mode 100644 rust/lang/security/audit/improper-null-neutralization.yaml create mode 100644 rust/lang/security/audit/incorrect-permission-assignment.yaml create mode 100644 rust/lang/security/audit/ldap-query.yml create mode 100644 rust/lang/security/audit/path/path-injection-cli-arg.yaml create mode 100644 rust/lang/security/audit/path/path-injection-env-var.yaml create mode 100644 rust/lang/security/audit/sensitive-data-exposure.yml create mode 100644 rust/lang/security/audit/temporary-insecure-file-permissions.yaml create mode 100644 rust/lang/security/audit/unprotected-credentials.yaml create mode 100644 rust/lang/security/audit/xss-templates.yaml create mode 100644 rust/lang/security/injection/improper-neutralization-special-elements.yaml create mode 100644 rust/lang/security/injection/mysql-sqli.yaml create mode 100644 rust/lang/security/injection/resource-injection.yaml create mode 100644 rust/lang/security/injection/sql-string.yaml create mode 100644 rust/lang/security/injection/sqlx-sqli.yaml create mode 100644 rust/lang/security/injection/xml-entity-injection.yaml create mode 100644 rust/lang/security/injection/xpath-injection.yaml create mode 100644 rust/lang/security/net/bind-all.yaml create mode 100644 rust/lang/security/net/cookie-missing-httponly.yaml create mode 100644 rust/lang/security/net/cookie-missing-secure.yaml create mode 100644 rust/lang/security/net/disabled-cert-validation.rs create mode 100644 rust/lang/security/net/disabled-cert-validation.yaml create mode 100644 rust/lang/security/net/unsecure-transmission.rs create mode 100644 rust/lang/security/net/unsecure-transmission.yaml create mode 100644 rust/lang/security/net/xsrf.rs create mode 100644 rust/lang/security/net/xsrf.yaml diff --git a/rust/lang/security/audit/Insufficient-protected-credentials.yml b/rust/lang/security/audit/Insufficient-protected-credentials.yml new file mode 100644 index 0000000000..639e7e846e --- /dev/null +++ b/rust/lang/security/audit/Insufficient-protected-credentials.yml @@ -0,0 +1,20 @@ +rules: + - id: insufficient-protected-credentials + message: | + This is a Insufficiently Protected Credentials weakness: https://cwe.mitre.org/data/definitions/522.html + Consider using an appropriate security mechanism to protect the credentials (e.g. keeping secrets in environment variables) + metadata: + cwe: 'CWE-522: Insufficiently Protected Credentials' + owasp: 'A2: Broken Authentication' + source-rule-url: https://r2c.dev/blog/2020/hardcoded-secrets-unverified-tokens-and-other-common-jwt-mistakes/ + pattern-either: + - patterns: + - pattern: | + fn $FUNC(...) { + requests::$METHOD(...); + ... + Connection::connect(...); + } + + languages: [rust] + severity: WARNING diff --git a/rust/lang/security/audit/command-injection-process-builder.yaml b/rust/lang/security/audit/command-injection-process-builder.yaml new file mode 100644 index 0000000000..6132e90f67 --- /dev/null +++ b/rust/lang/security/audit/command-injection-process-builder.yaml @@ -0,0 +1,26 @@ +rules: + - id: dangerous-spawn-process + message: | + Found dynamic content when spawning a process. This is dangerous if external + data can reach this function call because it allows a malicious actor to + execute commands. Ensure no external data reaches here. + metadata: + cwe: "CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')" + owasp: 'A1: Injection' + asvs: + section: 'V5: Validation, Sanitization and Encoding Verification Requirements' + control_id: 5.3.8 OS Command Injection + control_url: https://github.com/OWASP/ASVS/blob/master/4.0/en/0x13-V5-Validation-Sanitization-Encoding.md#v53-output-encoding-and-injection-prevention-requirements + version: '4' + languages: [rust] + severity: WARNING + patterns: + - pattern-either: + - patterns: + - pattern: | + fn $FUNC(...) { + ... + Command::new(...); + ... + } + diff --git a/rust/lang/security/audit/crypto/deprecated-ssl.rs b/rust/lang/security/audit/crypto/deprecated-ssl.rs new file mode 100644 index 0000000000..139ab3ddc2 --- /dev/null +++ b/rust/lang/security/audit/crypto/deprecated-ssl.rs @@ -0,0 +1,17 @@ +fn set_min_version_ssl3() { + let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); + //ruleid: ssl-v3-is-insecure + acceptor.set_min_proto_version(Some(SslVersion::SSL3)); +} + +fn set_min_version_tls1() { + let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); + //ruleid: tls-v1-is-insecure + acceptor.set_min_proto_version(Some(SslVersion::TLS1)); +} + +fn set_min_version_tls1_1() { + let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); + //ruleid: tls-v1_1-is-insecure + acceptor.set_min_proto_version(Some(SslVersion::TLS1_1)); +} diff --git a/rust/lang/security/audit/crypto/deprecated-ssl.yaml b/rust/lang/security/audit/crypto/deprecated-ssl.yaml new file mode 100644 index 0000000000..54854330bf --- /dev/null +++ b/rust/lang/security/audit/crypto/deprecated-ssl.yaml @@ -0,0 +1,50 @@ +rules: + - id: ssl-v3-is-insecure + message: | + SSLv3 is insecure because it has known vulnerabilities. + Instead, use 'SslVersion::TLS1_2 or SslVersion::TLS1_3'. + metadata: + cwe: 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm' + owasp: 'A9: Using Components with Known Vulnerabilities' + references: + - https://golang.org/doc/go1.14#crypto/tls + - https://www.us-cert.gov/ncas/alerts/TA14-290A + languages: [rust] + severity: ERROR + fix-regex: + regex: SslVersion::SSL3 + replacement: SslVersion::TLS1_3 + pattern: $VAR.set_min_proto_version(Some(SslVersion::SSL3)) + + - id: tls-v1-is-insecure + message: | + TLSv1 is insecure because it has known vulnerabilities. + Instead, use 'SslVersion::TLS1_2 or SslVersion::TLS1_3'. + metadata: + cwe: 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm' + owasp: 'A9: Using Components with Known Vulnerabilities' + references: + - https://www.us-cert.gov/ncas/alerts/TA14-290A + languages: [rust] + severity: ERROR + fix-regex: + regex: SslVersion::TLSv1 + replacement: SslVersion::TLS1_3 + pattern: $VAR.set_min_proto_version(Some(SslVersion::TLS1)) + + + - id: tls-v1_1-is-insecure + message: | + TLSv1_1 is insecure because it has known vulnerabilities. + Instead, use 'SslVersion::TLS1_2 or SslVersion::TLS1_3'. + metadata: + cwe: 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm' + owasp: 'A9: Using Components with Known Vulnerabilities' + references: + - https://www.us-cert.gov/ncas/alerts/TA14-290A + languages: [rust] + severity: ERROR + fix-regex: + regex: SslVersion::TLSv1_1 + replacement: SslVersion::TLS1_3 + pattern: $VAR.set_min_proto_version(Some(SslVersion::TLS1_1)) diff --git a/rust/lang/security/audit/crypto/math-random.rs b/rust/lang/security/audit/crypto/math-random.rs new file mode 100644 index 0000000000..59efa0183c --- /dev/null +++ b/rust/lang/security/audit/crypto/math-random.rs @@ -0,0 +1,15 @@ +fn random() { + //ruleid: math-random-used + let x = rand::random(); +} +fn thread_rng() { + //ruleid: math-random-used + let x = rand::thread_rng(); +} + +fn thread_rng_1() { + //todoruleid: math-random-used + use rand::thread_rng; + //todoruleid: math-random-used + let _ = thread_rng(); +} diff --git a/rust/lang/security/audit/crypto/math-random.yaml b/rust/lang/security/audit/crypto/math-random.yaml new file mode 100644 index 0000000000..fdce131783 --- /dev/null +++ b/rust/lang/security/audit/crypto/math-random.yaml @@ -0,0 +1,30 @@ +rules: + - id: math-random-used + metadata: + cwe: 'CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)' + owasp: 'A3: Sensitive Data Exposure' + references: + - https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html#secure-random-number-generation + message: | + The product uses a Pseudo-Random Number Generator (PRNG) in a security context, + but the PRNG's algorithm is not cryptographically strong. + languages: [rust] + severity: WARNING + + pattern-either: + - pattern: rand::thread_rng() + - pattern: rand::random() +# issues with pattern inside +# - pattern: | +# use rand::thread_rng; +# ... + +# - pattern-not-inside: | +# use rand::CryptoRng; +# ... +# - pattern-not-inside: | +# use rand_core::StdRng; +# ... +# - pattern-not-inside: | +# use rand_core::OsRng; +# ... diff --git a/rust/lang/security/audit/crypto/reversible-hash.rs b/rust/lang/security/audit/crypto/reversible-hash.rs new file mode 100644 index 0000000000..48ec762141 --- /dev/null +++ b/rust/lang/security/audit/crypto/reversible-hash.rs @@ -0,0 +1,33 @@ +use openssl::hash::MessageDigest; +use openssl::pkey::PKey; +use openssl::rsa::Rsa; +use openssl::sign::Signer; + +// Use of unsecure sha1 as MessageDigest +fn reversible_hash_1() { + //ruleid: reversible-hash + let msg_digest = MessageDigest::from_name("sha1").unwrap(); + + let keypair = PKey::from_rsa(Rsa::generate(2048).unwrap()).unwrap(); + let signer = Signer::new(msg_digest, &keypair).unwrap(); + let signature = signer.sign_to_vec().unwrap(); + + println!("signature = {:?}", signature); +} + +// Use of unsecure sha1 as MessageDigest +fn reversible_hash_2() { + //ruleid: reversible-hash + let msg_digest = MessageDigest::sha1(); + + let keypair = PKey::from_rsa(Rsa::generate(2048).unwrap()).unwrap(); + let signer = Signer::new(msg_digest, &keypair).unwrap(); + let signature = signer.sign_to_vec().unwrap(); + + println!("signature = {:?}", signature); +} + +fn main() { + reversible_hash_1(); + reversible_hash_2(); +} diff --git a/rust/lang/security/audit/crypto/reversible-hash.yaml b/rust/lang/security/audit/crypto/reversible-hash.yaml new file mode 100644 index 0000000000..c496ba0123 --- /dev/null +++ b/rust/lang/security/audit/crypto/reversible-hash.yaml @@ -0,0 +1,21 @@ +rules: + - id: reversible-hash + message: | + The product uses a hashing algorithm that produces a hash value that can be used + to determine the original input, or to find an input that can produce the same hash, + more efficiently than brute force techniques. This weakness is especially dangerous + when the hash is used in security algorithms that require the one-way property to + hold. For example, if an authentication system takes an incoming password and + generates a hash, then compares the hash to another hash that it has stored in its + authentication database, then the ability to create a collision could allow an attacker + to provide an alternate password that produces the same target hash, bypassing authentication. + languages: [rust] + severity: WARNING + metadata: + owasp: 'A9: Using Components with Known Vulnerabilities' + cwe: 'CWE-328: Reversible One-Way Hash' + pattern-either: + - pattern: | + MessageDigest::from_name("sha1") + - pattern: | + MessageDigest::sha1() diff --git a/rust/lang/security/audit/crypto/use-of-weak-rsa-key.yaml b/rust/lang/security/audit/crypto/use-of-weak-rsa-key.yaml new file mode 100644 index 0000000000..b5b31020c0 --- /dev/null +++ b/rust/lang/security/audit/crypto/use-of-weak-rsa-key.yaml @@ -0,0 +1,21 @@ +rules: + - id: use-of-weak-rsa-key + message: RSA keys should be at least 2048 bits + languages: [rust] + severity: WARNING + metadata: + cwe: 'CWE-326: Inadequate Encryption Strength' + owasp: 'A3: Sensitive Data Exposure' + references: + - https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html#algorithms + patterns: + - pattern-either: + - pattern: | + Rsa::generate($BITS) + - pattern: | + rsa::Rsa::generate($BITS) + - pattern: | + openssl::rsa::Rsa::generate($BITS) + - metavariable-comparison: + metavariable: $BITS + comparison: $BITS < 2048 \ No newline at end of file diff --git a/rust/lang/security/audit/dangerous-command-injection.yml b/rust/lang/security/audit/dangerous-command-injection.yml new file mode 100644 index 0000000000..929b68e05e --- /dev/null +++ b/rust/lang/security/audit/dangerous-command-injection.yml @@ -0,0 +1,18 @@ +rules: + - id: command-injection + patterns: + - pattern-either: + - pattern: | + fn $FUNC(...) { + requests::$METHOD(...); + ... + Command::new(...); + ... + } + message: | + "The software constructs all or part of a command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended command when it is sent to a downstream component." + metadata: + cwe: "CWE-77: Improper Neutralization of Special Elements used in a Command ('Command Injection')" + owasp: 'A1: Injection' + languages: [rust] + severity: WARNING \ No newline at end of file diff --git a/rust/lang/security/audit/dangerous-exec-command.yaml b/rust/lang/security/audit/dangerous-exec-command.yaml new file mode 100644 index 0000000000..8fb64e8525 --- /dev/null +++ b/rust/lang/security/audit/dangerous-exec-command.yaml @@ -0,0 +1,32 @@ +rules: + - id: dangerous-exec-command + patterns: + - pattern-either: + - patterns: + - pattern-either: + - pattern: | + Command::new($CMD) + - patterns: + - pattern-either: + - pattern: | + Command::new("=~/(sh|bash|ksh|csh|tcsh|zsh|ls)/").arg("-c").arg($CMD) + - pattern: | + Command::new("=~/(sh|bash|ksh|csh|tcsh|zsh)/").arg("-c").arg($CMD).output() + - pattern: | + Command::new("=~/(sh|bash|ksh|csh|tcsh|zsh)/").arg("-c").arg($CMD).output().expect(...) + - pattern-inside: | + use std::process::Command; + ... + - pattern-not-inside: | + $CMD = "..."; + ... + message: | + Detected non-static command inside Command. Audit the input to 'Command:new'. + If unverified user data can reach this call site, this is a code injection + vulnerability. A malicious actor can inject a malicious script to execute + arbitrary code. + metadata: + cwe: "CWE-94: Improper Control of Generation of Code ('Code Injection')" + owasp: 'A1: Injection' + severity: WARNING + languages: [rust] \ No newline at end of file diff --git a/rust/lang/security/audit/dangerous-system-call.yaml b/rust/lang/security/audit/dangerous-system-call.yaml new file mode 100644 index 0000000000..e162181b1c --- /dev/null +++ b/rust/lang/security/audit/dangerous-system-call.yaml @@ -0,0 +1,18 @@ +rules: + - id: argument-injection + patterns: + - pattern-either: + - pattern: | + fn $FUNC(...) { + requests::$METHOD(...); + ... + Command::new(...); + ... + } + message: | + The software constructs a string for a command to executed by a separate component in another control sphere, but it does not properly delimit the intended arguments, options, or switches within that command string. + metadata: + cwe: "CWE-88: Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')" + owasp: 'A1: Injection' + languages: [rust] + severity: WARNING diff --git a/rust/lang/security/audit/deserialize-untrusted.rs b/rust/lang/security/audit/deserialize-untrusted.rs new file mode 100644 index 0000000000..bcffd4b4b8 --- /dev/null +++ b/rust/lang/security/audit/deserialize-untrusted.rs @@ -0,0 +1,46 @@ +//id: deserialize-untrusted-data +#[derive(serde::Deserialize)] +struct MyData { + a: u8, + b: String, +} + +fn main() { + //ruleid: deserialize-untrusted-data + let data_from_str: MyData = serde_json::from_str("{invalid: json}").unwrap(); + //ruleid: deserialize-untrusted-data + let data_from_bytes: MyData = serde_json::from_slice("{invalid: bytes}".as_bytes()).unwrap(); + + //ruleid: deserialize-untrusted-data + let data_from_value: u8 = + serde_json::from_value(serde_json::value::Value::Bool(false)).unwrap(); + //ruleid: deserialize-untrusted-data + let data_from_reader: MyData = serde_json::from_reader(std::io::BufReader::new( + std::fs::File::open("/tmp/txtfile.txt").unwrap(), + )) + .unwrap(); +} + +fn safe_deserialization() -> Result<(), serde_json::Error> { + // Use the `?` operator to return an error if the data is invalid + //ok: deserialize-untrusted-data + let data_from_str: MyData = serde_json::from_str("{invalid: json}")?; + + // Use the `match` operator to handle deserialization errors + //ok: deserialize-untrusted-data + let data_from_bytes: MyData = match serde_json::from_slice("newobject".as_bytes()) { + Ok(data) => data, + Err(e) => MyData { + a: 0, + b: "Invalid data supplied, created other object".to_string(), + }, + }; + + // Use an `if let` syntax to perform logic conditionally + //ok: deserialize-untrusted-data + if let Ok(data) = serde_json::from_value(serde_json::value::Value::Bool(true)) { + println!("Data was: {}", data && true); + }; + + Ok(()) +} diff --git a/rust/lang/security/audit/deserialize-untrusted.yaml b/rust/lang/security/audit/deserialize-untrusted.yaml new file mode 100644 index 0000000000..fc485d2096 --- /dev/null +++ b/rust/lang/security/audit/deserialize-untrusted.yaml @@ -0,0 +1,16 @@ +rules: + - id: deserialize-untrusted-data + message: | + Data that is untrusted can not be trusted to be well-formed. + The application deserializes untrusted data without sufficiently verifying that the resulting data will be valid. + metadata: + cwe: "CWE-502: Deserialization of Untrusted Data" + owasp: 'A8: Insecure Deserialization' + languages: [rust] + severity: WARNING + pattern-either: + - pattern: $OBJECT::from_str(...).unwrap(); + - pattern: $OBJECT::from_slice(...).unwrap(); + - pattern: $OBJECT::from_value(...).unwrap(); + - pattern: $OBJECT::from_reader(...).unwrap(); + diff --git a/rust/lang/security/audit/exposure-dir-listing.yaml b/rust/lang/security/audit/exposure-dir-listing.yaml new file mode 100644 index 0000000000..0ee9dbc952 --- /dev/null +++ b/rust/lang/security/audit/exposure-dir-listing.yaml @@ -0,0 +1,31 @@ +rules: + - id: exposure-dir-listing + languages: + - rust + message: | + A directory listing is inappropriately exposed, yielding potentially sensitive + information to attackers. + metadata: + owasp: 'A10: Insecure Configuration Management' + cwe: 'CWE-548: Exposure of Information Through Directory Listing' + severity: WARNING + patterns: + - pattern-either: + # Check for path literals which should have application access blocked + - patterns: + - pattern: $MOD::read_dir($DIR) + - metavariable-pattern: + metavariable: $DIR + patterns: + - pattern-either: + - pattern: '"/System"' + - pattern: '"/usr"' + - pattern: '"/bin"' + - pattern: '"/sbin"' + - pattern: '"/var"' + # Check for impled sensitive file paths via regex + - patterns: + - pattern: $MOD::read_dir($DIR) + - metavariable-regex: + metavariable: $DIR + regex: (.*)(password|account_id|key|secret|social_security_number|national_id|government_id|location)(.*) diff --git a/rust/lang/security/audit/exposure-of-private-info.yaml b/rust/lang/security/audit/exposure-of-private-info.yaml new file mode 100644 index 0000000000..4ae513651d --- /dev/null +++ b/rust/lang/security/audit/exposure-of-private-info.yaml @@ -0,0 +1,44 @@ +rules: + - id: exposure-of-private-info + languages: + - rust + message: | + The product does not properly prevent a person's private, personal information + from being accessed by actors who either (1) are not explicitly authorized to + access the information or (2) do not have the implicit consent of the person about + whom the information is collected. + metadata: + owasp: 'A3: Sensitive Data Exposure' + + cwe: | + - CWE-359: Exposure of Private Personal Information to an Unauthorized Actor + - CWE-532: Insertion of Sensitive Information into Log File + + + severity: WARNING + patterns: + - pattern-either: + - patterns: + - pattern: | + fn $FUNC (...) { + ... + let $NPI = $OBJ.$METHOD(...); + ... + $EMIT(<... $NPI ...>); + } + - pattern: | + fn $FUNC (...) { + ... + let $NPI = $F(...); + ... + $EMIT(<... $NPI ...>); + } + - metavariable-regex: + metavariable: $EMIT + regex: ^(write|foramt|println|trace|debug|info|warn|error|critical)!$ + - metavariable-regex: + metavariable: $METHOD + regex: (.*)(password|account_id|key|secret|social_security_number|national_id|government_id|location)(.*) + - metavariable-regex: + metavariable: $F + regex: (.*)(password|account_id|key|secret|social_security_number|national_id|government_id|location)(.*) diff --git a/rust/lang/security/audit/exposure-via-error-msg.yaml b/rust/lang/security/audit/exposure-via-error-msg.yaml new file mode 100644 index 0000000000..bd48e98be7 --- /dev/null +++ b/rust/lang/security/audit/exposure-via-error-msg.yaml @@ -0,0 +1,29 @@ +rules: + - id: exposure-error-msg + languages: + - rust + message: | + The software generates an error message that includes sensitive information + about its environment, users, or associated data. + metadata: + owasp: 'A3: Sensitive Data Expsoure' + cwe: 'CWE-209: Generation of Error Message Containing Sensitive Information' + severity: WARNING + patterns: + - pattern-either: + - patterns: + - pattern: | + fn $FUNC1(...) -> Result<$T, Box> { + ... + } + + fn $FUNC2(...) { + ... + let $X = $FUNC1(...); + ... + $WRITE(..., $X, ...); + ... + } + - metavariable-regex: + metavariable: $WRITE + regex: ^(println!|debug!|warn!|error!|info!|trace!|format!)$ diff --git a/rust/lang/security/audit/improper-null-neutralization.yaml b/rust/lang/security/audit/improper-null-neutralization.yaml new file mode 100644 index 0000000000..160139e236 --- /dev/null +++ b/rust/lang/security/audit/improper-null-neutralization.yaml @@ -0,0 +1,33 @@ +rules: + - id: improper-null-neutralization + message: | + The software receives input from an upstream component, but it does not neutralize or incorrectly + neutralizes NUL characters or null bytes when they are sent to a downstream component. + metadata: + cwe: | + - "CWE-138: Improper Neutralization of Special Elements" + - "CWE-158: Improper Neutralization of Null Byte or NUL Character" + cwe-url: "https://cwe.mitre.org/data/definitions/158.html" + references: + - https://doc.rust-lang.org/std/ffi/struct.CStr.html + languages: [rust] + severity: WARNING + pattern-either: + - pattern: | + let $CSTR = $OBJECT::from_ptr(...).to_bytes(); + ... + let $STR = $OBJECT::from_bytes_with_nul_unchecked($CSTR); + - pattern: | + let $CSTR = $OBJECT::from_ptr(...).to_bytes(); + ... + $OBJECT::from_bytes_with_nul_unchecked($CSTR); + - pattern: | + let $SLICE = $OBJECT::from_ptr(...); + ... + let $UNKNOWN = $SLICE.to_str().unwrap(); + - pattern: | + let $SLICE = $OBJECT::from_ptr(...); + ... + $SLICE.to_str().unwrap(); + + diff --git a/rust/lang/security/audit/incorrect-permission-assignment.yaml b/rust/lang/security/audit/incorrect-permission-assignment.yaml new file mode 100644 index 0000000000..ed9720bddd --- /dev/null +++ b/rust/lang/security/audit/incorrect-permission-assignment.yaml @@ -0,0 +1,16 @@ +rules: + - id: incorrect-permission-assignment + languages: + - rust + message: The product specifies permissions for a security-critical resource in a way that allows that resource to be read or modified by unintended actors. + metadata: + cwe: | + - "CWE-276: Incorrect Default Permissions" + - "CWE-732: Incorrect Permission Assignment for Critical Resource" + cwe-url: "https://cwe.mitre.org/data/definitions/276.html" + patterns: + - pattern: OpenOptions::new().mode($ARG).open("..."); + - metavariable-regex: + metavariable: $ARG + regex: 0[0-7][0-7](2|6|7) + severity: WARNING diff --git a/rust/lang/security/audit/ldap-query.yml b/rust/lang/security/audit/ldap-query.yml new file mode 100644 index 0000000000..f753e8245b --- /dev/null +++ b/rust/lang/security/audit/ldap-query.yml @@ -0,0 +1,18 @@ +rules: + - id: ldap-injection + patterns: + - pattern-either: + - pattern: | + let $DATA = requests::$METHOD(...).unwrap(); + ... + let mut $LDAP = LdapConn::new(...)?; + ... + let $RESULT = $LDAP.search(...,$DATA,...)?.success()?; + + message: | + "The software constructs all or part of an LDAP query using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended LDAP query when it is sent to a downstream component." + metadata: + cwe: "CWE-90: Improper Neutralization of Special Elements used in an LDAP Query ('LDAP Injection')" + owasp: 'A2: Injection Flaws' + languages: [rust] + severity: WARNING \ No newline at end of file diff --git a/rust/lang/security/audit/path/path-injection-cli-arg.yaml b/rust/lang/security/audit/path/path-injection-cli-arg.yaml new file mode 100644 index 0000000000..fd19386e15 --- /dev/null +++ b/rust/lang/security/audit/path/path-injection-cli-arg.yaml @@ -0,0 +1,35 @@ +rules: + - id: path-injection-cli-arg + patterns: + - pattern-either: + - patterns: + - pattern: | + fn $FUNC(...) { + let $VAR = $F_ENV(...); + ... + $X.push(...); + ... + $F_FS($X); + } + - metavariable-pattern: + metavariable: $F_ENV + patterns: + - pattern-either: + - pattern: "args" + - pattern: "$PATH::args" + - metavariable-pattern: + metavariable: $PATH + patterns: + - pattern-either: + - pattern: std::env + - pattern: env + message: | + Detected path injection from external input. If input is user supplied, this can + result in a program accessing the file system outside it's parent directory. For + example, a malicious actor could feed in '..' or '/' as input data to cause this + program to access a path outside the current parent directory. + metadata: + cwe: "CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')" + owasp: 'A1: Injection' + severity: WARNING + languages: [rust] \ No newline at end of file diff --git a/rust/lang/security/audit/path/path-injection-env-var.yaml b/rust/lang/security/audit/path/path-injection-env-var.yaml new file mode 100644 index 0000000000..2e1ac523d6 --- /dev/null +++ b/rust/lang/security/audit/path/path-injection-env-var.yaml @@ -0,0 +1,46 @@ +rules: + - id: path-injection-env-var + patterns: + - pattern-either: + - patterns: + - pattern: | + fn $FUNC(...) { + let $VAR = $F_ENV(...)?; + ... + $X.push($VAR); + ... + $F_FS($X); + } + - pattern: | + fn $FUNC(...) { + let $VAR = $F_ENV(...)?; + ... + $F_FS(...); + } + - metavariable-pattern: + metavariable: $F_ENV + pattern-either: + - pattern: std::env::var + - pattern: env::var + - pattern: "var" + - pattern: "var" + - pattern: "std::env::var_os" + - pattern: "env::var_os" + - pattern: "var_os" + - metavariable-pattern: + metavariable: $F_FS + pattern-either: + - pattern: "std::fs::File::open" + - pattern: "fs::File::open" + - pattern: "File::open" + - pattern: "open" + message: | + Detected path injection from external input. If input is user supplied, this can + result in a program accessing the file system outside it's parent directory. For + example, a malicious actor could feed in '..' or '/' as input data to cause this + program to access a path outside the current parent directory. + metadata: + cwe: "CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')" + owasp: 'A1: Injection' + severity: WARNING + languages: [rust] \ No newline at end of file diff --git a/rust/lang/security/audit/sensitive-data-exposure.yml b/rust/lang/security/audit/sensitive-data-exposure.yml new file mode 100644 index 0000000000..375ba1d708 --- /dev/null +++ b/rust/lang/security/audit/sensitive-data-exposure.yml @@ -0,0 +1,22 @@ +rules: + - id: sensitive-data-exposure + message: | + Because the information is stored in cleartext, attackers could potentially read it. + Even if the information is encoded in a way that is not human-readable, certain techniques could determine which encoding is being used, then decode the information. + metadata: + cwe: | + - "CWE-312: Cleartext Storage of Sensitive Information." + - "CWE-318: Cleartext Storage of Sensitive Information in Executable." + owasp: 'A3: Sensitive Data Exposure' + languages: [rust] + severity: WARNING + pattern-either: + - pattern: | + let $VAL = requests::$METHOD(...).unwrap(); + ... + headers.set($HEADER($VAL)); + - pattern: | + let $VAL = requests::$METHOD(...); + ... + headers.set($HEADER($VAL)); + diff --git a/rust/lang/security/audit/temporary-insecure-file-permissions.yaml b/rust/lang/security/audit/temporary-insecure-file-permissions.yaml new file mode 100644 index 0000000000..43141dd382 --- /dev/null +++ b/rust/lang/security/audit/temporary-insecure-file-permissions.yaml @@ -0,0 +1,19 @@ +rules: + - id: temporary-insecure-file-permissions + patterns: + - pattern-either: + - pattern: | + let $TMPDIR = tempdir()?; + ... + let $FILENAME = dir.path().join(...); + ... + File::create($FILENAME); + ... + message: | + Opening temporary files without appropriate measures or controls can leave the file, its contents and any function that it impacts vulnerable to attack. + metadata: + cwe: "CWE-378: Creation of Temporary File With Insecure Permissions + CWE-379: Creation of Temporary File in Directory with Insecure Permissions + CWE-377: Insecure Temporary File" + languages: [rust] + severity: WARNING diff --git a/rust/lang/security/audit/unprotected-credentials.yaml b/rust/lang/security/audit/unprotected-credentials.yaml new file mode 100644 index 0000000000..03f6de12df --- /dev/null +++ b/rust/lang/security/audit/unprotected-credentials.yaml @@ -0,0 +1,27 @@ +rules: + - id: unprotected-credentials + metadata: + cwe: 'CWE-256: Unprotected Storage of Credentials' + owasp: 'A3: Sensitive Data Exposure' + languages: [rust] + severity: WARNING + message: | + Password management issues occur when a password is stored in plaintext in an + application's properties or configuration file. Storing a plaintext password + in a configuration file allows anyone who can read the file access to the + password-protected resource. + patterns: + - pattern-either: + - patterns: + - pattern-either: + - pattern: | + $PATH.push("$FILENAME"); + ... + - pattern: | + PathBuf::from($FILENAME); + ... + - metavariable-regex: + metavariable: $FILENAME + # Match a filename containing any of these substrings (i.e. "password"), + # unless the filename designates it as encrypted (.pgp or .gpg) + regex: '.*(passwords?|credentials?|creds?|secrets?|keys?).*\.(?!pgp$|gpg$)[^.]+' \ No newline at end of file diff --git a/rust/lang/security/audit/xss-templates.yaml b/rust/lang/security/audit/xss-templates.yaml new file mode 100644 index 0000000000..1476d6551f --- /dev/null +++ b/rust/lang/security/audit/xss-templates.yaml @@ -0,0 +1,24 @@ +rules: + - id: cross-site-scripting-templates-detected + patterns: + - pattern-either: + - pattern: | + let $VAR = requests::$METHOD(...).unwrap(); + ... + return [...,$VAR,...].join("\n"); + + message: | + Mako templates do not provide a global HTML escaping mechanism. + This means you must escape all sensitive data in your templates + using '| u' for URL escaping or '| h' for HTML escaping. + If you are using Mako to serve web content, consider using + a system such as Jinja2 which enables global escaping. + metadata: + cwe: "CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')" + owasp: 'A7: Cross-Site Scripting (XSS)' + source-rule-url: https://github.com/PyCQA/bandit/blob/b1411bfb43795d3ffd268bef17a839dee954c2b1/bandit/plugins/mako_templates.py + references: + - https://docs.makotemplates.org/en/latest/syntax.html#expression-escaping + - https://jinja.palletsprojects.com/en/2.11.x/intro/# + languages: [rust] + severity: INFO \ No newline at end of file diff --git a/rust/lang/security/injection/improper-neutralization-special-elements.yaml b/rust/lang/security/injection/improper-neutralization-special-elements.yaml new file mode 100644 index 0000000000..2701e38370 --- /dev/null +++ b/rust/lang/security/injection/improper-neutralization-special-elements.yaml @@ -0,0 +1,20 @@ +rules: + - id: improper_neutralization_special_elements + patterns: + - pattern-either: + - pattern: | + fn $FUNC(...) { + requests::$METHOD(...); + ... + PgPoolOptions::new(...); + ... + sqlx::query_as(...); + + } + message: "The application generates a query intended to access or manipulate data in a data store such as a database, but it does not neutralize or incorrectly neutralizes special elements that can modify the intended logic of the query." + languages: + - rust + severity: ERROR + metadata: + owasp: 'A1: Injection' + cwe: 'CWE-943: Improper Neutralization of Special Elements in Data Query Logic' diff --git a/rust/lang/security/injection/mysql-sqli.yaml b/rust/lang/security/injection/mysql-sqli.yaml new file mode 100644 index 0000000000..8335982ab5 --- /dev/null +++ b/rust/lang/security/injection/mysql-sqli.yaml @@ -0,0 +1,43 @@ +rules: + - id: mysql-sqli + languages: + - rust + message: > + Detected string concatenation with a non-literal variable in a go-pg + + SQL statement. This could lead to SQL injection if the variable is user-controlled + + and not properly sanitized. In order to prevent SQL injection, + + used parameterized queries instead of string concatenation. You can use parameterized queries like so: + + '(SELECT ? FROM table, data1)' + metadata: + owasp: 'A1: Injection' + references: + - https://pg.uptrace.dev/ + severity: WARNING + patterns: + - pattern-either: + - patterns: + - pattern: | + $CONN.query_first($QUERY) + - pattern-either: + - pattern-inside: | + $QUERY = $X + $Y + ... + - pattern-inside: | + $QUERY += $X + ... + - pattern-inside: | + $QUERY = format!($PARAM1, ...) + ... + - pattern-not-inside: | + $QUERY += "..." + ... + - pattern-not-inside: | + $QUERY = "..." + "..." + ... + - pattern: $CONN.query_first($X + $Y) + - pattern: $CONN.query_first(format!("...", $PARAM1, ...)) + diff --git a/rust/lang/security/injection/resource-injection.yaml b/rust/lang/security/injection/resource-injection.yaml new file mode 100644 index 0000000000..e65737f0db --- /dev/null +++ b/rust/lang/security/injection/resource-injection.yaml @@ -0,0 +1,29 @@ +rules: + - id: resource-injection + languages: + - rust + message: | + The software receives input from an upstream component, but it does not restrict + or incorrectly restricts the input before it is used as an identifier for a + resource that may be outside the intended sphere of control. + metadata: + owasp: 'A1: Injection' + cwe: 'CWE-99: Improper Control of Resource Identifiers ("Resource Injection")' + severity: WARNING + patterns: + - pattern-either: + - pattern: | + fn $FUNC($ARG: $ARGTYPE, ...) { + ... + let $PATH = PathBuf::from(...); + ... + $PATH.push(<... $ARG ...>); + ... + } + - pattern: | + fn $FUNC($ARG: $ARGTYPE, ...) { + ... + let $PATH = PathBuf::from($ARG); + ... + } + diff --git a/rust/lang/security/injection/sql-string.yaml b/rust/lang/security/injection/sql-string.yaml new file mode 100644 index 0000000000..d842d966af --- /dev/null +++ b/rust/lang/security/injection/sql-string.yaml @@ -0,0 +1,32 @@ +rules: + - id: sql-string + message: | + Avoiding SQL string concatenation: untrusted input concatinated with raw + SQL query can result in SQL Injection. This could lead to SQL + injection if variables in the SQL statement are not properly sanitized. + metadata: + cwe:"CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')" + owasp: 'A1: Injection' + source-rule-url: https://find-sec-bugs.github.io/bugs.htm#SQL_INJECTION + asvs: + section: "V5: Validation, Sanitization and Encoding Verification Requirements" + control_id: "5.3.5 Injection" + control_url: "https://github.com/OWASP/ASVS/blob/master/4.0/en/0x13-V5-Validation-Sanitization-Encoding.md#v53-output-encoding-and-injection-prevention-requirements" + version: "4" + references: + - https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html + - https://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html#create_ps + - https://software-security.sans.org/developer-how-to/fix-sql-injection-in-java-using-prepared-callable-statement + languages: [rust] + severity: WARNING + pattern-either: + - patterns: + - pattern: | + fn $FUNC(...) { + requests::$METHOD(...); + ... + PgPoolOptions::new(...); + ... + sqlx::query_as(...); + } + diff --git a/rust/lang/security/injection/sqlx-sqli.yaml b/rust/lang/security/injection/sqlx-sqli.yaml new file mode 100644 index 0000000000..94bfb4c087 --- /dev/null +++ b/rust/lang/security/injection/sqlx-sqli.yaml @@ -0,0 +1,43 @@ +rules: + - id: sqlx-sqli + languages: + - rust + message: > + Detected string concatenation with a non-literal variable in a go-pg + + SQL statement. This could lead to SQL injection if the variable is user-controlled + + and not properly sanitized. In order to prevent SQL injection, + + used parameterized queries instead of string concatenation. You can use parameterized queries like so: + + '(SELECT ? FROM table, data1)' + metadata: + owasp: 'A1: Injection' + references: + - https://pg.uptrace.dev/ + severity: WARNING + patterns: + - pattern-either: + - patterns: + - pattern: | + sqlx::query!($QUERY) + - pattern-either: + - pattern-inside: | + $QUERY = $X + $Y + ... + - pattern-inside: | + $QUERY += $X + ... + - pattern-inside: | + $QUERY = format!($PARAM1, ...) + ... + - pattern-not-inside: | + $QUERY += "..." + ... + - pattern-not-inside: | + $QUERY = "..." + "..." + ... + - pattern: sqlx::query!($X + $Y) + - pattern: sqlx::query!(format!("...", $PARAM1, ...)) + diff --git a/rust/lang/security/injection/xml-entity-injection.yaml b/rust/lang/security/injection/xml-entity-injection.yaml new file mode 100644 index 0000000000..8e63b4c112 --- /dev/null +++ b/rust/lang/security/injection/xml-entity-injection.yaml @@ -0,0 +1,33 @@ +rules: + - id: xml-entity-injection + languages: + - rust + message: | + The software processes an XML document that can contain XML entities with URIs that + resolve to documents outside of the intended sphere of control, causing the product + to embed incorrect documents into its output. It is also possible for recursive DTDs + to create an explosive growth of data when processed, similar to a zip bomb. + metadata: + owasp: 'A1: Injection' + cwe: + - 'CWE-611: Improper Restriction of XML External Entity Reference' + - 'CWE-776: Improper Restriction of Recursive Entity References in DTDs' + reference: 'https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html' + severity: WARNING + patterns: + - pattern-either: + - pattern: xmlCtxtReadDoc(...); + - pattern: xmlCtxtReadFd(...); + - pattern: xmlCtxtReadFile(...); + - pattern: xmlCtxtReadIO(...); + - pattern: xmlCtxtReadMemory(...); + - pattern: xmlCtxtUseOptions(...); + - pattern: xmlParseInNodeContext(...); + - pattern: xmlReadDoc(...); + - pattern: xmlReadFd(...); + - pattern: xmlReadFile(...); + - pattern: xmlReadIO(...); + - pattern: xmlReadMemory(...); + - pattern: <... xmlParserOption_XML_PARSE_NOENT ...> + - pattern: <... xmlParserOption_XML_PARSE_DTDLOAD ...> + diff --git a/rust/lang/security/injection/xpath-injection.yaml b/rust/lang/security/injection/xpath-injection.yaml new file mode 100644 index 0000000000..ba48a39cd3 --- /dev/null +++ b/rust/lang/security/injection/xpath-injection.yaml @@ -0,0 +1,23 @@ +rules: + - id: xpath-injection + languages: + - rust + message: | + The software does not properly neutralize special elements that are used in XML, + allowing attackers to modify the syntax, content, or commands of the XML before + it is processed by an end system. + metadata: + owasp: 'A1: Injection' + cwe: 'CWE-91: XML Injection (aka Blind XPath Injection)' + severity: WARNING + patterns: + - pattern-either: + - patterns: + - pattern: | + fn $FUNC(...) { + let $QUERY = format!(...); + ... + $CTX.evaluate(&$QUERY); + ... + } + diff --git a/rust/lang/security/net/bind-all.yaml b/rust/lang/security/net/bind-all.yaml new file mode 100644 index 0000000000..7278b24615 --- /dev/null +++ b/rust/lang/security/net/bind-all.yaml @@ -0,0 +1,17 @@ +rules: + - id: avoid-bind-to-all-interfaces + message: Listening on 0.0.0.0 or empty string could unexpectedly expose the server publicly as it binds to all available + interfaces + languages: [rust] + severity: WARNING + metadata: + cwe: 'CWE-200: Exposure of Sensitive Information to an Unauthorized Actor' + owasp: 'A6: Security Misconfiguration' + source-rule-url: https://github.com/securego/gosec + pattern-either: + - pattern: TcpListener::bind("=~/^0.0.0.0:.*$/",...) + - pattern: UdpSocket::bind("=~/^0.0.0.0:.*$/") + - pattern: TcpListener::bind("=~/^:.*$/",...) + - pattern: UdpSocket::bind("=~/^:.*$/") + - pattern: Ipv4Addr::new(0, 0, 0, 0) + - pattern: SocketAddr::from(([0, 0, 0, 0], ...)) diff --git a/rust/lang/security/net/cookie-missing-httponly.yaml b/rust/lang/security/net/cookie-missing-httponly.yaml new file mode 100644 index 0000000000..86cd10f6d8 --- /dev/null +++ b/rust/lang/security/net/cookie-missing-httponly.yaml @@ -0,0 +1,36 @@ +rules: + - id: cookie-set-httponly-false + patterns: + - pattern: $VAR.set_http_only(false) + message: | + A session cookie was detected without setting the 'HttpOnly' flag. + The 'HttpOnly' flag for cookies instructs the browser to forbid + client-side scripts from reading the cookie which mitigates XSS + attacks. Set the 'HttpOnly' flag by setting 'set_http_only' to 'true' + in the Cookie. + metadata: + cwe: "CWE-1004: Sensitive Cookie Without 'HttpOnly' Flag" + owasp: 'A3: Sensitive Data Exposure' + fix-regex: + regex: (set_http_only(\s*false\s+)) + replacement: \1true + severity: WARNING + languages: [rust] + + - id: cookie-build-httponly + patterns: + - pattern: $VAR.http_only(false) + message: | + A session cookie was detected without setting the 'HttpOnly' flag. + The 'HttpOnly' flag for cookies instructs the browser to forbid + client-side scripts from reading the cookie which mitigates XSS + attacks. Set the 'HttpOnly' flag by setting 'set_http_only' to 'true' + in the Cookie. + metadata: + cwe: "CWE-1004: Sensitive Cookie Without 'HttpOnly' Flag" + owasp: 'A3: Sensitive Data Exposure' + fix-regex: + regex: (http_only(\s*false\s+)) + replacement: \1true + severity: WARNING + languages: [rust] diff --git a/rust/lang/security/net/cookie-missing-secure.yaml b/rust/lang/security/net/cookie-missing-secure.yaml new file mode 100644 index 0000000000..4bb2b3e210 --- /dev/null +++ b/rust/lang/security/net/cookie-missing-secure.yaml @@ -0,0 +1,34 @@ +rules: + - id: cookie-missing-set_secure + patterns: + - pattern: $VAR.set_secure(false) + message: | + A session cookie was detected without setting the 'Secure' flag. + The 'secure' flag for cookies prevents the client from transmitting + the cookie over insecure channels such as HTTP. Set the 'Secure' + flag by setting 'Secure' to 'true' in the Options struct. + metadata: + cwe: "CWE-614: Sensitive Cookie in HTTPS Session Without 'Secure' Attribute" + owasp: 'A3: Sensitive Data Exposure' + fix-regex: + regex: (set_secure(\s*false\s+)) + replacement: \1true + severity: WARNING + languages: [rust] + + - id: cookie-build-secure_false + patterns: + - pattern: $VAR.secure(false) + message: | + A session cookie was detected without setting the 'Secure' flag. + The 'secure' flag for cookies prevents the client from transmitting + the cookie over insecure channels such as HTTP. Set the 'Secure' + flag by setting 'Secure' to 'true' in the Options struct. + metadata: + cwe: "CWE-614: Sensitive Cookie in HTTPS Session Without 'Secure' Attribute" + owasp: 'A3: Sensitive Data Exposure' + fix-regex: + regex: (secure(\s*false\s+)) + replacement: \1true + severity: WARNING + languages: [rust] diff --git a/rust/lang/security/net/disabled-cert-validation.rs b/rust/lang/security/net/disabled-cert-validation.rs new file mode 100644 index 0000000000..e78a533b4e --- /dev/null +++ b/rust/lang/security/net/disabled-cert-validation.rs @@ -0,0 +1,29 @@ +fn reqwests_f1() -> Result<(), Box> { + let builder = reqwest::ClientBuilder::new(); + // ruleid: disabled-cert-validation + let builder = builder.danger_accept_invalid_certs(true); + let client = builder.build()?; + + Ok(()) +} + +fn reqwests_f2() -> Result<(), Box> { + // ruleid: disabled-cert-validation + let client = reqwest::ClientBuilder::new() + .danger_accept_invalid_certs(true) + .build()?; + + Ok(()) +} + +fn reqwests_f3() -> Result<(), Box> { + // ok: disabled-cert-validation + let client = reqwest::ClientBuilder::new() + // ok: disabled-cert-validation + .danger_accept_invalid_certs(false) + .build()?; + + Ok(()) +} + +fn main() {} diff --git a/rust/lang/security/net/disabled-cert-validation.yaml b/rust/lang/security/net/disabled-cert-validation.yaml new file mode 100644 index 0000000000..78043f7d28 --- /dev/null +++ b/rust/lang/security/net/disabled-cert-validation.yaml @@ -0,0 +1,10 @@ +rules: + - id: disabled-cert-validation + pattern-either: + - pattern: $LHS.danger_accept_invalid_certs(true); + message: certificate verification explicitly disabled, insecure connections possible + metadata: + cwe: 'CWE-295: Improper Certificate Validation' + owasp: 'A3: Sensitive Data Exposure' + languages: [rust] + severity: ERROR diff --git a/rust/lang/security/net/unsecure-transmission.rs b/rust/lang/security/net/unsecure-transmission.rs new file mode 100644 index 0000000000..05db7d032a --- /dev/null +++ b/rust/lang/security/net/unsecure-transmission.rs @@ -0,0 +1,26 @@ +use reqwest; +use reqwest::blocking::Client; + +fn unsecure() { + // ruleid: unsecure-transmission + let client = Client::builder() + .danger_accept_invalid_hostnames(true) + .build() + .unwrap(); + let res = client.get("http://www.google.com").send().unwrap(); + + println!("res: is {}", res.status()); +} + +fn secure() { + // ok: unsecure-transmission + let client = Client::builder().build().unwrap(); + let res = client.get("http://www.google.com").send().unwrap(); + + println!("res: is {}", res.status()); +} + +fn main() { + unsecure(); + secure(); +} diff --git a/rust/lang/security/net/unsecure-transmission.yaml b/rust/lang/security/net/unsecure-transmission.yaml new file mode 100644 index 0000000000..aa10281d18 --- /dev/null +++ b/rust/lang/security/net/unsecure-transmission.yaml @@ -0,0 +1,14 @@ +rules: + - id: unsecure-transmission + patterns: + - pattern-either: + - pattern: $LHS.danger_accept_invalid_hostnames(true) + message: | + The software transmits sensitive or security-critical data in cleartext + in a communication channel that can be sniffed by unauthorized actors. + metadata: + cwe: "CWE-319: Cleartext Transmission of Sensitive Information" + owasp: 'A3: Sensitive Data Exposure' + severity: WARNING + languages: [rust] + diff --git a/rust/lang/security/net/xsrf.rs b/rust/lang/security/net/xsrf.rs new file mode 100644 index 0000000000..530ad2a779 --- /dev/null +++ b/rust/lang/security/net/xsrf.rs @@ -0,0 +1,34 @@ +use reqwest::{self}; + +//ruleid: XSRF_URL +fn get(my_url: String){ + let res = reqwest::get(my_url).send().unwrap(); +} + +//ruleid: XSRF_URL +fn post(my_url: String){ + let res = reqwest::post(my_url).send().unwrap(); +} + +//ruleid: XSRF_URL +fn put(my_url: String){ + let res = reqwest::put(my_url).send().unwrap(); +} + +//ruleid: XSRF_URL +fn patch(my_url: String){ + let res = reqwest::patch(my_url).send().unwrap(); +} + +//ruleid: XSRF_URL +fn head(my_url: String){ + let res = reqwest::head(my_url).send().unwrap(); +} + +//ruleid: XSRF_URL +fn head_no_method(my_url: String){ + let res = reqwest::head(my_url); +} + + + diff --git a/rust/lang/security/net/xsrf.yaml b/rust/lang/security/net/xsrf.yaml new file mode 100644 index 0000000000..563d36b9ad --- /dev/null +++ b/rust/lang/security/net/xsrf.yaml @@ -0,0 +1,33 @@ +rules: + - id: XSRF_URL + message: > + A parameter being passed directly into .URL function most likely lead to + SSRF. + metadata: + cwe: CWE-918 Server-Side Request Forgery (SSRF) + owasp: A10:2021 – Server-Side Request Forgery (SSRF) + source-rule-url: https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html + severity: ERROR + patterns: + + - pattern-either: + - pattern: | + fn $METHOD($VAR: String) { + ... + let $VAL = reqwest::$WRITE($VAR). ...; + ... + } + + - pattern: | + fn $METHOD($VAR: String) { + ... + let $VAL = reqwest::$WRITE($VAR); + ... + } + + - metavariable-regex: + metavariable: $WRITE + regex: ^(get|post|put|patch|head)$ + + languages: + - rust From 29997874a1bcf2d508493f1777a6d9a9b0242ab9 Mon Sep 17 00:00:00 2001 From: sxm588 Date: Tue, 19 Sep 2023 14:00:18 -0500 Subject: [PATCH 2/4] adding other rules --- .../Insufficient-protected-credentials.rs | 13 +++ .../command-injection-process-builder.rs | 5 ++ .../audit/crypto/tls-insecure-cipher.rs | 32 +++++++ .../audit/crypto/tls-insecure-cipher.yaml | 14 ++++ rust/lang/security/audit/crypto/use-of-des.rs | 12 +++ .../security/audit/crypto/use-of-des.yaml | 15 ++++ rust/lang/security/audit/crypto/use-of-md2.rs | 67 +++++++++++++++ .../security/audit/crypto/use-of-md2.yaml | 33 ++++++++ rust/lang/security/audit/crypto/use-of-md4.rs | 67 +++++++++++++++ .../security/audit/crypto/use-of-md4.yaml | 33 ++++++++ rust/lang/security/audit/crypto/use-of-md5.rs | 73 ++++++++++++++++ .../security/audit/crypto/use-of-md5.yaml | 45 ++++++++++ rust/lang/security/audit/crypto/use-of-rc4.rs | 41 +++++++++ .../security/audit/crypto/use-of-rc4.yaml | 33 ++++++++ .../lang/security/audit/crypto/use-of-sha1.rs | 39 +++++++++ .../security/audit/crypto/use-of-sha1.yaml | 22 +++++ .../audit/crypto/use-of-weak-rsa-key.rs | 8 ++ .../audit/dangerous-command-injection.rs | 12 +++ .../security/audit/dangerous-exec-command.rs | 83 +++++++++++++++++++ .../security/audit/dangerous-system-call.rs | 15 ++++ .../security/audit/exposure-dir-listing.rs | 68 +++++++++++++++ .../audit/exposure-of-private-info.rs | 63 ++++++++++++++ .../security/audit/exposure-via-error-msg.rs | 33 ++++++++ .../audit/improper-null-neutralization.rs | 46 ++++++++++ .../audit/incorrect-permission-assignment.rs | 14 ++++ rust/lang/security/audit/ldap-query.rs | 21 +++++ .../audit/path/path-injection-cli-arg.rs | 23 +++++ .../audit/path/path-injection-env-var.rs | 22 +++++ .../security/audit/sensitive-data-exposure.rs | 18 ++++ .../temporary-insecure-file-permissions.rs | 25 ++++++ .../security/audit/unprotected-credentials.rs | 42 ++++++++++ rust/lang/security/audit/xss-templates.rs | 25 ++++++ ...mproper-neutralization-special-elements.rs | 26 ++++++ rust/lang/security/injection/mysql-sqli.rs | 10 +++ .../security/injection/resource-injection.rs | 38 +++++++++ rust/lang/security/injection/sql-string.rs | 18 ++++ rust/lang/security/injection/sqlx-sqli.rs | 9 ++ .../injection/xml-entity-injection.rs | 32 +++++++ .../security/injection/xpath-injection.rs | 63 ++++++++++++++ rust/lang/security/net/bind-all.rs | 40 +++++++++ .../security/net/cookie-missing-httponly.rs | 14 ++++ .../security/net/cookie-missing-secure.rs | 15 ++++ rust/lang/security/net/log-neutralization.rs | 41 +++++++++ .../lang/security/net/log-neutralization.yaml | 43 ++++++++++ 44 files changed, 1411 insertions(+) create mode 100644 rust/lang/security/audit/Insufficient-protected-credentials.rs create mode 100644 rust/lang/security/audit/command-injection-process-builder.rs create mode 100644 rust/lang/security/audit/crypto/tls-insecure-cipher.rs create mode 100644 rust/lang/security/audit/crypto/tls-insecure-cipher.yaml create mode 100644 rust/lang/security/audit/crypto/use-of-des.rs create mode 100644 rust/lang/security/audit/crypto/use-of-des.yaml create mode 100644 rust/lang/security/audit/crypto/use-of-md2.rs create mode 100644 rust/lang/security/audit/crypto/use-of-md2.yaml create mode 100644 rust/lang/security/audit/crypto/use-of-md4.rs create mode 100644 rust/lang/security/audit/crypto/use-of-md4.yaml create mode 100644 rust/lang/security/audit/crypto/use-of-md5.rs create mode 100644 rust/lang/security/audit/crypto/use-of-md5.yaml create mode 100644 rust/lang/security/audit/crypto/use-of-rc4.rs create mode 100644 rust/lang/security/audit/crypto/use-of-rc4.yaml create mode 100644 rust/lang/security/audit/crypto/use-of-sha1.rs create mode 100644 rust/lang/security/audit/crypto/use-of-sha1.yaml create mode 100644 rust/lang/security/audit/crypto/use-of-weak-rsa-key.rs create mode 100644 rust/lang/security/audit/dangerous-command-injection.rs create mode 100644 rust/lang/security/audit/dangerous-exec-command.rs create mode 100644 rust/lang/security/audit/dangerous-system-call.rs create mode 100644 rust/lang/security/audit/exposure-dir-listing.rs create mode 100644 rust/lang/security/audit/exposure-of-private-info.rs create mode 100644 rust/lang/security/audit/exposure-via-error-msg.rs create mode 100644 rust/lang/security/audit/improper-null-neutralization.rs create mode 100644 rust/lang/security/audit/incorrect-permission-assignment.rs create mode 100644 rust/lang/security/audit/ldap-query.rs create mode 100644 rust/lang/security/audit/path/path-injection-cli-arg.rs create mode 100644 rust/lang/security/audit/path/path-injection-env-var.rs create mode 100644 rust/lang/security/audit/sensitive-data-exposure.rs create mode 100644 rust/lang/security/audit/temporary-insecure-file-permissions.rs create mode 100644 rust/lang/security/audit/unprotected-credentials.rs create mode 100644 rust/lang/security/audit/xss-templates.rs create mode 100644 rust/lang/security/injection/improper-neutralization-special-elements.rs create mode 100644 rust/lang/security/injection/mysql-sqli.rs create mode 100644 rust/lang/security/injection/resource-injection.rs create mode 100644 rust/lang/security/injection/sql-string.rs create mode 100644 rust/lang/security/injection/sqlx-sqli.rs create mode 100644 rust/lang/security/injection/xml-entity-injection.rs create mode 100644 rust/lang/security/injection/xpath-injection.rs create mode 100644 rust/lang/security/net/bind-all.rs create mode 100644 rust/lang/security/net/cookie-missing-httponly.rs create mode 100644 rust/lang/security/net/cookie-missing-secure.rs create mode 100644 rust/lang/security/net/log-neutralization.rs create mode 100644 rust/lang/security/net/log-neutralization.yaml diff --git a/rust/lang/security/audit/Insufficient-protected-credentials.rs b/rust/lang/security/audit/Insufficient-protected-credentials.rs new file mode 100644 index 0000000000..1ecc2163ad --- /dev/null +++ b/rust/lang/security/audit/Insufficient-protected-credentials.rs @@ -0,0 +1,13 @@ +use postgres::{Connection, TlsMode}; + +//This works but does it really cover the rule? We need to rethink this one +//todoruleid: insufficient-protected-credentials +fn main() { + //The following code reads a password and uses the password to connect to a database. + let password = requests::get("http://example.org/getPassword").unwrap(); + let url = format!( + "postgresql://postgres:{}@localhost:5433:2994/food", + password + ); + let conn = Connection::connect(url, TlsMode::None).unwrap(); +} diff --git a/rust/lang/security/audit/command-injection-process-builder.rs b/rust/lang/security/audit/command-injection-process-builder.rs new file mode 100644 index 0000000000..4f62883848 --- /dev/null +++ b/rust/lang/security/audit/command-injection-process-builder.rs @@ -0,0 +1,5 @@ +//ruleid: dangerous-spawn-process +fn main() { + let a = requests::get("http://example.org/get").unwrap(); + Command::new("ls").arg(a); +} diff --git a/rust/lang/security/audit/crypto/tls-insecure-cipher.rs b/rust/lang/security/audit/crypto/tls-insecure-cipher.rs new file mode 100644 index 0000000000..c49118171c --- /dev/null +++ b/rust/lang/security/audit/crypto/tls-insecure-cipher.rs @@ -0,0 +1,32 @@ +fn tls_insecure_cipher() { + //ruleid: tls-with-insecure-cipher + acceptor + .set_ciphersuites( + "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256", + ) + .unwrap(); +} + +fn tls_insecure_cipher_rsa_rc4_sha() { + //ruleid: tls-with-insecure-cipher + acceptor + .set_ciphersuites( + "TLS_RSA_WITH_RC4_128_SHA:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256", + ) + .unwrap(); +} + +fn tls_insecure_cipher_rsa_acs_128() { + //ruleid: tls-with-insecure-cipher + acceptor + .set_ciphersuites( + "TLS_RSA_WITH_AES_128_CBC_SHA256:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256", + ) + .unwrap(); +} + +fn tls_insecure_cipher_all() { + //ruleid: tls-with-insecure-cipher + acceptor.set_ciphersuites( + "TLS_RSA_WITH_RC4_128_SHA:TLS_RSA_WITH_AES_128_CBC_SHA256:TLS_RSA_WITH_AES_128_CBC_SHA256:TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256").unwrap(); +} diff --git a/rust/lang/security/audit/crypto/tls-insecure-cipher.yaml b/rust/lang/security/audit/crypto/tls-insecure-cipher.yaml new file mode 100644 index 0000000000..43f31275fb --- /dev/null +++ b/rust/lang/security/audit/crypto/tls-insecure-cipher.yaml @@ -0,0 +1,14 @@ +rules: + - id: tls-with-insecure-cipher + message: | + Detected an insecure CipherSuite via the 'tls' module. This suite is considered weak. + Use the function 'acceptor.set_ciphersuites()' to get a list of good cipher suites. + metadata: + cwe: 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm' + owasp: 'A9: Using Components with Known Vulnerabilities' + languages: [rust] + severity: WARNING + pattern-either: + - pattern: | + $VAR.set_ciphersuites("=~/(TLS_RSA_WITH_RC4_128_SHA|TLS_RSA_WITH_AES_128_CBC_SHA256|TLS_RSA_WITH_AES_128_CBC_SHA256|TLS_ECDHE_ECDSA_WITH_RC4_128_SHA|TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256|TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256|TLS_AES_256_GCM_SHA384)/") + diff --git a/rust/lang/security/audit/crypto/use-of-des.rs b/rust/lang/security/audit/crypto/use-of-des.rs new file mode 100644 index 0000000000..f217f66603 --- /dev/null +++ b/rust/lang/security/audit/crypto/use-of-des.rs @@ -0,0 +1,12 @@ +// Simplest to just block the import of `des` as its interface has changed quite a bit from 0.0.1 to current +pub mod a { + #[allow(unused_imports)] + //ruleid: use-of-DES + use des; +} + +pub mod b { + #[allow(unused_imports)] + //ruleid: use-of-DES + use des::Des; +} \ No newline at end of file diff --git a/rust/lang/security/audit/crypto/use-of-des.yaml b/rust/lang/security/audit/crypto/use-of-des.yaml new file mode 100644 index 0000000000..a8ab550348 --- /dev/null +++ b/rust/lang/security/audit/crypto/use-of-des.yaml @@ -0,0 +1,15 @@ +rules: + - id: use-of-DES + message: | + Detected DES cipher algorithm which is insecure. The algorithm is + considered weak and has been deprecated. Use AES instead. + languages: [rust] + severity: WARNING + metadata: + owasp: 'A9: Using Components with Known Vulnerabilities' + cwe: 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm' + references: + - https://docs.rs/des/latest/des/ + pattern-either: + - pattern: use des; + - pattern: use des::Des; \ No newline at end of file diff --git a/rust/lang/security/audit/crypto/use-of-md2.rs b/rust/lang/security/audit/crypto/use-of-md2.rs new file mode 100644 index 0000000000..2ab6fe4711 --- /dev/null +++ b/rust/lang/security/audit/crypto/use-of-md2.rs @@ -0,0 +1,67 @@ +pub mod a { + use md2::{Digest, Md2}; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md2 + let mut mymd2 = Md2::new(); + mymd2.update(a); + println!("{:#?}", mymd2.finalize()); + } +} + +pub mod b { + use md2::Digest; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md2 + let mut mymd2 = md2::Md2::new(); + mymd2.update(a); + println!("{:#?}", mymd2.finalize()); + } +} + +pub mod c { + use md2::{Digest, Md2}; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md2 + let mymd2 = Md2::new_with_prefix(a); + println!("{:#?}", mymd2.finalize()); + } +} + +pub mod d { + use md2::Digest; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md2 + let mymd2 = md2::Md2::new_with_prefix(a); + println!("{:#?}", mymd2.finalize()); + } +} + +pub mod e { + use md2::Digest; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md2 + let mymd2 = md2::Md2::digest(a); + println!("{:#?}", mymd2); + } +} + +pub mod f { + use md2::{Digest, Md2}; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md2 + let mymd2 = Md2::digest(a); + println!("{:#?}", mymd2); + } +} diff --git a/rust/lang/security/audit/crypto/use-of-md2.yaml b/rust/lang/security/audit/crypto/use-of-md2.yaml new file mode 100644 index 0000000000..82e73ceb90 --- /dev/null +++ b/rust/lang/security/audit/crypto/use-of-md2.yaml @@ -0,0 +1,33 @@ +rules: + - id: use-of-md2 + languages: [rust] + message: avoid cryptographically insecure hashing function(s) + pattern-either: + - patterns: + - pattern: md2::new(...) + - pattern-inside: | + use md2::Md2; + ... + - patterns: + - pattern: md2::Md2::new() + - patterns: + - pattern: md2::new_with_prefix(...) + - pattern-inside: | + use md2::Md2; + ... + - patterns: + - pattern: md2::Md2::new_with_prefix(...) + - patterns: + - pattern: md2::Md2::digest(...) + - patterns: + - pattern: Md2::digest(...) + - pattern-inside: | + use md2::Digest; + ... + metadata: + references: + - https://github.com/RustCrypto/hashes + - https://docs.rs/md2/latest/md2/ + category: security + cwe: "CWE-328: Use of Weak Hash" + severity: WARNING \ No newline at end of file diff --git a/rust/lang/security/audit/crypto/use-of-md4.rs b/rust/lang/security/audit/crypto/use-of-md4.rs new file mode 100644 index 0000000000..e17a1cb5c6 --- /dev/null +++ b/rust/lang/security/audit/crypto/use-of-md4.rs @@ -0,0 +1,67 @@ +pub mod a { + use md4::{Digest, Md4}; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md4 + let mut mymd4 = Md4::new(); + mymd4.update(a); + println!("{:#?}", mymd4.finalize()); + } +} + +pub mod b { + use md4::Digest; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md4 + let mut mymd4 = md4::Md4::new(); + mymd4.update(a); + println!("{:#?}", mymd4.finalize()); + } +} + +pub mod c { + use md4::{Digest, Md4}; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md4 + let mymd4 = Md4::new_with_prefix(a); + println!("{:#?}", mymd4.finalize()); + } +} + +pub mod d { + use md4::Digest; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md4 + let mymd4 = md4::Md4::new_with_prefix(a); + println!("{:#?}", mymd4.finalize()); + } +} + +pub mod e { + use md4::Digest; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md4 + let mymd4 = md4::Md4::digest(a); + println!("{:#?}", mymd4); + } +} + +pub mod f { + use md4::{Digest, Md4}; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md4 + let mymd4 = Md4::digest(a); + println!("{:#?}", mymd4); + } +} diff --git a/rust/lang/security/audit/crypto/use-of-md4.yaml b/rust/lang/security/audit/crypto/use-of-md4.yaml new file mode 100644 index 0000000000..5cbb9b2b40 --- /dev/null +++ b/rust/lang/security/audit/crypto/use-of-md4.yaml @@ -0,0 +1,33 @@ +rules: + - id: use-of-md4 + languages: [rust] + message: avoid cryptographically insecure hashing function(s) + pattern-either: + - patterns: + - pattern: md4::new(...) + - pattern-inside: | + use md4::Md4; + ... + - patterns: + - pattern: md4::Md4::new() + - patterns: + - pattern: md4::new_with_prefix(...) + - pattern-inside: | + use md4::Md4; + ... + - patterns: + - pattern: md4::Md4::new_with_prefix(...) + - patterns: + - pattern: md4::Md4::digest(...) + - patterns: + - pattern: Md4::digest(...) + - pattern-inside: | + use md4::Digest; + ... + metadata: + references: + - https://github.com/RustCrypto/hashes + - https://docs.rs/md4/latest/md4/ + category: security + cwe: "CWE-328: Use of Weak Hash" + severity: WARNING \ No newline at end of file diff --git a/rust/lang/security/audit/crypto/use-of-md5.rs b/rust/lang/security/audit/crypto/use-of-md5.rs new file mode 100644 index 0000000000..45ac9f06e6 --- /dev/null +++ b/rust/lang/security/audit/crypto/use-of-md5.rs @@ -0,0 +1,73 @@ +pub mod a { + use md5; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md5 + let mut mymd5 = md5::Context::new(); + mymd5.consume(a); + println!("{:#?}", mymd5.compute()); + } +} + +pub mod b { + use md5::Digest; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ok: use-of-md5 + let mymd4 = Digest(a); + println!("{:#?}", mymd4); + } +} + +pub mod c { + use md5::{Context, Digest}; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md5 + let mut mein = Context::new(); + let _ = Digest(a); + mein.consume(a); + println!("{:#?}", mein.compute()); + } +} + +pub mod d { + use md5::Context; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md5 + let mut mein = Context::new(); + mein.consume(a); + println!("{:#?}", mein.compute()); + } +} + +pub mod e { + use md5::*; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md5 + let _ = compute(a); + // The following should be supported by semgrep but isn't presently + //todoruleid: use-of-md5 + println!("{:#?}", compute(a)); + } +} + +pub mod f { + use md5; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-md5 + let _ = md5::compute(a); + // The following should be supported by semgrep but isn't presently + //todoruleid: use-of-md5 + println!("{:#?}", md5::compute(a)); + } +} diff --git a/rust/lang/security/audit/crypto/use-of-md5.yaml b/rust/lang/security/audit/crypto/use-of-md5.yaml new file mode 100644 index 0000000000..2a73e4e88a --- /dev/null +++ b/rust/lang/security/audit/crypto/use-of-md5.yaml @@ -0,0 +1,45 @@ +rules: + - id: use-of-md5 + message: | + Detected MD5 hash algorithm which is considered insecure. MD5 is not + collision resistant and is therefore not suitable as a cryptographic + signature. Use SHA256 or SHA3 instead. + languages: [rust] + severity: WARNING + metadata: + owasp: 'A9: Using Components with Known Vulnerabilities' + cwe: 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm' + references: + - https://docs.rs/md5/latest/md5/ + - https://www.rfc-editor.org/rfc/rfc6151 + pattern-either: + - patterns: + - pattern: md5::Context::new(...) + - pattern-inside: | + use md5; + ... + - patterns: + - pattern: Context::new(...) + - pattern-inside: | + use md5::Context; + ... + - patterns: + - pattern: new(...) + - pattern-inside: | + use md5::Md5; + ... + - patterns: + - pattern: <...compute(...)...> + - pattern-inside: | + use md5::*; + ... + - patterns: + - pattern: md5::compute(...) + - pattern-inside: | + use md5; + ... + - patterns: + - pattern: Md5::new() + - pattern-inside: | + use md5; + ... \ No newline at end of file diff --git a/rust/lang/security/audit/crypto/use-of-rc4.rs b/rust/lang/security/audit/crypto/use-of-rc4.rs new file mode 100644 index 0000000000..8c20f563dc --- /dev/null +++ b/rust/lang/security/audit/crypto/use-of-rc4.rs @@ -0,0 +1,41 @@ +pub mod a { + use rc4::KeyInit; + use rc4::Rc4; + + pub fn ex() { + //ruleid: use-of-rc4 + let _ = Rc4::new(b"key".into()); + } +} + +pub mod b { + use rc4::Rc4; + use rc4::{consts::*, KeyInit}; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-rc4 + let _ = Rc4::::new_from_slice(&a); + } +} + +pub mod c { + use rc4; + use rc4::KeyInit; + + pub fn ex() { + //ruleid: use-of-rc4 + let _ = rc4::Rc4::new(b"key".into()); + } +} + +pub mod d { + use rc4; + use rc4::{consts::*, KeyInit}; + + pub fn ex() { + let a: [u8; 16] = [0; 16]; + //ruleid: use-of-rc4 + let _ = rc4::Rc4::::new_from_slice(&a); + } +} diff --git a/rust/lang/security/audit/crypto/use-of-rc4.yaml b/rust/lang/security/audit/crypto/use-of-rc4.yaml new file mode 100644 index 0000000000..7e89646f72 --- /dev/null +++ b/rust/lang/security/audit/crypto/use-of-rc4.yaml @@ -0,0 +1,33 @@ +rules: + - id: use-of-rc4 + message: | + Detected RC4 cipher algorithm which is insecure. The algorithm has many + known vulnerabilities. Use AES instead. + languages: [rust] + severity: WARNING + metadata: + owasp: 'A9: Using Components with Known Vulnerabilities' + cwe: 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm' + references: https://docs.rs/rc4/latest/rc4/ + pattern-either: + - patterns: + - pattern: Rc4::new(...) + - pattern-inside: | + use rc4::Rc4; + ... + - patterns: + - pattern: rc4::Rc4::new(...) + - patterns: + - pattern: Rc4::<$TYPE>::new_from_slice(...) + - pattern-inside: | + use rc4::Rc4; + ... + - patterns: + - pattern: rc4::Rc4::<$TYPE>::new_from_slice(...) + - patterns: + - pattern: rc4::Rc4::digest(...) + - patterns: + - pattern: Rc4::digest(...) + - pattern-inside: | + use rc4::Digest; + ... \ No newline at end of file diff --git a/rust/lang/security/audit/crypto/use-of-sha1.rs b/rust/lang/security/audit/crypto/use-of-sha1.rs new file mode 100644 index 0000000000..2cdf18fd64 --- /dev/null +++ b/rust/lang/security/audit/crypto/use-of-sha1.rs @@ -0,0 +1,39 @@ +pub mod a { + use md2::Digest; + use sha1::Sha1; + pub fn ex() { + //ruleid: use-of-sha1 + let _ = Sha1::new(); + } +} + +pub mod b { + use md2::Digest; + pub fn ex() { + //ruleid: use-of-sha1 + let _ = sha1::Sha1::new(); + } +} + +pub mod c { + pub fn ex() { + //ruleid: use-of-sha1 + let _ = openssl::sha::sha1(b"some_bytes"); + } +} + +pub mod d { + use openssl::sha; + pub fn ex() { + //ruleid: use-of-sha1 + let _ = sha::sha1(b"some_bytes"); + } +} + +pub mod e { + use openssl::sha::sha1; + pub fn ex() { + //ruleid: use-of-sha1 + let _ = sha1(b"some_bytes"); + } +} diff --git a/rust/lang/security/audit/crypto/use-of-sha1.yaml b/rust/lang/security/audit/crypto/use-of-sha1.yaml new file mode 100644 index 0000000000..2d185c6562 --- /dev/null +++ b/rust/lang/security/audit/crypto/use-of-sha1.yaml @@ -0,0 +1,22 @@ +rules: + - id: use-of-sha1 + message: | + Detected SHA1 hash algorithm which is considered insecure. SHA1 is not + collision resistant and is therefore not suitable as a cryptographic + signature. Use SHA256 or SHA3 instead. + languages: [rust] + severity: WARNING + metadata: + owasp: 'A9: Using Components with Known Vulnerabilities' + cwe: 'CWE-327: Use of a Broken or Risky Cryptographic Algorithm' + references: + - https://docs.rs/sha-1/latest/sha1/ + pattern-either: + - patterns: + - pattern: Sha1::new(...) + - pattern-inside: | + use sha1::Sha1; + ... + - pattern: sha1::Sha1::new(...) + - pattern: $MOD::sha1(...) + - pattern: $MOD::Sha1(...) \ No newline at end of file diff --git a/rust/lang/security/audit/crypto/use-of-weak-rsa-key.rs b/rust/lang/security/audit/crypto/use-of-weak-rsa-key.rs new file mode 100644 index 0000000000..a3c30f6c81 --- /dev/null +++ b/rust/lang/security/audit/crypto/use-of-weak-rsa-key.rs @@ -0,0 +1,8 @@ +//id: use-of-weak-rsa-key +fn main() { + //todoruleid: use-of-weak-rsa-key + let pvk = openssl::rsa::Rsa::generate(1024); + use openssl::rsa::Rsa; + //todoruleid: use-of-weak-rsa-key + let pvk = Rsa::generate(1024); +} diff --git a/rust/lang/security/audit/dangerous-command-injection.rs b/rust/lang/security/audit/dangerous-command-injection.rs new file mode 100644 index 0000000000..35ccf3fbc8 --- /dev/null +++ b/rust/lang/security/audit/dangerous-command-injection.rs @@ -0,0 +1,12 @@ +use std::process::Command; + +//todoruleid: command-injection +fn main() { + let get_args = requests::get("http://example.org/get").unwrap(); + + let mut echo_hello = Command::new("/usr") + .arg(get_args) + .output() + .expect("failed to execute process"); + println!("status: {}", echo_hello); +} diff --git a/rust/lang/security/audit/dangerous-exec-command.rs b/rust/lang/security/audit/dangerous-exec-command.rs new file mode 100644 index 0000000000..7f9a8f036a --- /dev/null +++ b/rust/lang/security/audit/dangerous-exec-command.rs @@ -0,0 +1,83 @@ +fn dangerous_exec_command_sh() { + use std::process::Command; + //ruleid: dangerous-exec-command + let _ = Command::new("sh"); + //ruleid: dangerous-exec-command + let _ = Command::new("sh") + .arg("-c") + .arg("echo hello") + .output() + .expect("failed to execute process"); + //ruleid: dangerous-exec-command + let mut ls = Command::new("ls"); +} + +fn dangerous_exec_command_bash() { + use std::process::Command; + //ruleid: dangerous-exec-command + let _ = Command::new("bash"); + //ruleid: dangerous-exec-command + let _ = Command::new("bash") + .arg("-c") + .arg("echo hello") + .output() + .expect("failed to execute process"); + //ruleid: dangerous-exec-command + let mut ls = Command::new("ls"); +} + +fn dangerous_exec_command_csh() { + use std::process::Command; + //ruleid: dangerous-exec-command + let _ = Command::new("csh"); + //ruleid: dangerous-exec-command + let _ = Command::new("csh") + .arg("-c") + .arg("echo hello") + .output() + .expect("failed to execute process"); + //ruleid: dangerous-exec-command + let mut ls = Command::new("ls"); +} + +fn dangerous_exec_command_ksh() { + use std::process::Command; + //ruleid: dangerous-exec-command + let _ = Command::new("ksh"); + //ruleid: dangerous-exec-command + let _ = Command::new("ksh") + .arg("-c") + .arg("echo hello") + .output() + .expect("failed to execute process"); + //ruleid: dangerous-exec-command + let mut ls = Command::new("ls"); +} + +fn dangerous_exec_command_tcsh() { + use std::process::Command; + //ruleid: dangerous-exec-command + let _ = Command::new("tcsh"); + //ruleid: dangerous-exec-command + let _ = Command::new("tcsh") + .arg("-c") + .arg("echo hello") + .output() + .expect("failed to execute process"); + //ruleid: dangerous-exec-command + let mut ls = Command::new("ls"); +} + +fn dangerous_exec_command_zsh() { + use std::process::Command; + //ruleid: dangerous-exec-command + let _ = Command::new("zsh"); + //ruleid: dangerous-exec-command + let _ = Command::new("zsh") + .arg("-c") + .arg("echo hello") + .output() + .expect("failed to execute process"); + //ruleid: dangerous-exec-command + let mut ls = Command::new("ls"); +} diff --git a/rust/lang/security/audit/dangerous-system-call.rs b/rust/lang/security/audit/dangerous-system-call.rs new file mode 100644 index 0000000000..323e15d1cc --- /dev/null +++ b/rust/lang/security/audit/dangerous-system-call.rs @@ -0,0 +1,15 @@ +use std::process::Command; + +//ruleid: argument-injection +fn main() { + let args = requests::get("http://example.org/get").unwrap(); + + let mut echo_hello = Command::new("/usr/bin/cat") + .arg(args) + .output() + .expect("failed to execute process"); + println!("status: {}", output.status); + + io::stdout().write_all(&output.stdout).unwrap(); + io::stderr().write_all(&output.stderr).unwrap(); +} diff --git a/rust/lang/security/audit/exposure-dir-listing.rs b/rust/lang/security/audit/exposure-dir-listing.rs new file mode 100644 index 0000000000..b98aab9891 --- /dev/null +++ b/rust/lang/security/audit/exposure-dir-listing.rs @@ -0,0 +1,68 @@ +use std::env; +use std::error::Error; +use std::fs; +use std::path::PathBuf; + +fn list_user_dir() -> Result<(), Box> { + //ok: exposure-dir-listing + for f in fs::read_dir("~/Documents")? { + println!("{:?}", f?.path()); + } + + Ok(()) +} + +// Not-ok access system dir +fn list_system_dir() -> Result<(), Box> { + //ruleid: exposure-dir-listing + for f in fs::read_dir("/sbin")? { + println!("{:?}", f?.path()); + } + + Ok(()) +} + +// Directory with implied sensitive contents +fn list_sensitive_dir() -> Result<(), Box> { + //ruleid: exposure-dir-listing + for f in fs::read_dir("~/passwords")? { + println!("{:?}", f?.path()); + } + + Ok(()) +} + +fn list_bin_dir() -> Result<(), Box> { + //ruleid: exposure-dir-listing + for f in fs::read_dir("/bin")? { + println!("{:?}", f?.path()); + } + + Ok(()) +} + +fn list_sensitive_dir() -> Result<(), Box> { + //ruleid: exposure-dir-listing + for f in fs::read_dir("/System")? { + println!("{:?}", f?.path()); + } + + Ok(()) +} + +fn list_var_dir() -> Result<(), Box> { + //ruleid: exposure-dir-listing + for f in fs::read_dir("/var")? { + println!("{:?}", f?.path()); + } + + Ok(()) +} + +fn main() -> Result<(), Box> { + list_user_dir()?; + list_system_dir()?; + list_sensitive_dir()?; + + Ok(()) +} diff --git a/rust/lang/security/audit/exposure-of-private-info.rs b/rust/lang/security/audit/exposure-of-private-info.rs new file mode 100644 index 0000000000..9996dd3cd2 --- /dev/null +++ b/rust/lang/security/audit/exposure-of-private-info.rs @@ -0,0 +1,63 @@ +use env_logger; +use log; + +// A mock structure containing personl information +struct PersonalInfomration { + username: String, + password: String, + social_security_number: String, + account_id: u64, + location: (f64, f64), +} + +impl PersonalInfomration { + pub fn get_username(&self) -> &String { + &self.username + } + + pub fn get_password(&self) -> &String { + &self.password + } + + pub fn get_social_security_number(&self) -> &String { + &self.social_security_number + } + + pub fn get_account_id(&self) -> u64 { + self.account_id + } + + pub fn get_location(&self) -> (f64, f64) { + self.location + } +} + +fn get_user_info() -> PersonalInfomration { + let username = "JohnDoe79".to_string(); + let password = "passw0rd1234".to_string(); + let social_security_number = "999999999".to_string(); + let account_id = 12345; + let location = (38.8974, -77.0366); + + PersonalInfomration { + username, + password, + social_security_number, + account_id, + location, + } +} + +fn main() { + env_logger::init(); + + // Leak sensitive info via log statement + //todoruleid: exposure-of-private-info + let user = get_user_info(); + log::debug!("user account_id = {:?}", user.get_account_id()); + + // Leak sensitive info via print statement + //todoruleid: exposure-of-private-info + let pwd = user.get_password(); + println!("user password = {:?}", pwd); +} diff --git a/rust/lang/security/audit/exposure-via-error-msg.rs b/rust/lang/security/audit/exposure-via-error-msg.rs new file mode 100644 index 0000000000..dfc762ffb4 --- /dev/null +++ b/rust/lang/security/audit/exposure-via-error-msg.rs @@ -0,0 +1,33 @@ +use std::error::Error; +use std::fmt; + +#[derive(Debug)] +pub struct SensitiveError { + msg: String, +} + +impl fmt::Display for SensitiveError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SensitiveError: {}", self.msg) + } +} + +impl Error for SensitiveError { +} + +// This function returns an error with sensitive information internalized +fn sensitive_error() -> Result<(), Box> { + let msg = String::from("password: pass1234"); + let err = Box::new(SensitiveError { msg }); + + Err(err) +} + +fn main() { + let err = sensitive_error(); + + // NOTE: There is an error in semgrep v0.59.0 which causes function-like + // macros not to get correctly identified. + //todoruleid: exposure-error-msg + println!("err = {:?}", err); +} diff --git a/rust/lang/security/audit/improper-null-neutralization.rs b/rust/lang/security/audit/improper-null-neutralization.rs new file mode 100644 index 0000000000..ca8b066991 --- /dev/null +++ b/rust/lang/security/audit/improper-null-neutralization.rs @@ -0,0 +1,46 @@ +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; + +extern "C" { + fn get_c_string() -> *const c_char; +} + +fn main() { + let nul_escape_char = '\0'; + let nul_unicode_char = '\u{0000}'; + + unsafe { + // ruleid: improper-null-neutralization + let slice = CStr::from_ptr(get_c_string()); + let unknown_nul_terminated = slice.to_str().unwrap(); + slice.to_str().unwrap(); + } + + unsafe { + // ruleid: improper-null-neutralization + let cstr = CStr::from_ptr(get_c_string()).to_bytes(); + let not_null_terminated = CStr::from_bytes_with_nul_unchecked(cstr); + CStr::from_bytes_with_nul_unchecked(cstr); + } + + unsafe { + // ruleid: improper-null-neutralization + let cstr = CStr::from_ptr(get_c_string()).to_bytes(); + let not_null_terminated = CStr::from_bytes_with_nul_unchecked(cstr); + let str = CStr::from_bytes_with_nul_unchecked(cstr); + } + + unsafe { + // ruleid: improper-null-neutralization + let slice = CStr::from_ptr(get_c_string()); + let unknown_nul_terminated = slice.to_str().unwrap(); + let unknown = slice.to_str().unwrap(); + } + + unsafe { + // ok: improper-null-neutralization + let cstr = CStr::from_ptr(get_c_string()); + let null_terminated = CStr::from_bytes_with_nul(cstr); + CStr::from_bytes_with_nul(cstr); + } +} diff --git a/rust/lang/security/audit/incorrect-permission-assignment.rs b/rust/lang/security/audit/incorrect-permission-assignment.rs new file mode 100644 index 0000000000..5ba109ebb3 --- /dev/null +++ b/rust/lang/security/audit/incorrect-permission-assignment.rs @@ -0,0 +1,14 @@ +use std::fs::OpenOptions; +use std::os::unix::fs::*; + + +fn main() { + //ruleid: incorrect-permission-assignment + OpenOptions::new().mode(0777).open("somefile"); + //ruleid: incorrect-permission-assignment + OpenOptions::new().mode(0772).open("somefile"); + //ruleid: incorrect-permission-assignment + OpenOptions::new().mode(0776).open("somefile"); + //ok: incorrect-permission-assignment + OpenOptions::new().mode(0771).open("somefile"); +} diff --git a/rust/lang/security/audit/ldap-query.rs b/rust/lang/security/audit/ldap-query.rs new file mode 100644 index 0000000000..d8ca9d5a85 --- /dev/null +++ b/rust/lang/security/audit/ldap-query.rs @@ -0,0 +1,21 @@ +use ldap3::result::Result; +use ldap3::{LdapConn, Scope, SearchEntry}; + +//We need to evaluate how this works and semgrep doesnt support +fn main() -> Result<()> { + //ruleid: ldap-injection + let a = requests::get("http://example.org/get").unwrap(); + let mut ldap = LdapConn::new("ldap://localhost:2389")?; + let (rs, _res) = ldap + .search( + a, + Scope::Subtree, + "(&(objectClass=locality)(l=ma*))", + vec!["l"], + )? + .success()?; + for entry in rs { + println!("{:?}", SearchEntry::construct(entry)); + } + Ok(ldap.unbind()?) +} diff --git a/rust/lang/security/audit/path/path-injection-cli-arg.rs b/rust/lang/security/audit/path/path-injection-cli-arg.rs new file mode 100644 index 0000000000..76576004b3 --- /dev/null +++ b/rust/lang/security/audit/path/path-injection-cli-arg.rs @@ -0,0 +1,23 @@ +use std::env; +use std::error::Error; +use std::fs::File; + +/// CWE-22 Vulnerability: Path injection from environment variable +//ruleid: path-injection-cli-arg +fn path_injection_cli_arg() -> Result<(), Box> { + let args: Vec = env::args().collect(); + let arg = args.get(1).ok_or("Did not get CLI arg.")?; + let mut path = env::current_dir()?; + path.push(subpath); + + println!("path_injection_cli_arg\t{:?}", path); + File::open(path)?; + + Ok(()) +} + +fn main() -> Result<(), Box> { + path_injection_cli_arg(); + + Ok(()) +} diff --git a/rust/lang/security/audit/path/path-injection-env-var.rs b/rust/lang/security/audit/path/path-injection-env-var.rs new file mode 100644 index 0000000000..621d46e87b --- /dev/null +++ b/rust/lang/security/audit/path/path-injection-env-var.rs @@ -0,0 +1,22 @@ +use std::env; +use std::error::Error; +use std::fs::File; + +/// CWE-22 Vulnerability: Path injection from environment variable +//ruleid: path-injection-env-var +fn path_injection_env_var() -> Result<(), Box> { + let subpath = env::var("SUBPATH")?; + let mut path = env::current_dir()?; + path.push(subpath); + + println!("path_injection_env_var\t{:?}", path); + File::open(path)?; + + Ok(()) +} + +fn main() -> Result<(), Box> { + path_injection_env_var(); + + Ok(()) +} diff --git a/rust/lang/security/audit/sensitive-data-exposure.rs b/rust/lang/security/audit/sensitive-data-exposure.rs new file mode 100644 index 0000000000..fe8d50f6dd --- /dev/null +++ b/rust/lang/security/audit/sensitive-data-exposure.rs @@ -0,0 +1,18 @@ +use hyper::header::{Headers, SetCookie}; +fn main() { + //ruleid: sensitive-data-exposure + let a = requests::get("http://example.org/get").unwrap(); + let mut headers = Headers::new(); + + headers.set(SetCookie(a)); +} + +fn main_nowrap() { + //ruleid: sensitive-data-exposure + let a = requests::get("http://example.org/get"); + let mut headers = Headers::new(); + + headers.set(SetCookie(a)); +} + + diff --git a/rust/lang/security/audit/temporary-insecure-file-permissions.rs b/rust/lang/security/audit/temporary-insecure-file-permissions.rs new file mode 100644 index 0000000000..e8d484e004 --- /dev/null +++ b/rust/lang/security/audit/temporary-insecure-file-permissions.rs @@ -0,0 +1,25 @@ +use tempfile::tempdir; +use std::fs::File; +use std::io::{self, Write}; + +/* +In the following code examples a temporary file is created and written to. After using the temporary file, the file is closed and deleted from the file system. +*/ + +fn main(){ +// Create a directory inside of `std::env::temp_dir()`. +//ruleid: temporary-insecure-file-permissions +let dir = tempdir()?; + +let file_path = dir.path().join("my-temporary-note.txt"); +let mut file = File::create(file_path)?; +writeln!(file, "XYZ ZYZ XYZ")?; + +// By closing the `TempDir` explicitly, we can check that it has +// been deleted successfully. If we don't close it explicitly, +// the directory will still be deleted when `dir` goes out +// of scope, but we won't know whether deleting the directory +// succeeded. +drop(file); +dir.close()?; +} diff --git a/rust/lang/security/audit/unprotected-credentials.rs b/rust/lang/security/audit/unprotected-credentials.rs new file mode 100644 index 0000000000..fbe1a79937 --- /dev/null +++ b/rust/lang/security/audit/unprotected-credentials.rs @@ -0,0 +1,42 @@ +use serde::{Deserialize, Serialize}; +use serde_json; +use std::env; +use std::error::Error; +use std::fs::File; +use std::io::Read; +use std::path::PathBuf; + +#[derive(Debug, Serialize, Deserialize)] +struct PasswordConfig { + pub password: String, +} + +fn unprotected_credentials_1() -> Result<(), Box> { + let mut path: PathBuf = env::var("HOME")?.into(); + //ruleid: unprotected-credentials + path.push("password.json"); + let mut buf = String::new(); + let mut f = File::open(path)?; + f.read_to_string(&mut buf)?; + let conf: PasswordConfig = serde_json::from_str(&buf)?; + + Ok(()) +} + +fn unprotected_credentials_2() -> Result<(), Box> { + //ruleid: unprotected-credentials + let path = PathBuf::from("/Users/abc123/passwords.json"); + let mut buf = String::new(); + let mut f = File::open(path)?; + f.read_to_string(&mut buf)?; + let conf: PasswordConfig = serde_json::from_str(&buf)?; + + Ok(()) +} + +fn main() -> Result<(), Box> { + unprotected_credentials_1()?; + unprotected_credentials_2()?; + + Ok(()) +} diff --git a/rust/lang/security/audit/xss-templates.rs b/rust/lang/security/audit/xss-templates.rs new file mode 100644 index 0000000000..125c0b6ce1 --- /dev/null +++ b/rust/lang/security/audit/xss-templates.rs @@ -0,0 +1,25 @@ +fn main() { + let a = template(); + println!("{:?}", a); +} +//This is closer but its still not perfect. So many ways to do XSS +//@TODO +fn template() -> String { + //ruleid: cross-site-scripting-templates-detected + let get_username = requests::get("http://example.org/get_username").unwrap(); + + let a = " + + 'T' + + +

+ "; + + let b = " +

+ + "; + + return [a, get_username, get_usernameb, b].join("\n"); +} diff --git a/rust/lang/security/injection/improper-neutralization-special-elements.rs b/rust/lang/security/injection/improper-neutralization-special-elements.rs new file mode 100644 index 0000000000..b5f5d581de --- /dev/null +++ b/rust/lang/security/injection/improper-neutralization-special-elements.rs @@ -0,0 +1,26 @@ +use std::env; + +use sqlx::postgres::PgPool; + +//ruleid: improper_neutralization_special_elements +fn main() -> Result<(), sqlx::Error> { + let query = requests::get("http://example.com/get").unwrap(); + let result_type = requests::get("http://example.com/get").unwrap(); + // Create a connection pool + // for MySQL, use MySqlPoolOptions::new() + // for SQLite, use SqlitePoolOptions::new() + // etc. + let pool = PgPoolOptions::new() + .max_connections(5) + .connect(&env::var("DATABASE_URL")?) + .await?; + // Make a simple query to return the given parameter + let row: (i64,) = sqlx::query_as(format!("SELECT {}", query)) + .bind(result_type) + .fetch_one(&pool) + .await?; + + assert_eq!(row.0, 180); + + Ok(()) +} diff --git a/rust/lang/security/injection/mysql-sqli.rs b/rust/lang/security/injection/mysql-sqli.rs new file mode 100644 index 0000000000..de8d49b8f2 --- /dev/null +++ b/rust/lang/security/injection/mysql-sqli.rs @@ -0,0 +1,10 @@ +fn mysql_query_first() { + //due to the inside rule not working we will need to come back to this + //todoruleid: mysql-sqli + conn.query_first(format!("SELECT {} from tmp", "x")); +} + +fn mysql_query_first1() { + //ruleid: mysql-sqli + conn.query_first(x + y); +} diff --git a/rust/lang/security/injection/resource-injection.rs b/rust/lang/security/injection/resource-injection.rs new file mode 100644 index 0000000000..e3a35e2904 --- /dev/null +++ b/rust/lang/security/injection/resource-injection.rs @@ -0,0 +1,38 @@ +use actix_web::get; +use actix_web::web; +use actix_web::App; +use actix_web::HttpServer; +use serde::Deserialize; +use std::fs::File; +use std::io::Read; +use std::path::PathBuf; + +const SERVER_ROOT: &'static str = "/home/httpd"; + +#[derive(Deserialize)] +struct PathParam { + path: String, +} + +// Blind resource injection from HTTP request query param +#[get("/")] +//ruleid: resource-injection +async fn index(params: web::Query) -> String { + let mut path = PathBuf::from(SERVER_ROOT); + path.push(params.path.clone()); + println!("Server accesing: {:?}", path); + let mut f = File::open(path).expect("Could not open file. Does it exist?"); + let mut buf = String::new(); + f.read_to_string(&mut buf) + .expect("Failed to read file content to buffer"); + + buf +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + HttpServer::new(|| App::new().service(index)) + .bind("127.0.0.1:8080")? + .run() + .await +} diff --git a/rust/lang/security/injection/sql-string.rs b/rust/lang/security/injection/sql-string.rs new file mode 100644 index 0000000000..622b0a2486 --- /dev/null +++ b/rust/lang/security/injection/sql-string.rs @@ -0,0 +1,18 @@ +use std::env; + +use sqlx::postgres::PgPool; + +//todoruleid: sql-string +fn main() -> Result<(), sqlx::Error> { + let a = requests::get("http://example.org/get").unwrap(); + let pool = PgPoolOptions::new() + .max_connections(5) + .connect(&env::var("DATABASE_URL")?) + .await?; + + let row: (i64,) = sqlx::query_as("SELECT $1").bind(a).fetch_one(&pool).await?; + + assert_eq!(row.0, 150); + + Ok(()) +} diff --git a/rust/lang/security/injection/sqlx-sqli.rs b/rust/lang/security/injection/sqlx-sqli.rs new file mode 100644 index 0000000000..7b23ff2c16 --- /dev/null +++ b/rust/lang/security/injection/sqlx-sqli.rs @@ -0,0 +1,9 @@ +fn sqlx_sqli() { + //todoruleid: sqlx-sqli + sqlx::query!(format!("SELECT {} from tmp", "x")); +} + +fn sqlx_sqli1() { + //ruleid: sqlx-sqli + sqlx::query!(x + y); +} diff --git a/rust/lang/security/injection/xml-entity-injection.rs b/rust/lang/security/injection/xml-entity-injection.rs new file mode 100644 index 0000000000..00718a3403 --- /dev/null +++ b/rust/lang/security/injection/xml-entity-injection.rs @@ -0,0 +1,32 @@ +use libxml::bindings::xmlParserOption_XML_PARSE_DTDLOAD; +use libxml::bindings::xmlReadFile; +use std::env; +use std::ffi::CString; +use std::os::raw::c_char; +use std::path::PathBuf; + +// This example was created referencing the following documentation: +// https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html +// +// This document explains how XML entity injection works, and which features in +// popular xml parsing libraries create XXE vulnerabilities. This +// example calls into libxml2 (C library) via a FFI binding layer in Rust, +// and attempts to use features which risk an XXE. + +fn main() { + let home = env::var("HOME").expect("Env Var $HOME not set"); + let mut path = PathBuf::from(home); + path.push("test.xml"); + + let c_str = CString::new(path.to_str().unwrap()).unwrap(); + + unsafe { + //ruleid: xml-entity-injection + let _ptr = xmlReadFile( + c_str.as_ptr() as *const c_char, + std::ptr::null(), + //ruleid: xml-entity-injection + xmlParserOption_XML_PARSE_DTDLOAD as i32, + ); + } +} diff --git a/rust/lang/security/injection/xpath-injection.rs b/rust/lang/security/injection/xpath-injection.rs new file mode 100644 index 0000000000..582ac12add --- /dev/null +++ b/rust/lang/security/injection/xpath-injection.rs @@ -0,0 +1,63 @@ +use libxml::parser::Parser; +use libxml::xpath::Context; +use std::error::Error; + +fn get_xml_string() -> String { + String::from( + r#" + + + Chris + Jacoby + cjacoby + Secret1 + User + + + Peter + Pan + PPan + Secret2 + Admin + + + Donald + Duck + donaldduck + Secret3 + User + + "#, + ) +} + +//todoruleid: xpath-injection +fn login(username: &str, password: &str) -> Result<(), Box> { + let query = format!( + "//Employee[UserName/text()='{username}' and Password/text()='{password}']", + username = username, + password = password + ); + + println!("query = '{:?}'", query); + let parser = Parser::default(); + let text = get_xml_string(); + let document = parser.parse_string(text)?; + let context = Context::new(&document).unwrap(); + let res = context.evaluate(&query).unwrap(); + for r in res.get_nodes_as_vec() { + println!("r = {:?}", r.get_content()); + } + + Ok(()) +} + +fn main() -> Result<(), Box> { + // Normal query. Returns info for Donald Duck. + login("donaldduck", "Secret3")?; + + // Injection. Returns all users. + login("user' or 1=1 or 'a'='a", "pwd")?; + + Ok(()) +} diff --git a/rust/lang/security/net/bind-all.rs b/rust/lang/security/net/bind-all.rs new file mode 100644 index 0000000000..79a1dfcf1c --- /dev/null +++ b/rust/lang/security/net/bind-all.rs @@ -0,0 +1,40 @@ +fn tcp_bind_any_port() { + //ruleid: avoid-bind-to-all-interfaces + let _ = TcpListener::bind("0.0.0.0:80"); +} + +fn tcp_bind_any_port1() { + //ruleid: avoid-bind-to-all-interfaces + let _ = TcpListener::bind(":80"); +} + +fn udp_bind_any_port() { + //ruleid: avoid-bind-to-all-interfaces + let _ = UdpSocket::bind("0.0.0.0:80"); +} + +fn udp_bind_any_port1() { + //ruleid: avoid-bind-to-all-interfaces + let _ = UdpSocket::bind(":80"); +} + +fn socket_addr() { + let addrs = [ + //ruleid: avoid-bind-to-all-interfaces + SocketAddr::from(([0, 0, 0, 0], 80)), + //ruleid: avoid-bind-to-all-interfaces + SocketAddr::from(([0, 0, 0, 0], 443)), + ]; +} + +fn ipv4_addr() { + //ruleid: avoid-bind-to-all-interfaces + let _ = Ipv4Addr::new(0, 0, 0, 0); +} + +fn valid_entries() { + let _ = TcpListener::bind("192.168.1.1:2000"); + let _ = UdpSocket::bind("192.168.1.1:2000"); + let _ = SocketAddr::from(([192, 168, 1, 1], 80)); + let _ = Ipv4Addr::new(192, 168, 1, 1); +} diff --git a/rust/lang/security/net/cookie-missing-httponly.rs b/rust/lang/security/net/cookie-missing-httponly.rs new file mode 100644 index 0000000000..6d79042c78 --- /dev/null +++ b/rust/lang/security/net/cookie-missing-httponly.rs @@ -0,0 +1,14 @@ +fn set_http_cookie() { + let mut c = Cookie::new("name", "value"); + //ruleid: cookie-set-httponly-false + c.set_http_only(false); +} + +fn build_http_cookie() { + //ruleid: cookie-build-httponly + let cookie = Cookie::build("name", "value").http_only(false) + .domain("www.rust-lang.org") + .path("/") + .secure(true) + .finish(); +} diff --git a/rust/lang/security/net/cookie-missing-secure.rs b/rust/lang/security/net/cookie-missing-secure.rs new file mode 100644 index 0000000000..183851f37f --- /dev/null +++ b/rust/lang/security/net/cookie-missing-secure.rs @@ -0,0 +1,15 @@ +fn set_http_cookie() { + let mut c = Cookie::new("name", "value"); + //ruleid: cookie-missing-set_secure + c.set_secure(false); +} + +fn build_http_cookie() { + //ruleid: cookie-build-secure_false + let cookie = Cookie::build("name", "value") + .secure(false) + .domain("www.rust-lang.org") + .path("/") + .http_only(true) + .finish(); +} diff --git a/rust/lang/security/net/log-neutralization.rs b/rust/lang/security/net/log-neutralization.rs new file mode 100644 index 0000000000..40d40686db --- /dev/null +++ b/rust/lang/security/net/log-neutralization.rs @@ -0,0 +1,41 @@ + use std::num::ParseIntError; + + fn main() -> Result<(), ParseIntError> { + let mut number_str = "10"; + + //ruleid: log-neutralization + let number = match number_str.parse::() { + Ok(number) => number, + Err(e) => info!(Err(e)), + }; + + //ruleid: log-neutralization + let number = match number_str.parse::() { + Ok(number) => number, + Err(e) => debug!(Err(e)), + }; + + //ruleid: log-neutralization + let number = match number_str.parse::() { + Ok(number) => number, + Err(e) => println!(Err(e)), + }; + + //ruleid: log-neutralization + let number = match number_str.parse::() { + Ok(number) => number, + Err(e) => warn!(Err(e)), + }; + + //ruleid: log-neutralization + let number = match number_str.parse::() { + Ok(number) => number, + Err(e) => error!(Err(e)), + }; + //ruleid: log-neutralization + let number = match number_str.parse::() { + Ok(number) => number, + Err(e) => trace!(Err(e)), + }; + + } \ No newline at end of file diff --git a/rust/lang/security/net/log-neutralization.yaml b/rust/lang/security/net/log-neutralization.yaml new file mode 100644 index 0000000000..6f03d1ed1c --- /dev/null +++ b/rust/lang/security/net/log-neutralization.yaml @@ -0,0 +1,43 @@ +rules: + - id: log-neutralization + languages: + - rust + message: > + This can allow an attacker to forge log entries or inject malicious content into logs. + + Log forging vulnerabilities occur when: + + - Data enters an application from an untrusted source. + - The data is written to an application or system log file. + metadata: + owasp: 'A9: Injection' + cwe: "CWE-117: Improper Output Neutralization for Logs" + references: + - https://cwe.mitre.org/data/definitions/117.html + severity: WARNING + patterns: + - pattern-either: + - pattern: | + match $VAR() { + Err($ERR) => info!(Err($ERR)), + }; + - pattern: | + match $VAR() { + Err($ERR) => debug!(Err($ERR)), + }; + - pattern: | + match $VAR() { + Err($ERR) => println!(Err($ERR)), + }; + - pattern: | + match $VAR() { + Err($ERR) => warn!(Err($ERR)), + }; + - pattern: | + match $VAR() { + Err($ERR) => error!(Err($ERR)), + }; + - pattern: | + match $VAR() { + Err($ERR) => trace!(Err($ERR)), + }; From 33e86e8c4fa5434394398682588065dd9df3e87e Mon Sep 17 00:00:00 2001 From: sxm588 Date: Tue, 26 Sep 2023 21:13:54 -0500 Subject: [PATCH 3/4] cleaning up rules that broke in 1-41-0 --- .../security/audit/exposure-via-error-msg.rs | 3 +- rust/lang/security/injection/mysql-sqli.yaml | 32 +++++++++---------- .../security/injection/resource-injection.rs | 2 +- rust/lang/security/injection/sqlx-sqli.yaml | 32 +++++++++---------- 4 files changed, 35 insertions(+), 34 deletions(-) diff --git a/rust/lang/security/audit/exposure-via-error-msg.rs b/rust/lang/security/audit/exposure-via-error-msg.rs index dfc762ffb4..f2b5967db8 100644 --- a/rust/lang/security/audit/exposure-via-error-msg.rs +++ b/rust/lang/security/audit/exposure-via-error-msg.rs @@ -16,6 +16,7 @@ impl Error for SensitiveError { } // This function returns an error with sensitive information internalized +//ruleid: exposure-error-msg fn sensitive_error() -> Result<(), Box> { let msg = String::from("password: pass1234"); let err = Box::new(SensitiveError { msg }); @@ -28,6 +29,6 @@ fn main() { // NOTE: There is an error in semgrep v0.59.0 which causes function-like // macros not to get correctly identified. - //todoruleid: exposure-error-msg + println!("err = {:?}", err); } diff --git a/rust/lang/security/injection/mysql-sqli.yaml b/rust/lang/security/injection/mysql-sqli.yaml index 8335982ab5..338a01fff6 100644 --- a/rust/lang/security/injection/mysql-sqli.yaml +++ b/rust/lang/security/injection/mysql-sqli.yaml @@ -22,22 +22,22 @@ rules: - patterns: - pattern: | $CONN.query_first($QUERY) - - pattern-either: - - pattern-inside: | - $QUERY = $X + $Y - ... - - pattern-inside: | - $QUERY += $X - ... - - pattern-inside: | - $QUERY = format!($PARAM1, ...) - ... - - pattern-not-inside: | - $QUERY += "..." - ... - - pattern-not-inside: | - $QUERY = "..." + "..." - ... +# - pattern-either: //bug by semgrep https://github.com/returntocorp/semgrep-rules/issues/3128 +# - pattern-inside: | +# $QUERY = $X + $Y +# ... +# - pattern-inside: | +# $QUERY += $X +# ... +# - pattern-inside: | +# $QUERY = format!($PARAM1, ...) +# ... +# - pattern-not-inside: | +# $QUERY += "..." +# ... +# - pattern-not-inside: | +# $QUERY = "..." + "..." +# ... - pattern: $CONN.query_first($X + $Y) - pattern: $CONN.query_first(format!("...", $PARAM1, ...)) diff --git a/rust/lang/security/injection/resource-injection.rs b/rust/lang/security/injection/resource-injection.rs index e3a35e2904..22e5549edf 100644 --- a/rust/lang/security/injection/resource-injection.rs +++ b/rust/lang/security/injection/resource-injection.rs @@ -15,8 +15,8 @@ struct PathParam { } // Blind resource injection from HTTP request query param -#[get("/")] //ruleid: resource-injection +#[get("/")] async fn index(params: web::Query) -> String { let mut path = PathBuf::from(SERVER_ROOT); path.push(params.path.clone()); diff --git a/rust/lang/security/injection/sqlx-sqli.yaml b/rust/lang/security/injection/sqlx-sqli.yaml index 94bfb4c087..d7ca481b40 100644 --- a/rust/lang/security/injection/sqlx-sqli.yaml +++ b/rust/lang/security/injection/sqlx-sqli.yaml @@ -22,22 +22,22 @@ rules: - patterns: - pattern: | sqlx::query!($QUERY) - - pattern-either: - - pattern-inside: | - $QUERY = $X + $Y - ... - - pattern-inside: | - $QUERY += $X - ... - - pattern-inside: | - $QUERY = format!($PARAM1, ...) - ... - - pattern-not-inside: | - $QUERY += "..." - ... - - pattern-not-inside: | - $QUERY = "..." + "..." - ... +# - pattern-either: //bug by semgrep https://github.com/returntocorp/semgrep-rules/issues/3128 +# - pattern-inside: | +# $QUERY = $X + $Y +# ... +# - pattern-inside: | +# $QUERY += $X +# ... +# - pattern-inside: | +# $QUERY = format!($PARAM1, ...) +# ... +# - pattern-not-inside: | +# $QUERY += "..." +# ... +# - pattern-not-inside: | +# $QUERY = "..." + "..." +# ... - pattern: sqlx::query!($X + $Y) - pattern: sqlx::query!(format!("...", $PARAM1, ...)) From 5d9dab01fea3bfb871ed0afe3861c28f1164888a Mon Sep 17 00:00:00 2001 From: sxm588 Date: Wed, 27 Sep 2023 17:52:55 -0500 Subject: [PATCH 4/4] added new rules that actually were not broken with grammar updates --- .../lang/security/audit/Insufficient-protected-credentials.rs | 2 +- rust/lang/security/audit/crypto/use-of-weak-rsa-key.rs | 4 ++-- rust/lang/security/audit/dangerous-command-injection.rs | 2 +- rust/lang/security/injection/xpath-injection.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rust/lang/security/audit/Insufficient-protected-credentials.rs b/rust/lang/security/audit/Insufficient-protected-credentials.rs index 1ecc2163ad..ee82cdde75 100644 --- a/rust/lang/security/audit/Insufficient-protected-credentials.rs +++ b/rust/lang/security/audit/Insufficient-protected-credentials.rs @@ -1,7 +1,7 @@ use postgres::{Connection, TlsMode}; //This works but does it really cover the rule? We need to rethink this one -//todoruleid: insufficient-protected-credentials +//ruleid: insufficient-protected-credentials fn main() { //The following code reads a password and uses the password to connect to a database. let password = requests::get("http://example.org/getPassword").unwrap(); diff --git a/rust/lang/security/audit/crypto/use-of-weak-rsa-key.rs b/rust/lang/security/audit/crypto/use-of-weak-rsa-key.rs index a3c30f6c81..b8e66c70a7 100644 --- a/rust/lang/security/audit/crypto/use-of-weak-rsa-key.rs +++ b/rust/lang/security/audit/crypto/use-of-weak-rsa-key.rs @@ -1,8 +1,8 @@ //id: use-of-weak-rsa-key fn main() { - //todoruleid: use-of-weak-rsa-key + //ruleid: use-of-weak-rsa-key let pvk = openssl::rsa::Rsa::generate(1024); use openssl::rsa::Rsa; - //todoruleid: use-of-weak-rsa-key + //ruleid: use-of-weak-rsa-key let pvk = Rsa::generate(1024); } diff --git a/rust/lang/security/audit/dangerous-command-injection.rs b/rust/lang/security/audit/dangerous-command-injection.rs index 35ccf3fbc8..f235e61853 100644 --- a/rust/lang/security/audit/dangerous-command-injection.rs +++ b/rust/lang/security/audit/dangerous-command-injection.rs @@ -1,6 +1,6 @@ use std::process::Command; -//todoruleid: command-injection +//ruleid: command-injection fn main() { let get_args = requests::get("http://example.org/get").unwrap(); diff --git a/rust/lang/security/injection/xpath-injection.rs b/rust/lang/security/injection/xpath-injection.rs index 582ac12add..1286cc38c7 100644 --- a/rust/lang/security/injection/xpath-injection.rs +++ b/rust/lang/security/injection/xpath-injection.rs @@ -31,7 +31,7 @@ fn get_xml_string() -> String { ) } -//todoruleid: xpath-injection +//ruleid: xpath-injection fn login(username: &str, password: &str) -> Result<(), Box> { let query = format!( "//Employee[UserName/text()='{username}' and Password/text()='{password}']",