# yamllint disable
# This file is an aggregation of SAST rules from multiple sources.
#
# Unless otherwise specified:
# - The aggregation is Copyright 2025 BoostSecurity.io (All Rights Reserved).
# - Each rule retains its original license as indicated in the file headers from the source repositories and is preserved in the metadata license field.
#
# The source of the rules is hosted at: https://github.com/boost-community/sast-rules
#
# Rules licensed under LGPLv3 are included as-is, and the LGPLv3 terms apply only to those specific rules.
# See individual file headers or the source repository for details.
#
# yamllint enable

rules:
  - id: "c_buffer_rule-gets--getts"
    languages:
      - "c"
      - "cpp"
    message: |
      The gets() function reads a line from stdin into the provided buffer
      until either a terminating newline or EOF. This terminating newline or
      EOF is replaced with a null byte `'\0'`. No check for buffer overruns are
      performed so it is recommended to use `fgets()` instead. Do note
      that some platforms will continue reading data after a `'\0'` is encountered.

      Usage of `fgets()` is not recommended for reading binary based files or inputs,
      instead the `read` or `fread` functions should be used.

      For more information please see: https://linux.die.net/man/3/fgets
    metadata:
      shortDescription: "Use of deprecated function (gets)"
      cwe: "CWE-120"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      category: "security"
      license: GPL 2.0 (c) 1989, 1991 Free Software Foundation, Inc.
    pattern-either:
      - pattern: "gets(...)"
      - pattern: "_getts(...)"
      - pattern: "_getws(...)"
    severity: "ERROR"
  - id: "c_buffer_rule-getwd"
    languages:
      - "c"
      - "cpp"
    message: |
      `getwd` does not contain a parameter to limit how many characters can be copied into the
      destination buffer. For portability and security reasons `getwd` has been deprecated in
      favor of `getcwd`.

      For more information please see: https://linux.die.net/man/3/getcwd
    metadata:
      shortDescription: "Insufficient protection against buffer overflow (getwd)"
      cwe: "CWE-120"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      category: "security"
      license: GPL 2.0 (c) 1989, 1991 Free Software Foundation, Inc.
    pattern: "getwd(...)"
    severity: "WARNING"
  - id: "c_buffer_rule-scanf-vscanf"
    languages:
      - "c"
      - "cpp"
    message: |
      Format specifiers can take optional field widths, which should be
      used to limit how many characters are copied into the target buffer.

      For more information please see: https://linux.die.net/man/3/scanf

      Example:
      ```
        char buf[11] = {0};
        scanf("%10s", &buf); // buf = AAAAAAAAAA\0
      ```

      If developing for C Runtime Library (CRT), more secure versions of these functions should be
      used, see:
      https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/scanf-s-scanf-s-l-wscanf-s-wscanf-s-l?view=msvc-170
    metadata:
      shortDescription: "scanf() functions may allow format string based overflows"
      cwe: "CWE-120"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      category: "security"
      license: GPL 2.0 (c) 1989, 1991 Free Software Foundation, Inc.
    pattern-either:
      - patterns:
          - pattern: "scanf($FMT, ...)"
          - pattern-not: "scanf(\"...\", ...)"
      - patterns:
          - pattern: "vscanf($FMT, ...)"
          - pattern-not: "vscanf(\"...\", ...)"
      - patterns:
          - pattern: "wscanf($FMT, ...)"
          - pattern-not: "wscanf(\"...\", ...)"
      - patterns:
          - pattern: "_tscanf($FMT, ...)"
          - pattern-not: "_tscanf(_T(\"...\"), ...)"
      - patterns:
          - pattern: "vwscanf(FMT, ...)"
          - pattern-not: "vwscanf(\"...\", ...)"
    severity: "ERROR"
  - id: "c_crypto_rule-EVP-des-ecb-EVP-des-cbc"
    languages:
      - "c"
      - "cpp"
    message: |
      The DES algorithm has not been recommended for over 15 years and was withdrawn from NIST (FIPS
      46-3) in 2005.

      Consider using libsodium's `crypto_secretbox_easy` authenticated encryption functions instead.

      For more information please see:
       https://libsodium.gitbook.io/doc/secret-key_cryptography/secretbox.

      If you must be FIPS compliant, consider using OpenSSLs AES or 3DES ciphers.
    metadata:
      shortDescription: "Insecure encryption algorithm (DES)"
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      category: "security"
      related-packages: ['pkg:vcpkg/openssl']
      license: GPL 2.0 (c) 1989, 1991 Free Software Foundation, Inc.
    pattern-either:
      - pattern: "EVP_des_ecb(...)"
      - pattern: "EVP_des_cbc(...)"
      - pattern: "EVP_des_cfb(...)"
      - pattern: "EVP_des_ofb(...)"
      - pattern: "EVP_desx_cbc(...)"
    severity: "ERROR"
  - id: "c_crypto_rule-EVP-rc4-40-EVP-rc2-40-cbc"
    languages:
      - "c"
      - "cpp"
    message: |
      The RC4 algorithm is vulnerable to many attacks and should no longer be used for encrypting
      data streams.

      Consider using libsodium's `crypto_secretstream_xchacha20poly1305` stream cipher encryption
      functions instead. For more information please see:
      https://libsodium.gitbook.io/doc/secret-key_cryptography/secretstream

      If you must be FIPS compliant, consider using OpenSSLs AES or 3DES ciphers.
    metadata:
      shortDescription: "Insecure stream cipher (RC4)"
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      category: "security"
      related-packages: ['pkg:vcpkg/openssl']
      license: GPL 2.0 (c) 1989, 1991 Free Software Foundation, Inc.
    pattern-either:
      - pattern: "EVP_rc4_40(...)"
      - pattern: "EVP_rc2_40_cbc(...)"
      - pattern: "EVP_rc2_64_cbc(...)"
    severity: "ERROR"
  - id: "c_crypto_rule-crypt-crypt-r"
    languages:
      - "c"
      - "cpp"
    message: |
      The crypt functions are not recommended due to the significantly small
      key space. Modern hardware can crack crypt produced passwords relatively quickly.

      Consider using the Argon2id password hashing algorithm provided by libsodium.
      For more information please see: https://libsodium.gitbook.io/doc/password_hashing.
    metadata:
      shortDescription: "Insecure hashing algorithm"
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      category: "security"
      license: GPL 2.0 (c) 1989, 1991 Free Software Foundation, Inc.
    pattern-either:
      - pattern: "crypt(...)"
      - pattern: "crypt_r(...)"
    severity: "ERROR"
  - id: "c_format_rule-fprintf-vfprintf"
    languages:
      - "c"
      - "cpp"
    message: |
      Format string vulnerabilities allow an attacker to read or in some cases,
      potentially write data to
      and from locations in the processes' memory. To prevent against format
      string attacks, do not allow
      users or un-validated input to provide the format specification.
      Consider using a constant for the format specification, or only allow specific
      characters to be provided to the format argument for the `fprintf` family of functions.

      For more information please see: https://linux.die.net/man/3/fprintf

      For more information on format string attacks please see OWASP's attack
      guide: https://owasp.org/www-community/attacks/Format_string_attack
    metadata:
      shortDescription: "Potential format string vulnerability"
      cwe: "CWE-134"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      category: "security"
      license: GPL 2.0 (c) 1989, 1991 Free Software Foundation, Inc.
    pattern-either:
      - patterns:
          - pattern: "fprintf($FD, $FMT, ...)"
          - pattern-not: "fprintf($FD, \"...\", ...)"
      - patterns:
          - pattern: "vfprintf($FMT, $ARGS, ...)"
          - pattern-not: "vfprintf(\"...\", $ARGS, ...)"
      - patterns:
          - pattern: "_ftprintf($FD, $FMT, ...)"
          - pattern-not: "_ftprintf($FD, \"...\", ...)"
      - patterns:
          - pattern: "fwprintf($FD, $FMT, ...)"
          - pattern-not: "fwprintf($FD, \"...\", ...)"
      - patterns:
          - pattern: "fvwprintf($FD, $FMT, ...)"
          - pattern-not: "fvwprintf($FD, \"...\", ...)"
    severity: "ERROR"
  - id: "c_format_rule-printf-vprintf"
    languages:
      - "c"
      - "cpp"
    message: |
      Format string vulnerabilities allow an attacker to read or in some cases, potentially write
      data to
      and from locations in the processes' memory. To prevent against format string attacks, do not
      allow
      users or un-validated input to provide the format specification.
      Consider using a constant for the format specification, or only allow specific
      characters to be provided to the format argument for the `printf` family of functions.

      For more information please see: https://linux.die.net/man/3/fprintf

      For more information on format string attacks please see OWASP's attack guide:
      https://owasp.org/www-community/attacks/Format_string_attack
    metadata:
      shortDescription: "Potential format string vulnerability"
      cwe: "CWE-134"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      category: "security"
      license: GPL 2.0 (c) 1989, 1991 Free Software Foundation, Inc.
    pattern-either:
      - patterns:
          - pattern: "printf(...)"
          - pattern-not: "printf(\"...\",...)"
      - patterns:
          - pattern: "vprintf($FMT, ...)"
          - pattern-not: "vprintf(\"...\", ...)"
      - patterns:
          - pattern: "vwprintf($FMT, ...)"
          - pattern-not: "vwprintf(\"...\", ...)"
      - patterns:
          - pattern: "vfwprintf($FILE, $FMT, ...)"
          - pattern-not: "vfwprintf($FILE, \"...\", ...)"
      - patterns:
          - pattern: "_vtprintf($FILE, $FMT, ...)"
          - pattern-not: "_vtprintf($FILE, \"...\", ...)"
      - patterns:
          - pattern: "wprintf($FMT, ...)"
          - pattern-not: "wprintf(\"...\", ...)"
    severity: "ERROR"
  - id: "c_format_rule-snprintf-vsnprintf"
    languages:
      - "c"
      - "cpp"
    message: |
      Format string vulnerabilities allow an attacker to read or in some cases, potentially write
      data to
      and from locations in the processes' memory. To prevent against format string attacks, do not
      allow
      users or un-validated input to provide the format specification.
      Consider using a constant for the format specification, or strip all format
      specifiers from the input prior to calling the `snprintf` family of functions.

      Note that some variations of this function do not always null terminate the strings.

      For more information on using snprintf please see: https://linux.die.net/man/3/snprintf

      For more information on format string attacks please see OWASP's attack guide:
      https://owasp.org/www-community/attacks/Format_string_attack
    metadata:
      shortDescription: "Potential format string vulnerability"
      cwe: "CWE-134"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      category: "security"
      license: GPL 2.0 (c) 1989, 1991 Free Software Foundation, Inc.
    pattern-either:
      - patterns:
          - pattern: "snprintf($BUF,$SIZE,$FMT,...)"
          - pattern-not: "snprintf($BUF,$SIZE,\"...\",...)"
      - patterns:
          - pattern: "vsnprintf($BUF,$SIZE,$FMT)"
          - pattern-not: "vsnprintf($BUF,$SIZE,\"...\",...)"
      - patterns:
          - pattern: "_snprintf($BUF,$SIZE,$FMT,...)"
          - pattern-not: "_snprintf($BUF,$SIZE,$FMT,\"...\",...)"
      - patterns:
          - pattern: "_sntprintf($VAR,$FMT,...)"
          - pattern-not: "sntprintf($VAR,\"...\",...)"
      - patterns:
          - pattern: "_vsntprintf($VAR,$FMT,...)"
          - pattern-not: "_vsntprintf($VAR,\"...\",...)"
    severity: "ERROR"
  - id: "c_format_rule-syslog"
    languages:
      - "c"
      - "cpp"
    message: |
      Format string vulnerabilities allow an attacker to read or in some cases, potentially write
      data to
      and from locations in the processes' memory. To prevent against format string attacks, do not
      allow
      users or un-validated input to provide the format specification.
      Consider using a constant for the format specification, or strip all format
      specifiers from the input prior to calling the `syslog` function.

      For more information please see: https://capec.mitre.org/data/definitions/67.html
    metadata:
      shortDescription: "Potential format string vulnerability in syslog call"
      cwe: "CWE-134"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      category: "security"
      license: GPL 2.0 (c) 1989, 1991 Free Software Foundation, Inc.
    pattern-either:
      - patterns:
          - pattern: "syslog($FUNC,...)"
          - pattern-not: "syslog($FUNC,\"...\",...)"
    severity: "ERROR"
  - id: "c_shell_rule-CreateProcess"
    languages:
      - "c"
      - "cpp"
    message: |
      Due to how `CreateProcess` parses spaces, an attacker may be able to exploit this function
      by creating a binary with the same name that is loaded first, depending on the search path
      order.

      Ensure that quotation marks around the executable path are used, such as:
      ```
      CreateProcessA(NULL, "\"C:\\Program Files\\MyApp.exe\"", ...)
      ```
      For more information, please see MSDNs documentation at:
      https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa#security-remarks
    metadata:
      shortDescription: "Possible executable path hijacking (CreateProcess)"
      cwe: "CWE-78"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      category: "security"
      license: GPL 2.0 (c) 1989, 1991 Free Software Foundation, Inc.
    pattern: "CreateProcess(...)"
    severity: "WARNING"
  - id: "c_shell_rule-CreateProcessAsUser-CreateProcessWithLogon"
    languages:
      - "c"
      - "cpp"
    message: |
      Due to how `CreateProcess` parses spaces, an attacker may be able to exploit this function
      by creating a binary with the same name that is loaded first, depending on the search path
      order.

      Ensure that quotation marks around the executable path are used, such as:
      ```
      CreateProcessAsUser(hToken, NULL, "\"C:\\Program Files\\MyApp.exe\"", ...)
      ```
      For more information, please see MSDNs documentation at:
      https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera#security-remarks
    metadata:
      shortDescription: "Possible executable path hijacking (CreateProcessAsUser/CreateProcessWithLogon)"
      cwe: "CWE-78"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      category: "security"
      license: GPL 2.0 (c) 1989, 1991 Free Software Foundation, Inc.
    pattern-either:
      - pattern: "CreateProcessAsUser(...)"
      - pattern: "CreateProcessWithLogon(...)"
    severity: "WARNING"
  - id: "c_shell_rule-execl-execlp"
    languages:
      - "c"
      - "cpp"
    message: |
      It is generally not recommended to call out to the operating system to execute commands.
      When the application is executing file system based commands, user input should never be used
      in
      constructing commands or command arguments. If possible, determine if a library can be used
      instead to provide the same functionality. Otherwise, consider hard coding both the command
      and arguments to be used, or at the very least restricting which arguments can be passed
      to the command execution function.

      Please see the compliant solutions in the following page:
      https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152177
    metadata:
      shortDescription: "Potential for OS command injection"
      cwe: "CWE-78"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      category: "security"
      license: GPL 2.0 (c) 1989, 1991 Free Software Foundation, Inc.
    pattern-either:
      - pattern: "execl(...)"
      - pattern: "execlp(...)"
      - pattern: "execle(...)"
      - pattern: "execv(...)"
      - pattern: "execvp(...)"
      - pattern: "popen(...)"
      - pattern: "WinExec(...)"
      - pattern: "ShellExecute(...)"
    severity: "ERROR"
  - id: "c_shell_rule-system"
    languages:
      - "c"
      - "cpp"
    message: |
      It is generally not recommended to call out to the operating system to execute commands.
      When the application is executing file system based commands, user input should never be used
      in
      constructing commands or command arguments. If possible, determine if a library can be used
      instead to provide the same functionality. Otherwise, consider hard coding both the command
      and arguments to be used, or at the very least restricting which arguments can be passed
      to the command execution function.

      For more information please see:
      https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152177
    metadata:
      shortDescription: "Potential for OS command injection"
      cwe: "CWE-78"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      category: "security"
      license: GPL 2.0 (c) 1989, 1991 Free Software Foundation, Inc.
    pattern: "system(...)"
    severity: "ERROR"
  - id: "c_tmpfile_rule-mktemp"
    languages:
      - "c"
      - "cpp"
    message: |
      The `mktemp` function should no longer be used due to multiple flaws. Some implementations
      created random files by using known information like the process ID and a single letter. This
      allows for possible race conditions where an attacker could guess or manipulate these files
      prior to them being used.

      Consider using the `mkstemp` function instead, but be aware it also contains possible
      risks. Ensure the process has called the `umask` function with restricted permissions prior
      to calling `mkstemp` and validate the permissions prior to using the file descriptor.

      For more information on temporary files please see:
      https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152425
    metadata:
      shortDescription: "Use of deprecated function (mktemp)"
      cwe: "CWE-377"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Medium"
      category: "security"
      license: GPL 2.0 (c) 1989, 1991 Free Software Foundation, Inc.
    pattern: "mktemp(...)"
    severity: "ERROR"
  - id: "c_tmpfile_rule-tmpnam-tempnam"
    languages:
      - "c"
      - "cpp"
    message: |
      There exists a possible race condition in between the time that `tempnam` or `tmpnam`
      returns a pathname, and the time that the program opens it, another program might create
      that pathname using `open`, or create it as a symbolic link.

      Consider using the `mkstemp` function instead, but be aware it also contains possible
      risks. Ensure the process has called the `umask` function with restricted permissions prior
      to calling `mkstemp` and validate the permissions prior to using the file descriptor.

      For more information on temporary files please see:
      https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152425
    metadata:
      shortDescription: "Potential time of check time of use vulnerability (tmpnam/tempnam)"
      cwe: "CWE-377"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Medium"
      category: "security"
      license: GPL 2.0 (c) 1989, 1991 Free Software Foundation, Inc.
    pattern-either:
      - pattern: "tmpnam(...)"
      - pattern: "tempnam(...)"
    severity: "WARNING"
  - id: "csharp_cookies_rule-CookieWithoutHttpOnlyFlag"
    languages:
      - "csharp"
    mode: "taint"
    pattern-sources:
      - pattern-either:
          - pattern: |
              var $COOKIE = new HttpCookie(...);
          - pattern: |
              var $COOKIE = new Cookie(...);
    pattern-sinks:
      - pattern: "$COOKIE"
    pattern-sanitizers:
      - pattern: "$COOKIE.HttpOnly = true;"
    message: |
      The `HttpOnly` attribute when set to `true` protects the cookie value from being accessed by
      client side JavaScript such
      as reading the `document.cookie` values. By enabling this protection, a website that is
      vulnerable to
      Cross-Site Scripting (XSS) will be able to block malicious scripts from accessing the cookie
      value from JavaScript.

      Example of protecting an HttpCookie:
      ```
      // Create an HttpOnly cookie.
      HttpCookie someCookie = new HttpCookie("SomeCookieName", "SomeValue");
      someCookie.HttpOnly = true;
      ```

      For more information see:
      https://learn.microsoft.com/en-us/dotnet/api/system.web.httpcookie.httponly

      Session cookies should be configured with the following security directives:

      - [HTTPOnly](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
      - [Secure](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
      - [SameSite](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite)
    severity: "WARNING"
    metadata:
      shortDescription: "Sensitive cookie without 'HttpOnly' flag"
      category: "security"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-1004"
      security-severity: "Low"
      license: MIT (c) GitLab Inc.
  - id: "csharp_cookies_rule-CookieWithoutSSLFlag"
    languages:
      - "csharp"
    mode: "taint"
    pattern-sources:
      - pattern: |
          var $COOKIE = new HttpCookie(...);
    pattern-sinks:
      - pattern: "$COOKIE"
    pattern-sanitizers:
      - pattern: "$COOKIE.Secure = true;"
    message: |
      The `Secure` attribute when set to `true` protects the cookie value from being being
      transmitted over clear text
      communication paths such as HTTP. By enabling this protection, the cookie will only be sent
      over HTTPS.

      Example of protecting an HttpCookie:
      ```
      // Create an HttpOnly cookie.
      HttpCookie someCookie = new HttpCookie("SomeCookieName", "SomeValue");
      someCookie.Secure = true;
      ```

      For more information see:
      https://learn.microsoft.com/en-us/dotnet/api/system.web.httpcookie.secure

      Session cookies should be configured with the following security directives:

      - [HTTPOnly](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
      - [SameSite](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite)
      - [Secure](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
    severity: "WARNING"
    metadata:
      shortDescription: "Sensitive cookie in HTTPS session without 'Secure' attribute"
      category: "security"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-614"
      security-severity: "Low"
      license: MIT (c) GitLab Inc.
  - id: "csharp_crypto_rule-CertificateValidationDisabled"
    languages:
      - "csharp"
    patterns:
      - pattern-inside: |
          using System.Net;
          ...
      - pattern: "ServicePointManager.ServerCertificateValidationCallback += $CALLBACK;"
      - metavariable-pattern:
          metavariable: "$CALLBACK"
          patterns:
            - pattern-either:
                - pattern: "$RETURNTYPE $FUNC(...) { return true; }"
                - pattern: "(...) => true;"
    message: |
      The `ServicePointManager.ServerCertificateValidationCallback` event has been set
      to always return `true`, which effectively disables the validation of server
      certificates.

      This allows for an adversary who is in between the application and the target host to intercept
      potentially sensitive information or transmit malicious data.

      Remove the callback function that is returning true to allow normal certificate validation to
      proceed.
      When no callback is provided, the client will validate that the certificate name matches the
      hostname
      that was used when creating the request.

      For more information on the `ServerCertificateValidationCallback` property see:
      https://learn.microsoft.com/en-us/dotnet/api/system.net.servicepointmanager.servercertificatevalidationcallback
    severity: "WARNING"
    metadata:
      shortDescription: "Certificate validation disabled"
      category: "security"
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      cwe: "CWE-295"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
  - id: "csharp_crypto_rule-WeakCipherAlgorithm"
    patterns:
      - pattern-inside: |
          using System.Security.Cryptography;
          ...
      - pattern-either:
          - pattern-regex: ".*DES\\.Create\\(\\);"
          - pattern: "new DESCryptoServiceProvider();"
          - pattern-regex: ".*TripleDES\\.Create\\(\\);"
          - pattern: "new TripleDESCryptoServiceProvider();"
          - pattern-regex: ".*RC2\\.Create\\(\\);"
          - pattern: "new RC2CryptoServiceProvider();"
    message: |
      DES, TripleDES and RC2 are all considered broken or insecure cryptographic algorithms.
      If using .NET Framework greater than version 6.0 consider using `ChaCha20Poly1305`
      instead as it is easier and faster than the alternatives such as `AES-256-GCM`.

      For older applications, `AES-256-GCM` is recommended, however it has many drawbacks:
      - Slower than `ChaCha20Poly1305`.
      - Catastrophic failure if nonce values are reused.

      Example using `ChaCha20Poly1305`:
      ```
      // Generate a random key
      byte[] key = new byte[32];
      RandomNumberGenerator.Fill(key);

      // Note nonce values _must_ be regenerated every time they are used.
      byte[] nonce = new byte[12];
      RandomNumberGenerator.Fill(nonce);

      byte[] authTag = new byte[16];
      byte[] cipherText;

      using (ChaCha20Poly1305 encryptor = new ChaCha20Poly1305(key))
      {
          byte[] plainText = System.Text.Encoding.UTF8.GetBytes("Secret text to encrypt");
          cipherText = new byte[plainText.Length];
          encryptor.Encrypt(nonce, plainText, cipherText, authTag);
      }

      using (ChaCha20Poly1305 decryptor = new ChaCha20Poly1305(key))
      {
          byte[] output = new byte[cipherText.Length];
          decryptor.Decrypt(nonce, cipherText, authTag, output);
          Console.WriteLine("Output: {0}", System.Text.Encoding.UTF8.GetString(output));
      }
      ```

      Example using `AES-256-GCM`:
      ```
      // Generate a random key
      byte[] key = new byte[32];
      RandomNumberGenerator.Fill(key);

      // Note nonce values _must_ be regenerated every time they are used.
      byte[] nonce = new byte[AesGcm.NonceByteSizes.MaxSize];
      RandomNumberGenerator.Fill(nonce);

      byte[] authTag = new byte[AesGcm.TagByteSizes.MaxSize];
      byte[] cipherText;

      using (AesGcm encryptor = new AesGcm(key))
      {
          byte[] plainText = Encoding.UTF8.GetBytes("Secret text to encrypt");
          cipherText = new byte[plainText.Length];
          encryptor.Encrypt(nonce, plainText, cipherText, authTag);
      }

      using (AesGcm decryptor = new AesGcm(key))
      {
          byte[] output = new byte[cipherText.Length];
          decryptor.Decrypt(nonce, cipherText, authTag, output);
          Console.WriteLine("Output: {0}",  Encoding.UTF8.GetString(output));
      }
      ```
    languages:
      - "csharp"
    severity: "WARNING"
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      category: "security"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-327"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
  - id: "csharp_crypto_rule-WeakCipherMode"
    languages:
      - "csharp"
    patterns:
      - pattern-inside: |
          using System.Security.Cryptography;
          ...
      - metavariable-regex:
          metavariable: "$CIPHER"
          regex: "^(ECB|CBC|OFB|CFB|CTS)$"
      - pattern: "CipherMode.$CIPHER"
    message: |
      Cryptographic algorithms provide many different modes of operation, only some of which provide
      message integrity. Without message integrity it could be possible for an adversary to attempt
      to tamper with the ciphertext which could lead to compromising the encryption key. Newer
      algorithms
      apply message integrity to validate ciphertext has not been tampered with.

      Instead of using an algorithm that requires configuring a `CipherMode`, an algorithm
      that has built-in message integrity should be used. If using .NET Framework greater
      than version 6.0 consider using `ChaCha20Poly1305` or `AES-256-GCM`.

      For older applications, `AES-256-GCM` is recommended, however it has many drawbacks:
      - Slower than `ChaCha20Poly1305`.
      - Catastrophic failure if nonce values are re-used.

      Example using `ChaCha20Poly1305`:
      ```
      // Generate a random key
      byte[] key = new byte[32];
      RandomNumberGenerator.Fill(key);

      ChaCha20Poly1305 encryptor = new ChaCha20Poly1305(key);

      // Note nonce values _must_ be regenerated every time they are used.
      var nonce = new byte[12];
      RandomNumberGenerator.Fill(nonce);

      byte[] plainText = System.Text.Encoding.UTF8.GetBytes("Secret text to encrypt");
      byte[] cipherText = new byte[plainText.Length];
      var authTag = new byte[16];

      encryptor.Encrypt(nonce, plainText, cipherText, authTag);
      byte[] output = new byte[cipherText.Length];
      encryptor.Decrypt(nonce, cipherText, authTag, output);
      Console.WriteLine("Output: {0}", System.Text.Encoding.UTF8.GetString(output));
      ```

      Example using `AES-256-GCM`:
      ```
      var plaintextBytes = Encoding.UTF8.GetBytes("Secret text to encrypt");
      var key = new byte[32];
      RandomNumberGenerator.Fill(key);

      using var aes = new AesGcm(key);
      var nonce = new byte[AesGcm.NonceByteSizes.MaxSize];
      RandomNumberGenerator.Fill(nonce);

      var cipherText = new byte[plaintextBytes.Length];
      var tag = new byte[AesGcm.TagByteSizes.MaxSize];

      aes.Encrypt(nonce, plaintextBytes, cipherText, tag);

      // Decrypt
      using (var decrypt = new AesGcm(key))
      {
          var decryptedBytes = new byte[cipherText.Length];

          decrypt.Decrypt(nonce, cipherText, tag, decryptedBytes);

          Console.WriteLine("Decrypted: {0}",  Encoding.UTF8.GetString(decryptedBytes));
      }
      ```
    severity: "WARNING"
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      category: "security"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-327"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
  - id: "csharp_crypto_rule-WeakHashingFunction"
    languages:
      - "csharp"
    patterns:
      - pattern-either:
          - patterns:
              - metavariable-regex:
                  metavariable: "$HASH_PROVIDER"
                  regex: "^(SHA1CryptoServiceProvider|MD5CryptoServiceProvider)$"
              - pattern: "new $HASH_PROVIDER"
          - patterns:
              - metavariable-regex:
                  metavariable: "$HASH_CLASS"
                  regex: "^System.Security.Cryptography.(SHA1|MD5)$"
              - pattern: "$HASH_CLASS.$METHOD();"
    message: |
      Both MD5 and SHA1 hash algorithms have been found to be vulnerable to producing collisions.
      This means
      that two different values, when hashed, can lead to the same hash value. If the application is
      trying
      to use these hash methods for storing passwords, then it is recommended to switch to a
      password hashing
      algorithm such as Argon2id or PBKDF2. Currently there is no vetted Argon2id implementation for
      C# so
      it is recommended that PBKDF2 be used until one is available.

      Example using PBKDF2 to generate and compare passwords:
      ```
      const int SaltSize = 24;
      const int HashSize = 24;
      // number of pbkdf2 iterations, Rfc2898DeriveBytes uses hmac-sha1
      // so set a high iteration count
      const int Iterations = 1_300_000;
      byte[] salt = new byte[SaltSize];
      RandomNumberGenerator.Fill(salt);

      Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes("some password", salt, Iterations);
      byte[] hashBytes = pbkdf2.GetBytes(HashSize);
      // Store salt and hashedBytes in a data store such as database for authentication
      Console.WriteLine("Hash {0}", BitConverter.ToString(hashBytes).Replace("-", ""));
      // Do a constant time comparison as to not leak data based on timing
      if (CryptographicOperations.FixedTimeEquals(hashBytes, hashBytes)) {
          Console.WriteLine("hashes are equal");
      }
      ```
      For more information on PBKDF2 see:
      https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.rfc2898derivebytes

      For more information on secure password storage see OWASP:
      https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
    severity: "WARNING"
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm (SHA1/MD5)"
      category: "security"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-327"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
  - id: "csharp_crypto_rule-WeakRNG"
    languages:
      - "csharp"
    patterns:
      - pattern: "(Random $RNG).$METHOD(...);"
      - focus-metavariable: "$RNG"
    message: |
      Depending on the context, generating weak random numbers may expose cryptographic functions
      which rely on these numbers to be exploitable. When generating numbers for sensitive values
      such as tokens, nonces, and cryptographic keys, it is recommended that the
      `RandomNumberGenerator` class be used.

      Example `RandomNumberGenerator` usage:
      ```
      Int32 randInt = RandomNumberGenerator.GetInt32(32000);
      byte[] randomBytes = new byte[64];
      RandomNumberGenerator.Fill(randomBytes);
      Console.WriteLine("Random Int32: {0}", randInt);
      Console.WriteLine("Random Bytes: {0}", BitConverter.ToString(randomBytes).Replace("-", ""));
      ```

      For more information see:
      https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.randomnumbergenerator
    severity: "WARNING"
    metadata:
      shortDescription: "Use of cryptographically weak Pseudo-Random Number Generator (PRNG)"
      category: "security"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-338"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
  - id: "csharp_csrf_rule-Csrf"
    languages:
      - "csharp"
    patterns:
      - pattern: |
          [$HTTPMETHOD]
          public $RET $FOO(...) {
            ...
          }
      - pattern-not: |
          [ValidateAntiForgeryToken]
          public $RET $FOO(...) {
            ...
          }
      - pattern-not-inside: |
          [AutoValidateAntiforgeryToken]
          class $CLASS{
            ...
          }
      - pattern-not-inside: |
          [ValidateAntiForgeryToken]
          class $CLASS{
            ...
          }
      - metavariable-regex:
          metavariable: "$HTTPMETHOD"
          regex: "Http(Post|Delete|Patch|Put)"
    message: |
      The application failed to protect against Cross-Site Request Forgery (CSRF)
      due to not including the `[ValidateAntiForgeryToken]` attribute on an
      HTTP method handler that could change user state (usually in the form of POST or PUT
      methods).

      The vulnerability can be exploited by an adversary creating a link or form on a third
      party site and tricking an authenticated victim to access them.

      Add the `[ValidateAntiForgeryToken]` to all methods which take in user data and change
      user state (such as updating a database with a new value). This is especially true for
      functionality such as updating passwords or other security sensitive functions.

      Alternatively, applications can enable a global
      [AutoValidateAntiforgeryTokenAttribute](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.autovalidateantiforgerytokenattribute)
      filter.

      For more information on ValidateAntiForgeryToken and other CSRF protections in .NET
      see the following URL:
      https://learn.microsoft.com/en-us/aspnet/core/security/anti-request-forgery

      Additionally, consider setting all session cookies to have the `SameSite=Strict` attribute.
      It should be noted that this may impact usability when sharing links across other mediums.
      It is recommended that a two cookie based approach is taken, as outlined in the
      [Top level
      navigations](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-08#section-8.8.2)
      section
      of the SameSite RFC.

      For more information on CSRF see OWASP's guide:
      https://owasp.org/www-community/attacks/csrf
    severity: "WARNING"
    metadata:
      shortDescription: "Potential Cross-Site Request Forgery (CSRF)"
      category: "security"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      cwe: "CWE-352"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
  - id: "csharp_deserialization_rule-InsecureDeserialization"
    mode: "taint"
    pattern-sources:
      - pattern: "Request.Cookies[...]"
      - pattern: "Request.Cookies.Get(...)"
      - pattern: "Request.Form[...]"
      - pattern: "Request.Form.Get(...)"
      - pattern: "Request.Headers[...]"
      - pattern: "Request.Headers.Get(...)"
      - pattern: "Request.QueryString[...]"
      - pattern: "Request.QueryString.Get(...)"
      - pattern: "Request.Body"
      - pattern: "$CTX.Request.Cookies[...]"
      - pattern: "$CTX.Request.Cookies.Get(...)"
      - pattern: "$CTX.Request.Form[...]"
      - pattern: "$CTX.Request.Form.Get(...)"
      - pattern: "$CTX.Request.Headers[...]"
      - pattern: "$CTX.Request.Headers.Get(...)"
      - pattern: "$CTX.Request.QueryString[...]"
      - pattern: "$CTX.Request.QueryString.Get(...)"
      - pattern: "$CTX.Request.Body"
      - pattern: "System.IO.File.ReadAllText(...)"
      - pattern: "System.IO.File.ReadAllTextAsync(...)"
      - pattern: "System.IO.File.ReadAllLines(...)"
      - pattern: "System.IO.File.ReadAllLinesAsync(...)"
      - pattern: "System.IO.File.ReadAllBytes(...)"
      - pattern: "System.IO.File.ReadAllBytesAsync(...)"
      - pattern: "System.IO.File.ReadLines(...)"
      - pattern: "System.IO.File.ReadLinesAsync(...)"
      - pattern: "System.Environment.GetEnvironmentVariable(...)"
    pattern-sinks:
      - pattern: "(System.Runtime.Serialization.Formatters.Binary.BinaryFormatter $OBJ).Deserialize(...)"
      - pattern: "(System.Runtime.Serialization.Formatters.Binary.BinaryFormatter $OBJ).UnsafeDeserialize(...)"
      - pattern: "(System.Runtime.Serialization.Formatters.Binary.BinaryFormatter $OBJ).UnsafeDeserializeMethod(...)"
      - pattern: "(System.Runtime.Serialization.Formatters.Soap.SoapFormatter $OBJ).Deserialize(...)"
      - pattern: "(System.Runtime.Serialization.NetDataContractSerializer $OBJ).Deserialize(...)"
      - pattern: "(System.Web.UI.LosFormatter $OBJ).Deserialize(...)"
    languages:
      - "csharp"
    message: |
      Deserialization attacks exploit the process of reading serialized data and turning it back into an
      object. By constructing malicious objects and serializing them, an adversary may attempt to:

      - Inject code that is executed upon object construction, which occurs during the deserialization process.
      - Exploit mass assignment by including fields that are not normally a part of the serialized data but are
        read in during deserialization.

      Microsoft recommends no longer using the following serialization formats:

      - BinaryFormatter
      - SoapFormatter
      - NetDataContractSerializer
      - LosFormatter
      - ObjectStateFormatter

      Consider safer alternatives such as serializing data in the JSON format. Ensure any format chosen allows
      the application to specify exactly which object types are allowed to be deserialized. Additionally, when
      deserializing, never deserialize to base object types like `Object` and only cast to the exact object
      type that is expected.

      To protect against mass assignment, only allow deserialization of the specific fields that are required.
      If this is not easily done, consider creating an intermediary type that can be serialized with only the
      necessary fields exposed.

      For more information see Microsoft's deserialization security guide:
      https://learn.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide

      For more details on deserialization attacks in general, see OWASP's guide:
      https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html

      It should be noted that [tools exist](https://github.com/pwntester/ysoserial.net) to automatically create
      exploit code for these vulnerabilities.
    metadata:
      shortDescription: "Deserialization of potentially untrusted data"
      category: "security"
      cwe: "CWE-502"
      owasp:
        - "A8:2017-Insecure Deserialization"
        - "A08:2021-Software and Data Integrity Failures"
      security-severity: "High"
      related-packages: ['pkg:nuget/system.runtime.serialization.formatters', 'pkg:nuget/system.text.json']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "csharp_endpoint_rule-UnvalidatedRedirect"
    languages:
      - "csharp"
    mode: "taint"
    pattern-sources:
      - patterns:
          - pattern: "$SRC"
          - pattern-inside: |
              public $RET $FUNC(...,$SRC,...){...}
      - patterns:
          - pattern: "$SRC"
          - pattern-inside: |
              if (Uri.TryCreate(..., ..., $SRC)){
              ...
              }
    pattern-sanitizers:
      - pattern: "Url.Action(...)"
      - pattern: "Url.HttpRouteUrl(...)"
      - pattern: "Url.RouteUrl(...)"
    pattern-sinks:
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern-either:
                      - pattern: "return $METHOD(...)"
                      - pattern: "return new $METHOD(...)"
                  - pattern: |
                      if (!Url.IsLocalUrl(...)) {
                          ...
                          return $METHOD(...)
                      }
              - patterns:
                  - pattern-not-inside: |
                      if (Url.IsLocalUrl(...)) {
                          ...
                      }
                  - pattern-not-inside: |
                      if (!Url.IsLocalUrl(...)) {
                          return $X
                      }
                      ...
                  - pattern-either:
                      - pattern: "$METHOD(...)"
                      - pattern: "new $METHOD(...)"
                  - metavariable-pattern:
                      metavariable: "$METHOD"
                      pattern-either:
                        - pattern: "Redirect"
                        - pattern: "RedirectPermanent"
                        - pattern: "RedirectToRoute"
                        - pattern: "RedirectToRoutePermanent"
                        - pattern: "RedirectResult"
    message: |
      The application may allow open redirects if created using user supplied input. Open redirects
      are
      commonly
      abused in phishing attacks where the original domain or URL looks like a legitimate link, but
      then
      redirects a user to a malicious site. An example would be
      `https://example.com/redirect?url=https://%62%61%64%2e%63%6f%6d%2f%66%61%6b%65%6c%6f%67%69%6e`
      which,
      when decoded, turns into `bad.com/fakelogin`.

      Never redirect a client based on user input. It is recommended that the list of target links
      to
      redirect a user to are contained server side, and retrieved using a numerical value
      as an index to return the link to be redirected to. For example, `/redirect?id=1` would cause
      the
      application to look up the `1` index and return a URL such as `https://example.com`. This URL
      would
      then be used to redirect the user, using the 301 response code and `Location` header.

      For more information on open redirects see OWASP's guide:
      https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html
    metadata:
      shortDescription: "URL redirection to untrusted site 'open redirect'"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-601"
      security-severity: "Info"
      related-packages: ['pkg:nuget/microsoft.aspnetcore.mvc']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "csharp_injection_rule-CommandInjection"
    languages:
      - "csharp"
    patterns:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  ...
                  (System.Diagnostics.Process $PROC).Start(...);
              - pattern-either:
                  - patterns:
                      - pattern: "(System.Diagnostics.Process $PROC).StartInfo.FileName = <...$ARG...>;"
                      - pattern-not: "(System.Diagnostics.Process $PROC).StartInfo.FileName = \"...\";"
                  - patterns:
                      - pattern: "(System.Diagnostics.Process $PROC).StartInfo.Arguments = <...$ARG...>;"
                      - pattern-not: "(System.Diagnostics.Process $PROC).StartInfo.Arguments = \"...\";"
          - patterns:
              - pattern: "System.Diagnostics.Process.Start($ARG);"
              - pattern-not: "System.Diagnostics.Process.Start(\"...\");"
          - patterns:
              - pattern-not: "$PSINFO.Arguments = \"...\";"
              - pattern-not: "$PSINFO.FileName = \"...\";"
              - pattern-not: "new System.Diagnostics.ProcessStartInfo(\"...\");"
              - pattern-not: "new System.Diagnostics.ProcessStartInfo();"
              - pattern-either:
                  - pattern: "new System.Diagnostics.ProcessStartInfo(...);"
                  - pattern: "$PSINFO.Arguments = <...$ARG...>;"
                  - pattern: "$PSINFO.FileName = <...$ARG...>;"
          - patterns:
              - pattern-inside: |
                  new System.Diagnostics.ProcessStartInfo{
                  ...
                  }
              - pattern-not: "Arguments = \"...\""
              - pattern-not: "FileName = \"...\""
              - pattern-either:
                  - pattern: "Arguments = ..."
                  - pattern: "FileName = ..."
    message: |
      OS command injection is a critical vulnerability that can lead to a full system
      compromise as it may allow an adversary to pass in arbitrary commands or arguments
      to be executed.

      User input should never be used in constructing commands or command arguments
      to functions which execute OS commands. This includes filenames supplied by
      user uploads or downloads.

      Ensure your application does not:

      - Use user-supplied information in the process name to execute.
      - Use user-supplied information in an OS command execution function which does
      not escape shell meta-characters.
      - Use user-supplied information in arguments to OS commands.

      The application should have a hardcoded set of arguments that are to be passed
      to OS commands. If filenames are being passed to these functions, it is
      recommended that a hash of the filename be used instead, or some other unique
      identifier. It is strongly recommended that a native library that implements
      the same functionality be used instead of using OS system commands, due to the
      risk of unknown attacks against third party commands.

      When specifying the OS command, ensure the application uses the full path
      information, otherwise the OS may attempt to look up which process to execute
      and could be vulnerable to untrusted search path vulnerabilities (CWE-426).

      Example of safely executing an OS command:
      ```
      public void ExecuteCommand(string userFileData) {
          // generate a random filename, do not using user input
          string fileName = "C:\\Temp\\" + Guid.NewGuid();
          File.WriteAllText(fileName, userFileData);

          using (Process process = new Process())
          {
              // hardcode the full process path
              ProcessStartInfo processInfo = new ProcessStartInfo("C:\\App\\FileReader.exe");
              // only pass in trust arguments, and never direct user input.
              processInfo.Arguments = fileName;
              processInfo.UseShellExecute = false;
              process.StartInfo = processInfo;
              process.Start();
          }
      }
      ```

      For more information on OS command injection, see OWASP's guide:
      https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html
    severity: "ERROR"
    metadata:
      shortDescription: "Improper neutralization of special elements used in an OS command ('OS Command Injection')"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-78"
      security-severity: "High"
      related-packages: ['pkg:nuget/system.diagnostics.process']
      license: MIT (c) GitLab Inc.
  - id: "csharp_injection_rule-LdapInjection"
    languages:
      - "csharp"
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-inside: |
              $RET $METHOD(...,$VAR,...){...}
          - pattern: "$VAR"
    pattern-sinks:
      - pattern: "System.DirectoryServices.AccountManagement.UserPrincipal.FindByIdentity(...)"
      - pattern: "new System.DirectoryServices.DirectoryEntry(...)"
      - pattern: "new System.DirectoryServices.DirectorySearcher(...)"
      - pattern: "new System.DirectoryServices.Protocols.SearchRequest(...)"
      - patterns:
          - focus-metavariable: $TAINTED
          - pattern-either:
              - pattern: "(System.DirectoryServices.DirectoryEntry $SOURCE).Path = $TAINTED"
              - pattern: "(System.DirectoryServices.DirectorySearcher $SEARCHER).Filter = $TAINTED"
              - pattern: "(System.DirectoryServices.Protocols.SearchRequest $SEARCHREQ).Filter = $TAINTED"
              - pattern: "(System.DirectoryServices.Protocols.SearchRequest $SEARCHREQ).DistinguishedName = $TAINTED"
              - pattern: "(System.DirectoryServices.AccountManagement.UserPrincipal $SEARCHREQ).$PROP = $TAINTED"
    message: |
      LDAP injection attacks exploit LDAP queries to influence how data is returned by
      the LDAP, or in this case an Active Directory server.

      It is recommended that newer applications use the `System.DirectoryServices.AccountManagement`
      API instead of `DirectorySearcher` API as it hides the complexity of querying LDAP directly.
      However,
      the `AccountManagement` API is still susceptible to LDAP injection if a user inputs LDAP
      queries,
      including LDAP filter characters such as `*`.

      It is recommended that all input passed to LDAP querying systems encode the following values:

      - Any occurrence of the null character must be escaped as “\00”.
      - Any occurrence of the open parenthesis character must be escaped as “\28”.
      - Any occurrence of the close parenthesis character must be escaped as “\29”.
      - Any occurrence of the asterisk character must be escaped as “\2a”.
      - Any occurrence of the backslash character must be escaped as “\5c”.

      Example code that safely encodes input for use in an LDAP query using the `AccountManagement`
      API:
      ```
      using System.DirectoryServices.AccountManagement;

      string EncodeLDAPString(string input) {
          // Note the \ character is replaced first
          char[] chars = new char[] { '\\', '\0', '(', ')', '*' };
          string[] encoded = new string[] { "\\5c", "\\00", "\\28", "\\29", "\\2a" };

          for (int i = 0; i < chars.Length; i++)
          {
              input = input.Replace(chars[i].ToString(), encoded[i]);
          }

          return input;
      }

      // unsafe, do not use without encoding first
      string userInput = "Administrator";
      PrincipalContext AD = new PrincipalContext(ContextType.Domain, "ad.example.dev");

      UserPrincipal u = new UserPrincipal(AD);
      string encodedUserName = EncodeLDAPString(userInput);

      // The AD search term, encoded prior to calling search
      u.SamAccountName = encodedUserName;

      // Search for user
      PrincipalSearcher search = new PrincipalSearcher(u);

      // Use FindOne to only return a single result
      UserPrincipal result = (UserPrincipal)search.FindOne();
      search.Dispose();

      // show some details
      if (result != null) {
          Console.WriteLine("User: {0}", result.DisplayName);
      } else {
          Console.WriteLine("user not found");
      }
      ```

      The same encoding method shown in `EncodeLDAPString` can also be used when using the
      older `DirectorySearcher` API.

      For more information see OWASP's guide:
      https://cheatsheetseries.owasp.org/cheatsheets/LDAP_Injection_Prevention_Cheat_Sheet.html
    severity: "WARNING"
    metadata:
      shortDescription: "Improper neutralization of special elements used in an LDAP query ('LDAP Injection')"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-90"
      security-severity: "Medium"
      related-packages: ['pkg:nuget/system.directoryservices.accountmanagement', 'pkg:nuget/system.directoryservices', 'pkg:nuget/system.directoryservices.protocols']
      license: MIT (c) GitLab Inc.
  - id: "csharp_injection_rule-SQLInjection"
    languages:
      - "csharp"
    patterns:
      - pattern-either:
          - patterns:
              - metavariable-pattern:
                  metavariable: "$FUNC"
                  pattern-either:
                    - pattern: "SqlQuery"
                    - pattern: "ExecuteSqlCommand"
                    - pattern: "ExecuteSqlCommandAsync"
                    - pattern: "ExecuteSqlRaw"
                    - pattern: "ExecuteSqlRawAsync"
                    - pattern: "FromSqlRaw"
                    - pattern: "FromSql"
                    - pattern: "GetSqlStringCommand"
                    - pattern: "ExecuteDataSet"
                    - pattern: "ExecuteReader"
                    - pattern: "ExecuteNonQuery"
                    - pattern: "ExecuteScalar"
                    - pattern: "CreateSQLQuery"
              - pattern-either:
                  - pattern: "$DB.$FUNC($ARG, ...)"
                  - pattern: "$DB.$FUNC<$CC>($ARG, ...)"
              - pattern-not: "$DB.$FUNC(\"...\", ...)"
              - pattern-not: "$DB.$FUNC<$CC>(\"...\", ...)"
          - patterns:
              - pattern-inside: |
                  using System.Data.Linq;
                  ...
              - pattern-either:
                  - patterns:
                      - pattern: "(DataContext $CTX).ExecuteQuery<$TRESULT>($ARG, ...)"
                      - pattern-not: "(DataContext $CTX).ExecuteQuery<$TRESULT>(\"...\", ...)"
                  - patterns:
                      - pattern: "(DataContext $CTX).ExecuteQuery($TYPE, $ARG, ...)"
                      - pattern-not: "(DataContext $CTX).ExecuteQuery($TYPE, \"...\", ...)"
                  - patterns:
                      - pattern: "(DataContext $CTX).ExecuteCommand($ARG, ...)"
                      - pattern-not: "(DataContext $CTX).ExecuteCommand(\"...\", ...)"
          - patterns:
              - metavariable-pattern:
                  metavariable: "$IMPL"
                  pattern-either:
                    - pattern: "SqlCommand"
                    - pattern: "OracleCommand"
                    - pattern: "NpgsqlCommand"
                    - pattern: "MySqlCommand"
                    - pattern: "EntityCommand"
                    - pattern: "OdbcCommand"
                    - pattern: "OleDbCommand"
                    - pattern: "SqliteCommand"
              - pattern-either:
                  - patterns:
                      - pattern: "new $IMPL($ARG, ...);"
                      - pattern-not: "new $IMPL(\"...\", ...);"
                  - patterns:
                      - pattern: "($IMPL $CMD).CommandText = <...$ARG...>;"
                      - pattern-not: "($IMPL $CMD).CommandText = \"...\";"
          - patterns:
              - metavariable-pattern:
                  metavariable: "$FUNC"
                  pattern-either:
                    - pattern: "ExecuteDataRow"
                    - pattern: "ExecuteDataRowAsync"
                    - pattern: "ExecuteDataset"
                    - pattern: "ExecuteDatasetAsync"
                    - pattern: "ExecuteNonQuery"
                    - pattern: "ExecuteNonQueryAsync"
                    - pattern: "ExecuteReader"
                    - pattern: "ExecuteReaderAsync"
                    - pattern: "ExecuteScalar"
                    - pattern: "ExecuteScalarAsync"
                    - pattern: "UpdateDataSet"
                    - pattern: "UpdateDataSetAsync"
              - pattern-inside: |
                  using MySql.Data.MySqlClient;
                  ...
              - pattern: "MySqlHelper.$FUNC($CONN, $ARG, ...)"
              - pattern-not: "MySqlHelper.$FUNC($CONN, \"...\", ...)"
          - patterns:
              - pattern-inside: |
                  using Cassandra;
                  ...
              - pattern-not: "$SESS.Execute(\"...\", ...)"
              - pattern-either:
                  - pattern: "(Session $SESS).Execute($ARG, ...)"
                  - patterns:
                      - pattern-inside: |
                          var $SESS = $CLUSTER.Connect(...);
                          ...
                      - pattern: "$SESS.Execute($ARG, ...)"
    message: |
      SQL Injection is a critical vulnerability that can lead to data or system compromise. By
      dynamically generating SQL query strings, user input may be able to influence the logic of
      the SQL statement. This could lead to an adversary accessing information they should
      not have access to, or in some circumstances, being able to execute OS functionality or code.

      Replace all dynamically generated SQL queries with parameterized queries. In situations where
      dynamic queries must be created, never use direct user input, but instead use a map or
      dictionary of valid values and resolve them using a user supplied key.

      For example, some database drivers do not allow parameterized queries for `>` or `<` comparison
      operators. In these cases, do not use a user supplied `>` or `<` value, but rather have the
      user
      supply a `gt` or `lt` value. The alphabetical values are then used to look up the `>` and `<`
      values to be used in the construction of the dynamic query. The same goes for other queries
      where
      column or table names are required but cannot be parameterized.

      Example using parameterized queries with `SqlCommand`:
      ```
      string userInput = "someUserInput";
      string connectionString = ...;
      using (SqlConnection connection = new SqlConnection(connectionString))
      {
          connection.Open();
          String sql = "SELECT name, value FROM table where name=@Name";

          using (SqlCommand command = new SqlCommand(sql, connection))
          {
              command.Parameters.Add("@Name", System.Data.SqlDbType.NVarChar);
              command.Parameters["@Name"].Value = userInput;
              using (SqlDataReader reader = command.ExecuteReader())
              {
                  while (reader.Read())
                  {
                      Console.WriteLine("{0} {1}", reader.GetString(0), reader.GetString(1));
                  }
              }
          }
      }
      ```

      For more information on SQL Injection see OWASP:
      https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
    severity: "WARNING"
    metadata:
      shortDescription: "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-89"
      security-severity: "Medium"
      related-packages: ['pkg:nuget/system.data.odbc', 'pkg:nuget/system.data.oledb', 'pkg:nuget/system.data.sqlclient', 'pkg:nuget/npgsql',
        'pkg:nuget/oracle.manageddataaccess', 'pkg:nuget/cassandra']
      license: MIT (c) GitLab Inc.
  - id: "csharp_injection_rule-XPathInjection"
    languages:
      - "csharp"
    patterns:
      - pattern-inside: |
          using System.Xml;
          ...
      - pattern-either:
          - patterns:
              - metavariable-regex:
                  metavariable: "$FUNC"
                  regex: "^(SelectNodes|SelectSingleNode|Compile|Evaluate|Matches|Select|SelectAncestors|SelectChildren|SelectDescendants)$"
              - metavariable-regex:
                  metavariable: "$TY"
                  regex: "^(XPathNavigator|XmlDocument|XmlNode|XmlDocumentXPathExtensions)$"
              - pattern: "($TY $VAR).$FUNC(<...$ARG...>, ...)"
              - pattern-not: "($TY $VAR).$FUNC(\"...\", ...)"
          - patterns:
              - pattern-inside: |
                  using System.Xml.Linq;
                  ...
              - metavariable-regex:
                  metavariable: "$FUNC"
                  regex: "^(XPathEvaluate|XPathSelectElement|XPathSelectElements)$"
              - pattern: "$VAR.$FUNC(<...$ARG...>, ...)"
              - pattern-not: "$VAR.$FUNC(\"...\", ...)"
          - patterns:
              - pattern-inside: |
                  using System.Xml.Schema;
                  ...
              - pattern-either:
                  - patterns:
                      - pattern: "$VAR.XPath = <...$ARG...>;"
                      - pattern-not: "$VAR.XPath = \"...\""
                  - patterns:
                      - pattern: "new XmlSchemaXPath { XPath = <...$ARG...> };"
                      - focus-metavariable: "$ARG"
    message: |
      XPath injection is a vulnerability that can allow an adversary to inject or modify how an XML
      query
      is structured. Depending on the logic of the original query, this could lead to adversaries
      extracting unauthorized information or in rare cases bypassing authorization checks.

      It is recommended that LINQ to XML is used instead of XPath for querying XML documents. Care
      must be taken to **not** call these LINQ functions with user input as they can still lead to
      XPath
      injection:

      - `XPathEvaluate`
      - `XPathSelectElement`
      - `XPathSelectElements`

      Example using LINQ to XML to safely extract the first user from a list of users:
      ```
      // XDocument is safe from XXE attacks as the resolver is disabled by default
      XDocument doc = XDocument.Load("users.xml");
      XNamespace ns = "urn:users-schema";

      string userInput = "LastName";

      // Get all the users.
      var user = doc.Descendants(ns + "user")
                     .Select(u => new {
                        FirstName = (string)u.Element(ns + "first-name"),
                        LastName = (string)u.Element(ns + "last-name")
                     }).Where(u => u.LastName == userInput).FirstOrDefault();

      Console.WriteLine(user.FirstName + " " + user.LastName);
      ```

      For more information on LINQ to XML security see:
      https://learn.microsoft.com/en-us/dotnet/standard/linq/linq-xml-security

      For more information on XML security see OWASP's guide:
      https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#net
    severity: "INFO"
    metadata:
      shortDescription: "Improper neutralization of data within XPath expressions ('XPath Injection')"
      category: "security"
      cwe: "CWE-643"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "Medium"
      related-packages: ['pkg:nuget/system.xml.linq', 'pkg:nuget/system.xml.xpath']
      license: MIT (c) GitLab Inc.
  - id: "csharp_injection_rule-XmlDocumentXXEInjection"
    languages:
      - "csharp"
    mode: "taint"
    pattern-sinks:
      - patterns:
          - pattern-not-inside: |
              (XmlDocument $DOC).XmlResolver = null;
              ...
          - pattern-either:
              - pattern: "(XmlDocument $DOC).Load(...);"
              - pattern: "(XmlDocument $DOC).LoadXml(...);"
    pattern-sources:
      - pattern: "var $DOC = new System.Xml.XmlDocument(...);"
      - patterns:
          - pattern: "var $DOC = new System.Xml.XmlDocument {...};"
          - pattern-not: "var $DOC = new System.Xml.XmlDocument {...,XmlResolver = null,...};"
    message: |
      External XML entities are a feature of XML parsers that allow documents to contain references
      to
      other documents or data. This feature can be abused to read files, communicate with external
      hosts,
      exfiltrate data, or cause a Denial of Service (DoS).

      XML parsers and document loaders must be configured to not resolve entities. This can be done
      by:
      - Ensuring you are running a version of .NET Framework greater than 4.5.2 (released in 2014).
      - Using `XDocument` which disables entity resolution and is generally safe from DoS.
      - Setting `XmlDocument`'s `XmlResolver` to null.

      Example of safely loading an XML file using `XmlDocument`:
      ```
      XmlDocument document = new XmlDocument();
      document.XmlResolver = null;
      document.Load("users.xml");
      ```

      For more information on XML security, see OWASP's guide:
      https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#net
    severity: "WARNING"
    metadata:
      shortDescription: "Improper restriction of XML external entity reference ('XXE')"
      category: "security"
      cwe: "CWE-611"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "Medium"
      related-packages: ['pkg:nuget/system.xml.xmldocument']
      license: MIT (c) GitLab Inc.
  - id: "csharp_injection_rule-XmlReaderXXEInjection"
    languages:
      - "csharp"
    mode: "taint"
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  $SETTINGS.ProhibitDtd = false;
                  ...
              - pattern-inside: |
                  $SETTINGS.DtdProcessing = DtdProcessing.Parse;
                  ...
          - pattern: "System.Xml.XmlReader.Create(..., $SETTINGS);"
    pattern-sources:
      - pattern: "var $SETTINGS = new XmlReaderSettings();"
    message: |
      External XML entities are a feature of XML parsers that allow documents to contain references
      to
      other documents or data. This feature can be abused to read files, communicate with external
      hosts,
      exfiltrate data, or cause a Denial of Service (DoS).

      XML parsers and document loaders must be configured to not resolve entities. This can be done
      by:
      - Ensuring you are running a version of .NET Framework greater than 4.5.2 (released in 2014).
      - Setting `XmlTextReader`'s  `ProhibitDtd` to `true`
      - Setting `XmlReaderSettings` `DtdProcessing` to `DtdProcessing.Prohibit`

      Example of safely loading an XML file using `XmlDocument`:
      ```
      var settings = new XmlReaderSettings();
      settings.DtdProcessing = DtdProcessing.Prohibit;
      XmlReader reader = XmlReader.Create(path, settings);
      ```

      For more information on XML security, see OWASP's guide:
      https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#net
    severity: "WARNING"
    metadata:
      shortDescription: "Improper restriction of XML external entity reference ('XXE')"
      category: "security"
      cwe: "CWE-611"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "MEDIUM"
      license: MIT (c) GitLab Inc.
  - id: "csharp_other_rule-UnsafeXSLTSettingUsed"
    patterns:
      - pattern-either:
          - patterns:
              - pattern: "new XsltSettings() {...,$OPTIONS,...};"
              - metavariable-pattern:
                  metavariable: "$OPTIONS"
                  pattern-either:
                    - pattern: "EnableDocumentFunction = true"
                    - pattern: "EnableScript = true"
          - patterns:
              - pattern: "$SETTINGS.$OPT = true;"
              - pattern: |
                  var $SETTINGS = new XsltSettings();
                  ...
                  $SETTINGS.$OPT = true;
              - metavariable-pattern:
                  metavariable: "$OPT"
                  pattern-either:
                    - pattern: "EnableDocumentFunction"
                    - pattern: "EnableScript"
    message: |
      By setting `XsltSettings.EnableScript` to true, an adversary who is able to influence the
      loaded
      XSL document could directly inject code to compromise the system. It is strongly
      recommended that an alternative approach is used to work with XML data.

      For increased security:

      - Never process user-supplied XSL style sheets
      - Ensure `XsltSettings.EnableScript` is set to false
      - Ensure `XsltSettings.EnableDocumentFunction` is set to false

      If the application must calculate values from XML input, instead of using XSL scripts to
      execute functions, modify the XML document prior to running the
      `XslCompiledTransform.Transform` method.

      Example of modifying the XML prior to running `Transform`:
      ```
      const String filename = "number.xml";
      const String stylesheet = "calc.xsl";

      // Compile the style sheet.
      XsltSettings xslt_settings = new XsltSettings();
      xslt_settings.EnableScript = false; // disable script
      xslt_settings.EnableDocumentFunction = false; // disable document() function
      XslCompiledTransform xslt = new XslCompiledTransform();
      XmlResolver resolver = null; // set a null entity resolver
      xslt.Load(stylesheet, xslt_settings, resolver);

      // Load the XML source file, using XDocument for safety
      XDocument doc = XDocument.Load(filename);

      // do our modifications to the document before the transformation
      // instead of inside of a script.
      doc.Element("data").Add(new XElement("circle", new XElement("radius", 12)));

      // Create an XmlWriter.
      XmlWriterSettings settings = new XmlWriterSettings();
      settings.OmitXmlDeclaration = true;
      settings.Indent = true;
      XmlWriter writer = XmlWriter.Create("output.xml", settings);
      // Finally, execute the transformation.
      xslt.Transform(doc.CreateReader(), writer);
      writer.Close();
      ```

      For more information on security considerations when using XSL see the following URLs:
      - https://learn.microsoft.com/en-us/dotnet/standard/data/xml/xslt-security-considerations
      - https://learn.microsoft.com/en-us/dotnet/api/system.xml.xsl.xslcompiledtransform?view=net-7.0#security-considerations
    languages:
      - "csharp"
    severity: "WARNING"
    metadata:
      shortDescription: "XML injection (aka Blind XPath injection)"
      category: "security"
      cwe: "CWE-91"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      license: MIT (c) GitLab Inc.
  - id: "csharp_password_rule-PasswordComplexity"
    languages:
      - "csharp"
    pattern-either:
      - pattern: "$OPT.Password.RequireDigit = false;"
      - pattern: "$OPT.Password.RequireLowercase = false;"
      - pattern: "$OPT.Password.RequireNonAlphanumeric = false;"
      - pattern: "$OPT.Password.RequireUppercase = false;"
      - patterns:
          - pattern: "$OPT.Password.RequiredLength = $LEN;"
          - metavariable-comparison:
              metavariable: "$LEN"
              comparison: "$LEN < 8"
      - patterns:
          - pattern: "$OPT.Password.RequiredUniqueChars = $LEN;"
          - metavariable-comparison:
              metavariable: "$LEN"
              comparison: "$LEN < 1"
    message: |
      The application's `PasswordValidator.RequiredLength` property allows passwords
      to be less than 8 characters. Consider requiring a length of at least 8 or more
      characters to reduce the chance of passwords being brute forced.

      Example of setting the RequiredLength to 8 in ASP.NET Core Identity:
      ```
      builder.Services.Configure<IdentityOptions>(options =>
      {
          // Default Password settings.
          options.Password.RequireDigit = true;
          options.Password.RequireLowercase = true;
          options.Password.RequireNonAlphanumeric = true;
          options.Password.RequireUppercase = true;
          options.Password.RequiredLength = 8;
          options.Password.RequiredUniqueChars = 1;
      });
      ```

      For more information on configuring ASP.NET Core Identity see:
      https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity-configuration
    severity: "WARNING"
    metadata:
      shortDescription: "Weak password requirements"
      category: "security"
      cwe: "CWE-521"
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      security-severity: "Info"
      related-packages: ['pkg:nuget/microsoft.aspnetcore.identity', 'pkg:nuget/microsoft.aspnetcore.identity.entityframeworkcore']
      license: MIT (c) GitLab Inc.
  - id: "csharp_path_rule-PathTraversal"
    languages:
      - "csharp"
    mode: "taint"
    pattern-sources:
      - patterns:
          - pattern-inside: |
              public class $CLASS : Controller {
                ...
              }
          - pattern: "$PARAM"
          - pattern-either:
              - patterns:
                  - metavariable-regex:
                      metavariable: "$HTTP_ANNO"
                      regex: "^(Http)"
                  - pattern-inside: |
                      [$HTTP_ANNO]
                      public string $METHOD(...,$PARAM,...){...}
              - pattern-inside: |
                  public IActionResult $METHOD(...,$PARAM,...){...}
    pattern-sanitizers:
      - pattern-either:
          - pattern: "(Microsoft.Extensions.FileProviders.PhysicalFileProvider $E).GetFileInfo(...)"
          - pattern: "(System.Web.HttpServerUtility $E).MapPath(...)"
          - pattern: "(System.Web.HttpServerUtilityBase $E).MapPath(...)"
          - pattern: "(System.Web.HttpRequest $E).MapPath(...)"
    pattern-sinks:
      - pattern-either:
          - pattern: "System.IO.Directory.Delete(...)"
          - pattern: "System.IO.Directory.GetFiles(...)"
          - pattern: "System.IO.Directory.Move(...)"
          - pattern: "System.IO.File.AppendAllLines(...)"
          - pattern: "System.IO.File.AppendAllLinesAsync(...)"
          - pattern: "System.IO.File.AppendAllText(...)"
          - pattern: "System.IO.File.AppendAllTextAsync(...)"
          - pattern: "System.IO.File.AppendText(...)"
          - pattern: "System.IO.File.Copy(...)"
          - pattern: "System.IO.File.Create(...)"
          - pattern: "System.IO.File.CreateText(...)"
          - pattern: "System.IO.File.Delete(...)"
          - pattern: "System.IO.File.Move(...)"
          - pattern: "System.IO.File.Open(...)"
          - pattern: "System.IO.File.OpenRead(...)"
          - pattern: "System.IO.File.OpenText(...)"
          - pattern: "System.IO.File.OpenWrite(...)"
          - pattern: "System.IO.File.ReadAllBytes(...)"
          - pattern: "System.IO.File.ReadAllBytesAsync(...)"
          - pattern: "System.IO.File.ReadAllLines(...)"
          - pattern: "System.IO.File.ReadAllLinesAsync(...)"
          - pattern: "System.IO.File.ReadAllText(...)"
          - pattern: "System.IO.File.ReadAllTextAsync(...)"
          - pattern: "System.IO.File.ReadLines(...)"
          - pattern: "System.IO.File.Replace(...)"
          - pattern: "System.IO.File.SetAccessControl(...)"
          - pattern: "System.IO.File.WriteAllBytes(...)"
          - pattern: "System.IO.File.WriteAllBytesAsync(...)"
          - pattern: "System.IO.File.WriteAllLines(...)"
          - pattern: "System.IO.File.WriteAllLinesAsync(...)"
          - pattern: "System.IO.File.WriteAllText(...)"
          - pattern: "System.IO.File.WriteAllTextAsync(...)"
          - pattern: "new System.IO.FileInfo(...)"
          - pattern: "(System.IO.FileInfo $E).CopyTo(...)"
          - pattern: "(System.IO.FileInfo $E).MoveTo(...)"
          - pattern: "(System.IO.FileInfo $E).Replace(...)"
          - pattern: "System.Reflection.Assembly.LoadFile(...)"
          - pattern: "System.Reflection.Assembly.LoadFrom(...)"
          - pattern: "System.Reflection.Assembly.ReflectionOnlyLoadFrom(...)"
          - pattern: "System.Reflection.Assembly.UnsafeLoadFrom(...)"
          - pattern: "System.AppDomain.AppendPrivatePath(...)"
          - pattern: "System.Xml.XmlReader.Create(...)"
          - pattern: "new System.IO.StreamReader.ctor(...)"
          - pattern: "new System.IO.StreamWriter.ctor(...)"
          - pattern: "new System.IO.FileStream.ctor(...)"
          - pattern: "new System.Web.Mvc.FilePathResult(...)"
          - pattern: "new Microsoft.AspNetCore.Mvc.PhysicalFileResult(...)"
          - pattern: "(Microsoft.AspNetCore.Mvc.RazorPages.PageModel $E).PhysicalFile(...)"
          - pattern: "(System.Web.UI.WebControls.FileUpload $E).SaveAs(...)"
          - pattern: "(System.Web.HttpResponse $E).TransmitFile(...)"
          - pattern: "(System.Web.HttpResponse $E).WriteFile(...)"
          - pattern: "(System.Web.HttpResponseBase $E).TransmitFile(...)"
          - pattern: "(System.Web.HttpResponseBase $E).WriteFile(...)"
          - pattern: "(System.IO.Compression.ZipFileExtensions $E).CreateEntryFromFile(...)"
          - pattern: "(System.IO.Compression.ZipFileExtensions $E).ExtractToFile(...)"
          - pattern: "(System.IO.Compression.ZipFileExtensions $E).ExtractToDirectory(...)"
          - pattern: "(System.Net.WebClient $E).DownloadFile(...)"
          - pattern: "(System.Net.WebClient $E).DownloadFileAsync(...)"
          - pattern: "(System.Net.WebClient $E).DownloadFileTaskAsync(...)"
    message: |
      The application dynamically constructs file or path information. If the path
      information comes from user input, it could be abused to read sensitive files,
      access other users data, or aid in exploitation to gain further system access.

      User input should never be used in constructing paths or files for interacting
      with the filesystem. This includes filenames supplied by user uploads or downloads.
      If possible consider hashing user input or replacing it with unique values and
      use `System.IO.Path.GetFullPath` to resolve and validate the path information
      prior to processing any file functionality.

      Example using `Path.GetFullPath` and not allowing direct user input:
      ```
      // store user input alongside an ID we control
      struct userData
      {
          public string userFilename;
          public Guid id;
      }

      class Program
      {
          public static void Main()
          {
              userData data = new userData();
              // user input, saved only as a reference
              data.userFilename = "..\\test.txt";

              // random id as the filename
              data.id = Guid.NewGuid();

              // restrict all file processing to this directory only
              string basePath = "C:\\Restricted\\";

              // resolve the full path, but only use our random generated id
              string fullPath = Path.GetFullPath(basePath + data.id);

              // verify the path is contained within our basePath
              if (!fullPath.StartsWith(basePath)) {
                  Console.WriteLine("Invalid path specified!");
                  return;
              }
              // process / work with file
          }
      }
      ```

      For more information on path traversal issues see OWASP:
      https://owasp.org/www-community/attacks/Path_Traversal
    metadata:
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      category: "security"
      cwe: "CWE-22"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Medium"
      related-packages: ['pkg:nuget/system.appdomain', 'pkg:nuget/system.io', 'pkg:nuget/microsoft.aspnetcore.mvc', 'system.io.compression']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "csharp_validation_rule-InputValidation"
    languages:
      - "csharp"
    patterns:
      - pattern: |
          [ValidateInput(false)]
          public $RET $FOO(...)
          {
            ...
          }
    message: |
      By using the `[ValidateInput(false)]` attribute in a controller
      class, the application will disable request validation for that
      method. This disables ASP.NET from examining requests for injection
      attacks such as Cross-Site-Scripting (XSS).

      If possible, re-enable validation by using `ValidateInput(true)`.
      In some cases this may not be possible, in which case ensure how the
      request data used is validated and this method does not
      output user input directly into the view.

      For more information on protecting ASP.NET Core applications from XSS see:
      https://learn.microsoft.com/en-us/aspnet/core/security/cross-site-scripting

      Example of enabling `ValidateInput` attribute:
      ```
      class ControllerClass
      {
          [ValidateInput(true)]
          public void SomeActionMethod()
          {
          }
      }
      ```

      For more information on ASP.NET request validation see OWASP:
      https://owasp.org/www-community/ASP-NET_Request_Validation
    severity: "WARNING"
    metadata:
      shortDescription: "ASP.NET input validation disabled"
      category: "security"
      cwe: "CWE-554"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
  - id: "csharp_xss_rule-HtmlElementXss"
    mode: "taint"
    pattern-sources:
      - patterns:
          - pattern: "$PARAM"
          - pattern-inside: |
              $RET $METHOD(...,$PARAM,...){...}
      - pattern: "Request.Cookies[...]"
      - pattern: "Request.Cookies.Get(...)"
      - pattern: "Request.Form[...]"
      - pattern: "Request.Form.Get(...)"
      - pattern: "Request.Headers[...]"
      - pattern: "Request.Headers.Get(...)"
      - pattern: "Request.QueryString[...]"
      - pattern: "Request.QueryString.Get(...)"
      - pattern: "Request.Params[...]"
      - pattern: "Request.RawUrl"
      - pattern: "Request.Url"
      - pattern: "Request.Path"
      - pattern: "Request.Body"
      - pattern: "$CTX.Request.Cookies[...]"
      - pattern: "$CTX.Request.Cookies.Get(...)"
      - pattern: "$CTX.Request.Form[...]"
      - pattern: "$CTX.Request.Form.Get(...)"
      - pattern: "$CTX.Request.Headers[...]"
      - pattern: "$CTX.Request.Headers.Get(...)"
      - pattern: "$CTX.Request.QueryString[...]"
      - pattern: "$CTX.Request.QueryString.Get(...)"
      - pattern: "$CTX.Request.Body"
    pattern-sanitizers:
      - patterns:
          - metavariable-regex:
              metavariable: "$FUNC"
              regex: "(SerializeObject|HtmlAttributeEncode|HtmlEncode|HtmlFormUrlEncode|UrlEncode|UrlPathEncode|XmlAttributeEncode|XmlEncode|Encode)"
          - pattern: "$CLASS.$FUNC(...)"
    pattern-sinks:
      - pattern: "(System.Web.Mvc.HtmlHelper $E).Raw(...)"
      - pattern: "(Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper $E).Raw(...)"
      - pattern: "Response.Write(...)"
      - pattern: "(System.Web.UI.HtmlTextWriter $E).AddAttribute(...)"
      - pattern: "(System.Web.UI.HtmlTextWriter $E).AddStyleAttribute(...)"
      - pattern: "(System.Web.UI.HtmlTextWriter $E).RenderBeginTag(...)"
      - pattern: "(System.Web.UI.HtmlTextWriter $E).Write(...)"
      - pattern: "(System.Web.UI.HtmlTextWriter $E).WriteAttribute(...)"
      - pattern: "(System.Web.UI.HtmlTextWriter $E).WriteBeginTag(...)"
      - pattern: "(System.Web.UI.HtmlTextWriter $E).WriteEndTag(...)"
      - pattern: "(System.Web.UI.HtmlTextWriter $E).WriteFullBeginTag(...)"
      - pattern: "(System.Web.UI.HtmlTextWriter $E).WriteStyleAttribute(...)"
    languages:
      - "csharp"
    message: |
      Cross Site Scripting (XSS) is an attack which exploits a web application or system to treat
      user input
      as markup or script code. It is important to encode the data depending on the specific context
      it
      is used in. There are at least six context types:

      - Inside HTML tags `<div>context 1</div>`
      - Inside attributes: `<div class="context 2"></div>`
      - Inside event attributes `<button onclick="context 3">button</button>`
      - Inside script blocks: `<script>var x = "context 4"</script>`
      - Unsafe element HTML assignment: `element.innerHTML = "context 5"`
      - Inside URLs: `<iframe src="context 6"></iframe><a href="context 6">link</a>`
      User input that is displayed within the application must be encoded, sanitized or validated
      to ensure it cannot be treated as HTML or executed as Javascript code. Care must also be
      taken
      to not mix server-side templating with client-side templating, as the server-side templating
      will
      not encode things like {{ 7*7 }} which may execute client-side templating features.

      It is _NOT_ advised to encode user input prior to inserting into a data store. The data will
      need to be
      encoded depending on context of where it is output. It is much safer to force the displaying
      system to
      handle the encoding and not attempt to guess how it should be encoded.

      Consider using built-in framework capabilities for automatically encoding user input.
      Depending
      on output context, consider using the following `System.Text.Encodings.Web` encoders:

      - [HtmlEncoder](https://learn.microsoft.com/en-us/dotnet/api/system.text.encodings.web.htmlencoder)
      - [JavaScriptEncoder](https://learn.microsoft.com/en-us/dotnet/api/system.text.encodings.web.javascriptencoder)
      - [UrlEncoder](https://learn.microsoft.com/en-us/dotnet/api/system.text.encodings.web.urlencoder)

      For more information on protecting ASP.NET Core applications from XSS see:
      https://learn.microsoft.com/en-us/aspnet/core/security/cross-site-scripting#accessing-encoders-in-code
    metadata:
      shortDescription: "Improper neutralization of input during web page generation ('Cross-site Scripting')"
      category: "security"
      cwe: "CWE-79"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "Medium"
      related-packages: ['pkg:nuget/microsoft.aspnetcore.mvc']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "csharp_xss_rule-ScriptXss"
    mode: "taint"
    pattern-sources:
      - patterns:
          - pattern: "$PARAM"
          - pattern-inside: |
              $RET $METHOD(...,$PARAM,...){...}
      - pattern: "Request.Cookies[...]"
      - pattern: "Request.Cookies.Get(...)"
      - pattern: "Request.Form[...]"
      - pattern: "Request.Form.Get(...)"
      - pattern: "Request.Headers[...]"
      - pattern: "Request.Headers.Get(...)"
      - pattern: "Request.QueryString[...]"
      - pattern: "Request.QueryString.Get(...)"
      - pattern: "Request.Params[...]"
      - pattern: "Request.RawUrl"
      - pattern: "Request.Url"
      - pattern: "Request.Path"
      - pattern: "Request.Body"
      - pattern: "$CTX.Request.Cookies[...]"
      - pattern: "$CTX.Request.Cookies.Get(...)"
      - pattern: "$CTX.Request.Form[...]"
      - pattern: "$CTX.Request.Form.Get(...)"
      - pattern: "$CTX.Request.Headers[...]"
      - pattern: "$CTX.Request.Headers.Get(...)"
      - pattern: "$CTX.Request.QueryString[...]"
      - pattern: "$CTX.Request.QueryString.Get(...)"
      - pattern: "$CTX.Request.Body"
      - pattern: "$ELE.Text"
    pattern-sanitizers:
      - patterns:
          - metavariable-regex:
              metavariable: "$FUNC"
              regex: "(SerializeObject|HtmlAttributeEncode|HtmlEncode|HtmlFormUrlEncode|UrlEncode|UrlPathEncode|XmlAttributeEncode|XmlEncode|Encode)"
          - pattern: "$CLASS.$FUNC(...)"
    pattern-propagators:
      - pattern: "(StringBuilder $OUT).Append($IN)"
        from: "$IN"
        to: "$OUT"
    pattern-sinks:
      - pattern: "$SCRIPTMANAGER.RegisterStartupScript(...)"
      - pattern: "$SCRIPTMANAGER.RegisterClientScriptBlock(...)"
      - pattern: "System.Web.UI.RegisterStartupScript(...)"
      - pattern: "System.Web.UI.RegisterClientScriptBlock(...)"
    languages:
      - "csharp"
    message: |
      Cross Site Scripting (XSS) is an attack which exploits a web application or system to treat
      user input
      as markup or script code. It is important to encode the data depending on the specific context
      it
      is used in.

      User input that is used within the application scripts must be encoded, sanitized or validated
      to ensure it cannot change the behavior of the Javascript code.

      It is _NOT_ advised to encode user input prior to inserting into a data store. The data will
      need to be
      encoded depending on context of where it is output. It is much safer to force the displaying
      system to
      handle the encoding and not attempt to guess how it should be encoded.

      Consider using built-in framework capabilities for automatically encoding user input.
      Depending
      on output context, consider using the following `System.Text.Encodings.Web` encoders:

      - [HtmlEncoder](https://learn.microsoft.com/en-us/dotnet/api/system.text.encodings.web.htmlencoder)
      - [JavaScriptEncoder](https://learn.microsoft.com/en-us/dotnet/api/system.text.encodings.web.javascriptencoder)
      - [UrlEncoder](https://learn.microsoft.com/en-us/dotnet/api/system.text.encodings.web.urlencoder)

      For more information on protecting ASP.NET Core applications from XSS see:
      https://learn.microsoft.com/en-us/aspnet/core/security/cross-site-scripting#accessing-encoders-in-code
    metadata:
      shortDescription: "Improper neutralization of input during web page generation ('Cross-site Scripting')"
      category: "security"
      cwe: "CWE-79"
      security-severity: "MEDIUM"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "go_blocklist_rule-blocklist-des"
    languages:
      - "go"
    patterns:
      - pattern: |
          import "crypto/des"
    message: |
      The DES algorithm has not been recommended for over 15 years and was withdrawn from NIST (FIPS
      46-3) in 2005. It is recommended that an algorithm that provides message integrity be used
      instead. Consider using `XChaCha20Poly1305` or `AES-256-GCM`.

      For older applications, `AES-256-GCM` is recommended, however it has many drawbacks:
      - Slower than `XChaCha20Poly1305`
      - Smaller nonce value size compared to `XChaCha20Poly1305`
      - Catastrophic failure if nonce values are re-used

      Example using
      [XChaCha20Poly1305](https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305#NewX):
      ```
      key := make([]byte, chacha20poly1305.KeySize)
      if _, err := io.ReadFull(rand.Reader, key); err != nil {
        log.Fatal(err)
      }

      // NewX is a variant that uses longer nonce values for better security
      aead, err := chacha20poly1305.NewX(key)
      if err != nil {
        log.Fatal(err)
      }

      var encrypted = []byte{}
      var nonce = []byte{}

      // Encryption routine
      {
        msg := []byte("Some secret message")
        nonce = make([]byte, aead.NonceSize())
        if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
          log.Fatal("failed to generate nonce")
        }

        encrypted = aead.Seal(nil, nonce, msg, nil)
      }

      // Decryption routine
      {
        if len(encrypted) < aead.NonceSize() {
          log.Fatal("incorrect ciphertext length")
        }

        msg, err := aead.Open(nil, nonce, encrypted, nil)
        if err != nil {
          log.Fatal(err)
        }
        fmt.Printf("Decrypted: %s\n", msg)
      }
      ```

      Example using [AES-256-GCM](https://pkg.go.dev/crypto/cipher#NewGCM):
      ```
      // 32 byte keys will configure AES-256
      key := make([]byte, 32)
      if _, err := io.ReadFull(rand.Reader, key); err != nil {
        log.Fatal(err)
      }

      blockCipher, err := aes.NewCipher(key)
      if err != nil {
        log.Fatal(err)
      }

      aead, err := cipher.NewGCM(blockCipher)
      if err != nil {
        log.Fatal(err)
      }

      var encrypted = []byte{}
      var nonce = []byte{}
      // Encryption routine
      {
        msg := []byte("Some secret message")
        // note that the key must be rotated every 2^32 random nonces used otherwise
        // cipher text could be repeated
        nonce = make([]byte, 12)
        if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
          log.Fatal(err)
        }
        encrypted = aead.Seal(nil, nonce, msg, nil)
      }

      // Decryption routine
      {
        msg, err := aead.Open(nil, nonce, encrypted, nil)
        if err != nil {
          log.Fatal(err)
        }
        fmt.Printf("Decrypted: %s\n", msg)
      }
      ```
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      category: "security"
      related-packages: ['pkg:go/golang.org/x/crypto']
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_blocklist_rule-blocklist-md5"
    languages:
      - "go"
    patterns:
      - pattern: |
          import "crypto/md5"
    message: |
      The MD5 message-digest algorithm has been cryptographically broken and is unsuitable for
      further use. The MD5 hash algorithm has been found to be vulnerable to producing collisions.
      This means that two different values, when hashed, can lead to the same hash value. It is
      recommended that the SHA-3 or BLAKE2 family of algorithms be used for non-password based
      cryptographic hashes instead. For password based cryptographic hashes,  consider using the
      bcrypt or Argon2id family of cryptographic hashes.

      Hashing values using [BLAKE2](https://pkg.go.dev/golang.org/x/crypto/blake2b):
      ```
      fileContents := []byte("some file contents to create hash for")
      blake2bHasher, err := blake2b.New512(nil)
      if err != nil {
        log.Fatal(err)
      }
      hashedValue := blake2bHasher.Sum(fileContents)
      fmt.Printf("%s\n", hex.EncodeToString(hashedValue))
      ```

      Hashing and securely comparing passwords using
      [Argon2id](https://pkg.go.dev/golang.org/x/crypto/argon2#hdr-Argon2id):
      ```
      type argonParameters struct {
        variant     string
        version     int
        memory      uint32
        iterations  uint32
        parallelism uint8
        saltLength  uint32
        keyLength   uint32
      }

      func (a argonParameters) StringFormat(salt, derivedKey []byte) string {
        encodedSalt := base64.RawStdEncoding.EncodeToString(salt)
        encodedKey := base64.RawStdEncoding.EncodeToString(derivedKey)

        return fmt.Sprintf("$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s",
          argon2.Version,
          a.memory,
          a.iterations,
          a.parallelism,
          encodedSalt,
          encodedKey,
        )
      }

      func main() {
        // Initialize Argon2id parameters
        p := argonParameters{
          memory:      64 * 1024,
          iterations:  3,
          parallelism: 2,
          saltLength:  16,
          keyLength:   32,
        }

        // Generate random salt (to be stored alongside derived hash key)
        salt := make([]byte, p.saltLength)
        if _, err := io.ReadFull(rand.Reader, salt); err != nil {
          log.Fatal(err)
        }

        usersPassword := []byte("User's Very S3cur3P4ss@rd@#$%")

        var derivedKey []byte
        // Create key hash derived from user's password
        {
          derivedKey = argon2.IDKey(usersPassword, salt, p.iterations, p.memory, p.parallelism,
      p.keyLength)
          // store p.StringFormat(...) result in a data store...
          fmt.Printf("%s\n", p.StringFormat(salt, derivedKey))
        }

        // Verify a user's password against key
        {
          keyToCompare := argon2.IDKey(usersPassword, salt, p.iterations, p.memory, p.parallelism,
      p.keyLength)

          // Use subtle.ConstantTimeCompare(..., ...) to ensure no side channel leaks used in timing
      attacks
          if subtle.ConstantTimeCompare(derivedKey, keyToCompare) == 1 {
            fmt.Printf("Passwords match\n")
          } else {
            fmt.Printf("Passwords do not match\n")
          }
        }
      }
      ```

      For more information on password storage see OWASP's guide:
      https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      category: "security"
      related-packages: ['pkg:go/golang.org/x/crypto']
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_blocklist_rule-blocklist-rc4"
    languages:
      - "go"
    patterns:
      - pattern: |
          import "crypto/rc4"
    message: |
      The RC4 stream-cipher has been cryptographically broken and is unsuitable
      for use in production. It is recommended that ChaCha20 or Advanced Encryption
      Standard (AES) be used instead. Consider using `XChaCha20Poly1305` or `AES-256-GCM`.

      For older applications, `AES-256-GCM` is recommended, however it has many drawbacks:
      - Slower than `XChaCha20Poly1305`
      - Smaller nonce value size compared to `XChaCha20Poly1305`
      - Catastrophic failure if nonce values are re-used

      Example using
      [XChaCha20Poly1305](https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305#NewX):
      ```
      key := make([]byte, chacha20poly1305.KeySize)
      if _, err := io.ReadFull(rand.Reader, key); err != nil {
        log.Fatal(err)
      }

      // NewX is a variant that uses longer nonce values for better security
      aead, err := chacha20poly1305.NewX(key)
      if err != nil {
        log.Fatal(err)
      }

      var encrypted = []byte{}
      var nonce = []byte{}

      // Encryption routine
      {
        msg := []byte("Some secret message")
        nonce = make([]byte, aead.NonceSize())
        if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
          log.Fatal("failed to generate nonce")
        }

        encrypted = aead.Seal(nil, nonce, msg, nil)
      }

      // Decryption routine
      {
        if len(encrypted) < aead.NonceSize() {
          log.Fatal("incorrect ciphertext length")
        }

        msg, err := aead.Open(nil, nonce, encrypted, nil)
        if err != nil {
          log.Fatal(err)
        }
        fmt.Printf("Decrypted: %s\n", msg)
      }
      ```

      Example using [AES-256-GCM](https://pkg.go.dev/crypto/cipher#NewGCM):
      ```
      // 32 byte keys will configure AES-256
      key := make([]byte, 32)
      if _, err := io.ReadFull(rand.Reader, key); err != nil {
        log.Fatal(err)
      }

      blockCipher, err := aes.NewCipher(key)
      if err != nil {
        log.Fatal(err)
      }

      aead, err := cipher.NewGCM(blockCipher)
      if err != nil {
        log.Fatal(err)
      }

      var encrypted = []byte{}
      var nonce = []byte{}
      // Encryption routine
      {
        msg := []byte("Some secret message")
        // note that the key must be rotated every 2^32 random nonces used otherwise
        // cipher text could be repeated
        nonce = make([]byte, 12)
        if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
          log.Fatal(err)
        }
        encrypted = aead.Seal(nil, nonce, msg, nil)
      }

      // Decryption routine
      {
        msg, err := aead.Open(nil, nonce, encrypted, nil)
        if err != nil {
          log.Fatal(err)
        }
        fmt.Printf("Decrypted: %s\n", msg)
      }
      ```
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      category: "security"
      related-packages: ['pkg:go/golang.org/x/crypto']
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_blocklist_rule-blocklist-sha1"
    languages:
      - "go"
    patterns:
      - pattern: |
          import "crypto/sha1"
    message: |
      The SHA-1 message-digest algorithm has been cryptographically broken and
      is unsuitable for further use. It is
      recommended that the SHA-3, or BLAKE2 family of algorithms be used for non-password based
      cryptographic hashes instead. For password based cryptographic hashes, consider using the
      bcrypt or Argon2id family of cryptographic hashes.

      Hashing values using [BLAKE2](https://pkg.go.dev/golang.org/x/crypto/blake2b):
      ```
      fileContents := []byte("some file contents to create hash for")
      blake2bHasher, err := blake2b.New512(nil)
      if err != nil {
        log.Fatal(err)
      }
      hashedValue := blake2bHasher.Sum(fileContents)
      fmt.Printf("%s\n", hex.EncodeToString(hashedValue))
      ```

      Hashing and securely comparing passwords using
      [Argon2id](https://pkg.go.dev/golang.org/x/crypto/argon2#hdr-Argon2id):
      ```
      type argonParameters struct {
        variant     string
        version     int
        memory      uint32
        iterations  uint32
        parallelism uint8
        saltLength  uint32
        keyLength   uint32
      }

      func (a argonParameters) StringFormat(salt, derivedKey []byte) string {
        encodedSalt := base64.RawStdEncoding.EncodeToString(salt)
        encodedKey := base64.RawStdEncoding.EncodeToString(derivedKey)

        return fmt.Sprintf("$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s",
          argon2.Version,
          a.memory,
          a.iterations,
          a.parallelism,
          encodedSalt,
          encodedKey,
        )
      }

      func main() {
        // Initialize Argon2id parameters
        p := argonParameters{
          memory:      64 * 1024,
          iterations:  3,
          parallelism: 2,
          saltLength:  16,
          keyLength:   32,
        }

        // Generate random salt (to be stored alongside derived hash key)
        salt := make([]byte, p.saltLength)
        if _, err := io.ReadFull(rand.Reader, salt); err != nil {
          log.Fatal(err)
        }

        usersPassword := []byte("User's Very S3cur3P4ss@rd@#$%")

        var derivedKey []byte
        // Create key hash derived from user's password
        {
          derivedKey = argon2.IDKey(usersPassword, salt, p.iterations, p.memory, p.parallelism,
      p.keyLength)
          // store p.StringFormat(...) result in a data store...
          fmt.Printf("%s\n", p.StringFormat(salt, derivedKey))
        }

        // Verify a user's password against key
        {
          keyToCompare := argon2.IDKey(usersPassword, salt, p.iterations, p.memory, p.parallelism,
      p.keyLength)

          // Use subtle.ConstantTimeCompare(..., ...) to ensure no side channel leaks used in timing
      attacks
          if subtle.ConstantTimeCompare(derivedKey, keyToCompare) == 1 {
            fmt.Printf("Passwords match\n")
          } else {
            fmt.Printf("Passwords do not match\n")
          }
        }
      }
      ```

      For more information on password storage see OWASP's guide:
      https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      category: "security"
      related-packages: ['pkg:go/golang.org/x/crypto']
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_crypto_rule-badtlssettings"
    languages:
      - "go"
    patterns:
      - pattern-either:
          - pattern: |
              tls.Config{..., CipherSuites: []$SLICE{..., $CIPHERS, ...}, ...}
          - pattern: |
              tls.CipherSuite{..., ID: $CIPHERS, ...}
      - metavariable-regex:
          metavariable: "$CIPHERS"
          regex: "((?!tls.TLS_AES_128_GCM_SHA256)|(?!tls.TLS_AES_256_GCM_SHA384)|(?!tls.TLS_CHACHA20_POLY1305_SHA256)| (?!tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)|(?!tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)|
            (?!tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384)|(?!tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384)| (?!tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305)|(?!tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256)|
            (?!tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305)|(?!tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256)| (?!tls.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256)|(?!tls.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384))"
    message: |
      Usage of a cryptographically insecure cipher suite has been detected. It is recommended that
      alternative ciphers be used instead. It is strongly recommended that all TLS connections
      use TLS 1.3 as Go will automatically choose the most secure cipher when negotiating the
      TLS handshake with client or servers. TLS 1.3 cipher suites are configured to require Perfect
      Forward Secrecy (PFS).
      PFS is an important property as it will ensure that past encrypted transmissions could not be
      decrypted
      if the TLS certificate was compromised.

      Example using TLS 1.3 for a Go server:
      ```
      cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
      if err != nil {
        log.Fatal(err)
      }

      cfg := &tls.Config{Certificates: []tls.Certificate{cert}, MinVersion: tls.VersionTLS13}
      srv := &http.Server{
        Addr:         ":8999",
        TLSConfig:    cfg,
        ReadTimeout:  time.Minute,
        WriteTimeout: time.Minute,
      }
      log.Fatal(srv.ListenAndServeTLS("", ""))
      ```

      If TLS 1.0-1.2 must be used, then the following list of ciphers should be chosen as they
      support
      Perfect Forward Secrecy (PFS):

      - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
      - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
      - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
      - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
      - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
      - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305


      Example `tls.Config` using the recommended cipher suites:
      ```
      cfg := &tls.Config{
          MinVersion: tls.VersionTLS12,
          CipherSuites: []uint16{
              tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
              tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
              tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
              tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
              tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
              tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
          },
      }
      ```

      For more information on cipher suites in Go see: https://go.dev/blog/tls-cipher-suites
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      cwe: "CWE-327"
      category: "security"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      related-packages: ['pkg:go/golang.org/x/crypto']
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_crypto_rule-insecure-ignore-host-key"
    languages:
      - "go"
    patterns:
      - pattern: "ssh.InsecureIgnoreHostKey(...)"
    message: |
      The application was found to ignore host keys. Host keys are important as
      they provide assurance that the client can prove that the host is trusted.
      By ignoring these host keys, it is impossible for the client to validate the
      connection is to a trusted host.

      For the `ssh.ClientConfig` `HostKeyCallback` property, consider using the
      [knownhosts](https://pkg.go.dev/golang.org/x/crypto/ssh/knownhosts) package that
      parses OpenSSH's `known_hosts` key database.

      Example configuration connecting to a known, trusted host:
      ```
      knownHostCallback, err := knownhosts.New("/home/user/.ssh/known_hosts")
      if err != nil {
        log.Fatal(err)
      }

      // Create client config using the knownHost callback function
      config := &ssh.ClientConfig{
        ...
        HostKeyCallback: knownHostCallback,
      }

      // Connect to ssh server
      conn, err := ssh.Dial("tcp", "localhost:22", config)
      if err != nil {
        log.Fatal("unable to connect: ", err)
      }
      defer conn.Close()
      ```
    metadata:
      shortDescription: "Key exchange without entity authentication"
      cwe: "CWE-322"
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      security-severity: "Medium"
      category: "security"
      related-packages: ['pkg:go/golang.org/x/crypto']
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_crypto_rule-tlsversion"
    languages:
      - "go"
    pattern-either:
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern-inside: |
                      tls.Config{...}
                  - pattern: |
                      MinVersion: $VAL
              - patterns:
                  - pattern-inside: |
                      $VAR = uint16($VAL)
                      ...
                  - pattern-inside: |
                      tls.Config{...}
                  - pattern: |
                      MinVersion: $VAR
          - metavariable-pattern:
              metavariable: $VAL
              pattern-either:
                - pattern: "tls.VersionTLS11"
                - pattern: "tls.VersionTLS10"
      - patterns:
          - pattern-inside: |
              tls.Config{...}
          - pattern: |
              MaxVersion: $ANYVAL
          - pattern-not-inside: |
              tls.Config{..., MinVersion: ..., ...}
    message: |
      TLS versions 1.1 and 1.0 were deprecated by the IETF in June 2018 due to 
      a number of attacks against the vulnerable versions. Use of a deprecated 
      TLS version may result in the unauthorized retrieval of sensitive 
      information. It is strongly recommended that all TLS connections
      use TLS 1.3 as Go will automatically choose the most secure cipher when 
      negotiating the TLS handshake with client or servers. TLS 1.3 cipher suites 
      are configured to require Perfect Forward Secrecy (PFS). PFS is an important 
      property as it will ensure that past encrypted transmissions could not be
      decrypted if the TLS certificate was compromised.

      Example using TLS 1.3 for a Go server:
      ```
      cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
      if err != nil {
        log.Fatal(err)
      }

      cfg := &tls.Config{Certificates: []tls.Certificate{cert}, 
                        MinVersion: tls.VersionTLS13}

      srv := &http.Server{
        Addr:         ":8999",
        TLSConfig:    cfg,
        ReadTimeout:  time.Minute,
        WriteTimeout: time.Minute,
      }
      log.Fatal(srv.ListenAndServeTLS("cert.pem", "key.pem"))
      ```
    metadata:
      shortDescription: "Use of deprecated TLS version"
      cwe: "CWE-310"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      category: "security"
      related-packages: ['pkg:go/golang.org/x/crypto']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "go_crypto_rule-weakkeystrength"
    languages:
      - "go"
    patterns:
      - pattern-either:
          - pattern: |
              rsa.GenerateKey(..., $ARG)
      - metavariable-comparison:
          metavariable: "$ARG"
          comparison: "$ARG < 2048"
    message: |
      The application is generating an RSA key that is less than the recommended 2048 bits.
      The National Institute of Standards and Technology (NIST) deprecated signing Digital
      Certificates that contained RSA Public Keys of 1024 bits in December 2010. While
      1024-bit RSA keys have not been factored yet, advances in compute may make it possible
      in the near future.

      To generate an RSA key of 2048 pass the number of bits as the second parameter to
      the `rsa.GenerateKey` function:
      ```
      import (
        "crypto/rand"
        "crypto/rsa"
      )

      func generate() {
        key, err := rsa.GenerateKey(rand.Reader, 2048)
        if err != nil {
          log.Fatal(err)
        }
      }
      ```
    metadata:
      shortDescription: "Inadequate encryption strength"
      cwe: "CWE-326"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      category: "security"
      related-packages: ['pkg:go/golang.org/x/crypto']
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_crypto_rule-weakrandsource"
    languages:
      - "go"
    patterns:
      - patterns:
          - pattern-inside: |
              import $IMPORT "math/rand"
              ...
          - pattern-not-inside: |
              import "crypto/rand"
          - pattern-either:
              - pattern: "$IMPORT.$METHOD(...)"
              - pattern: "rand.$METHOD(...)"
      - metavariable-regex:
          metavariable: "$METHOD"
          regex: "(Float32|Float64|Int31|Int31n|Int63|Int63n|NormalFloat64|Uint32|Uint64)"
    message: |
      Go's `math/rand` is not meant for use in generating random numbers for any cryptographic or
      security sensitive context. This includes generating random numbers that could be used in
      user specific identifiers or where the random number that is generated is considered to
      be secret.

      Replace all imports of `math/rand` with `crypto/rand`.
    metadata:
      shortDescription: "Use of cryptographically weak Pseudo-Random Number Generator (PRNG)"
      cwe: "CWE-338"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_file-permissions_rule-fileperm"
    languages:
      - "go"
    patterns:
      - pattern-either:
          - pattern: "os.Chmod(...,$MASK)"
          - pattern: "os.OpenFile(...,$MASK)"
          - pattern: "os.WriteFile(...,$MASK)"
      - metavariable-comparison:
          metavariable: "$MASK"
          comparison: "$MASK > 0o640"
          base: 8
    message: |
      The application was found setting file permissions to overly permissive values. Consider
      using the following values if the application user is the only process to access
      the file:

      - 0400 - read only access to the file
      - 0200 - write only access to the file
      - 0600 - read/write access to the file

      Example creating a file with read/write permissions for the application user:
      ```
      f, err := os.OpenFile("file.txt", os.O_CREATE, 0600)
      if err != nil {
        log.Fatal(err)
      }
      defer f.Close()
      // continue to work with file here
      ```

      For all other values please see:
      https://en.wikipedia.org/wiki/File-system_permissions#Numeric_notation
    metadata:
      shortDescription: "Incorrect permission assignment for critical resource"
      cwe: "CWE-732"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Medium"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_file-permissions_rule-mkdir"
    languages:
      - "go"
    patterns:
      - pattern-either:
          - pattern: "os.Mkdir(...,$MASK)"
          - pattern: "os.MkdirAll(...,$MASK)"
      - metavariable-comparison:
          metavariable: "$MASK"
          comparison: "$MASK > 0o750"
          base: 8
    message: |
      The application was found setting directory permissions to overly permissive values. Consider
      using the following values if the application user is the only process to access
      files in the directory specified:
      - 0700 - read/write access to the files in the directory

      Another common value is `0750` which allows the application user read/write access and group
      users to read the files contained in the directory.

      Example creating a directory with read/write permissions for only the application user:
      ```
      err := os.Mkdir("directory", 0700)
      if err != nil {
        log.Fatal(err)
      }
      ```

      For all other values please see:
      https://en.wikipedia.org/wiki/File-system_permissions#Numeric_notation
    metadata:
      shortDescription: "Incorrect permission assignment for critical resource"
      cwe: "CWE-732"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Medium"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_filesystem_rule-decompression-bomb"
    languages:
      - "go"
    mode: "taint"
    pattern-sources:
      - pattern: "gzip.NewReader(...)"
      - pattern: "zlib.NewReader(...)"
      - pattern: "bzip2.NewReader(...)"
      - pattern: "flate.NewReader(...)"
      - pattern: "lzw.NewReader(...)"
      - pattern: "tar.NewReader(...)"
      - pattern: "zip.NewReader(...)"
      - pattern: "zlib.NewReaderDict(...)"
      - pattern: "flate.NewReaderDict(...)"
      - pattern: "zip.OpenReader(...)"
    pattern-sanitizers:
      - patterns:
          - pattern: "io.LimitReader($TAINTED, ...)"
          - focus-metavariable: "$TAINTED"
    pattern-sinks:
      - patterns:
          - pattern: "io.Copy($DST, $TAINTED)"
          - focus-metavariable: "$TAINTED"
      - patterns:
          - pattern: "io.CopyBuffer($DST, $TAINTED, $BUF)"
          - focus-metavariable: "$TAINTED"
    message: |
      Directly decompressing files or buffers may lead to a potential Denial of Service (DoS)
      due to a decompression bomb. Decompression bombs are maliciously compressed files
      or data that decompresses to extremely large sizes. This can cause the process to run
      out of memory, or the disk to fill up.

      To protect against decompression bombs, an
      [io.LimitReader(...)](https://pkg.go.dev/io#LimitReader)
      should be used to limit how much can be read during the decompression routine.

      Example using `io.LimitReader` to protect against a decompression bomb:
      ```
      f, err := os.Open("some.gz")
      if err != nil {
        log.Fatal(err)
      }

      r, err := gzip.NewReader(f)
      if err != nil {
        log.Fatal(err)
      }

      const oneMegabyte = 1024 * 1024
      limitedReader := io.LimitReader(r, oneMegabyte)

      // use limitedReader to stop copying after 1 MB
      if _, err := io.Copy(os.Stdout, limitedReader); err != nil {
        log.Fatal(err)
      }
      ```
    metadata:
      shortDescription: "Improper handling of highly compressed data"
      cwe: "CWE-409"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "Medium"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_filesystem_rule-fileread"
    languages:
      - "go"
    mode: "taint"
    pattern-sources:
      - pattern: "os.Getenv(...)"
      - pattern: "fmt.Sprintf(...)"
      - pattern: "filepath.Join(...)"
      - pattern: "path.Join(...)"
      - patterns:
          - pattern-either:
              - pattern: "... + $TAINTED"
              - pattern: "... + $TAINTED + ..."
              - pattern: "$TAINTED + ..."
          - pattern-not: "\"...\" + $TAINTED"
          - pattern-not: "\"...\" + $TAINTED + \"...\""
          - pattern-not: "$TAINTED + \"...\""
          - pattern-not: "fmt.Sprintf(\"...\", \"...\")"
      - patterns:
          - pattern-either:
              - pattern: |
                  ($REQUEST : *http.Request).$SOURCE_METHOD
              - pattern: |
                  ($REQUEST : http.Request).$SOURCE_METHOD
          - metavariable-regex:
              metavariable: "$SOURCE_METHOD"
              regex: "^(BasicAuth|Body|Cookie|Cookies|Form|FormValue|GetBody|Host|MultipartReader|ParseForm|ParseMultipartForm|PostForm|PostFormValue|Referer|RequestURI|Trailer|TransferEncoding|UserAgent|URL)$"
    pattern-sanitizers:
      - patterns:
          - pattern-either:
              - pattern: |
                  $CLEAN := $PKG.Clean(...)
                  ...
                  if !strings.HasPrefix($CLEAN, "...") {...}
              - pattern: |
                  $CLEAN := $PKG.Clean(...)
                  ...
                  if strings.HasPrefix($CLEAN, "...") {...}
              - pattern: |
                  $CLEAN := $PKG.Clean(...)
                  ...
                  if strings.HasPrefix($CLEAN, "...") == false {...}
          - metavariable-regex:
              metavariable: "$PKG"
              regex: "^((file)?path)$"
    pattern-sinks:
      - pattern: "os.OpenFile(...)"
      - pattern: "os.Open(...)"
      - pattern: "os.ReadFile(...)"
      - pattern: "ioutil.ReadFile(...)"
    message: |
      The application dynamically constructs file or path information. If the path
      information comes from user input, it could be abused to read sensitive files,
      access other users data or aid in exploitation to gain further system access.

      User input should never be used in constructing paths or files for interacting
      with the filesystem. This includes filenames supplied by user uploads or downloads.
      If possible, consider hashing user input or replacing it with unique values.
      Additionally, use `filepath.Base` to only use the filename and not path information.
      Always validate the full path prior to opening or writing to any file.

      Example using `filepath.Base`, generating a unique filename without using
      user input to construct filepath information:
      ```
      type userData struct {
          id           string
          userFilename string
      }

      func newUserData(userFilename string) userData {
          return userData{
              id:           randomFileID(), // random id as the filename
              userFilename: userFilename,
          }
      }

      // randomFileID generates a random id, to be used as a filename
      func randomFileID() string {
          id := make([]byte, 16)
          if _, err := io.ReadFull(rand.Reader, id); err != nil {
              log.Fatal(err)
          }
          return hex.EncodeToString(id)
      }

      func main() {

          // user input, saved only as a reference
          data := newUserData("../../possibly/malicious")

          // restrict all file access to this path
          const basePath = "/tmp/"

          // resolve the full path, but only use our random generated id
          resolvedPath, err := filepath.Join(basePath, filepath.Base(data.id))
          if err != nil {
              log.Fatal(err)
          }

          // verify the path is prefixed with our basePath
          if !strings.HasPrefix(resolvedPath, basePath) {
              log.Fatal("path does not start with basePath")
          }
          // process / work with file
      }
      ```

      For more information on path traversal issues see OWASP:
      https://owasp.org/www-community/attacks/Path_Traversal
    metadata:
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      cwe: "CWE-22"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Medium"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_filesystem_rule-httprootdir"
    languages:
      - "go"
    patterns:
      - pattern-either:
          - pattern: |
              import $NET "net/http"
              ...
              $NET.Dir("/")
          - pattern: |
              import "net/http"
              ...
              http.Dir("/")
    message: |
      The application is potentially exposing the entire filesystem by mounting the root
      directory `/` to an HTTP handler function. Anyone who is able to access this HTTP
      server may be able to access any file that the HTTP server has access to.

      Restrict the `http.Dir` path to only a specific folder instead of the entire
      filesystem.

      Example server only allowing directory listing on a public directory:
      ```
      const path = "/var/www/html/public"
      fs := http.FileServer(http.Dir(path))
      log.Fatal(http.ListenAndServe(":9000", fs))
      ```
    metadata:
      shortDescription: "Files or directories accessible to external parties"
      cwe: "CWE-552"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Medium"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_filesystem_rule-poorwritepermissions"
    languages:
      - "go"
    patterns:
      - pattern-either:
          - pattern: |
              ioutil.WriteFile(..., ..., $ARG)
      - metavariable-comparison:
          metavariable: "$ARG"
          comparison: "$ARG > 0o600"
          base: 8
    message: |
      The application was found setting file permissions to overly permissive values. Consider
      using the following values if the application user is the only process to access
      the file:

      - 0400 - read only access to the file
      - 0200 - write only access to the file
      - 0600 - read/write access to the file

      Example writing file contents with read/write permissions for the application user:
      ```
      dat := []byte("sensitive data")
      if err := os.WriteFile("file.txt", dat, 0600); err != nil {
        log.Fatal(err)
      }
      ```

      For all other values please see:
      https://en.wikipedia.org/wiki/File-system_permissions#Numeric_notation
    metadata:
      shortDescription: "Incorrect default permissions"
      cwe: "CWE-276"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Medium"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_filesystem_rule-tempfiles"
    languages:
      - "go"
    patterns:
      - pattern-either:
          - pattern: |
              os.WriteFile("$ARG", ...)
          - pattern: |
              ioutil.WriteFile("$ARG", ...)
          - pattern: |
              os.OpenFile("$ARG", <... os.O_CREATE ...>, ...)
          - pattern: |
              os.Create("$ARG")
      - metavariable-regex:
          metavariable: "$ARG"
          regex: "(/tmp/.*|/var/tmp/.*)"
    message: |
      The application was found creating files in shared system temporary directories
      (`/tmp` or `/var/tmp`) without using the `os.CreateTemp` function. Depending
      on how the application uses this temporary file, an attacker may be able to create
      symlinks that point to other files prior to the application creating or writing
      to the target file, leading to unintended files being created or overwritten.

      Example using `os.CreateTemp` in an application restricted directory:
      ```
      // assumes /opt/appdir/ is chown'd to the running application user
      if err := os.MkdirAll("/opt/appdir/restricted", 0700); err != nil {
        log.Fatal(err)
      }

      // create a temporary file in the restricted directory in the form of temp-952569059.txt
      f, err := os.CreateTemp("/opt/appdir/restricted", "temp-*.txt")
      if err != nil {
        log.Fatal(err)
      }

      defer f.Close()
      // clean up on exit
      defer os.Remove(f.Name())
      // work with file
      ```
    metadata:
      shortDescription: "Creation of temporary file with insecure permissions"
      cwe: "CWE-378"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Medium"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_filesystem_rule-ziparchive"
    languages:
      - "go"
    mode: "taint"
    pattern-sources:
      - pattern: "zip.OpenReader(...)"
      - pattern: "tar.OpenReader(...)"
    pattern-sanitizers:
      - patterns:
          - pattern-either:
              - pattern: |
                  $CLEAN := $PKG.Clean(...)
                  ...
                  if !strings.HasPrefix($CLEAN, "...") {...}
              - pattern: |
                  $CLEAN := $PKG.Clean(...)
                  ...
                  if strings.HasPrefix($CLEAN, "...") {...}
              - pattern: |
                  $CLEAN := $PKG.Clean(...)
                  ...
                  if strings.HasPrefix($CLEAN, "...") == false {...}
          - metavariable-regex:
              metavariable: "$PKG"
              regex: "^((file)?path)$"
    pattern-sinks:
      - pattern: "filepath.Join(...)"
    message: |
      The application may be vulnerable to a path traversal if it extracts untrusted archive files.
      This vulnerability is colloquially known as 'Zip Slip'. Archive files may contain folders
      which,
      when extracted, may write outside of the intended directory. This is exploited by including
      path traversal characters such as `../../other/directory` to overwrite or place files in system
      or application directories.

      Extra care must be taken when extracting archive files as there are numerous concerns:

      - Limit the size of the zip archive as it may contain "Zip Bombs", files that extract to
      extremely
      large sizes.
      - If possible, generate unique filenames instead of using the archives file names, as it may be
      possible for users to overwrite files if the filenames are the same.
      - Validate file paths are written with a prefixed, known trusted directory.
      - Only process regular files and not symbolic links, as some applications may attempt to
      read/follow
      the symbolic link, leading to arbitrary file read / write vulnerabilities.


      Example of securely processing an archive file:
      ```
      r, err := zip.OpenReader("trusted.zip")
      if err != nil {
        log.Fatal(err)
      }

      // Ensure archive contains only the expected number of files
      const expectedFileCount = 10
      if len(r.File) > expectedFileCount {
        log.Fatalf("too many files in archive: %d\n", len(r.File))
      }

      // One approach is to sum up all files before attempting to process
      // them.
      const totalAllowedSize = 1024 * 1024 * 10 // 10MB
      var totalSize uint64
      for _, f := range r.File {
        totalSize += f.UncompressedSize64
      }

      if totalSize > totalAllowedSize {
        log.Fatalf("archive exceeds total allowed size: %d\n", totalSize)
      }

      // configure a max size per file allowed
      const maxFileSize = 1024 * 1024 // 1 MB

      // set restricted basePath
      const basePath = "/var/restricted/"

      // iterate over the files in the archive
      for _, f := range r.File {

        // Ensure uncompressed size does not exceed our allowed file size
        if f.UncompressedSize64 > maxFileSize {
          log.Printf("skipping file as it exceeds maxFileSize: %s\n", f.Name)
          continue
        }

        // Ensure file is a regular file and not a symbolic link or has other mode type
        // bits set
        if !f.Mode().IsRegular() {
          log.Printf("skipping non regular file: %s\n", f.Name)
          continue
        }

        // if possible consider not using the name at all, but generating a random id instead.
        // If the filename must be used, extract the base name and not folder path information
        name := filepath.Base(f.Name)

        // Join the file name to the basePath.
        resolvedPath := filepath.Join(basePath, name)

        // Application must still verify the path is prefixed by the basePath
        if !strings.HasPrefix(resolvedPath, basePath) {
          log.Fatal("path does not start with basePath")
        }

        // process / work with file
      }
      ```

      If the application must process directory names as well, use the following code:
      ```
      // Join the cleaned name to the basePath, note if 'name' starts with `../../` it
      // will still allow for traversal, so you _must_ verify the path prefix below
      resolvedPath := filepath.Join(basePath, filepath.Clean(name))

      // Application must still verify the path is prefixed by the basePath
      if !strings.HasPrefix(resolvedPath, basePath) {
        log.Fatal("path does not start with basePath")
      }

      // process / work with file
      ```
    metadata:
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      cwe: "CWE-22"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Medium"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_http_rule-http-serve"
    languages:
      - "go"
    patterns:
      - pattern-inside: |
          import "net/http"
          ...
      - pattern-either:
          - pattern: "http.ListenAndServe(...)"
          - pattern: "http.ListenAndServeTLS(...)"
          - pattern: "http.Serve(...)"
          - pattern: "http.ServeTLS(...)"
          - patterns:
              - pattern-not-inside: |
                  &http.Server{
                    ...,
                    ReadHeaderTimeout: ...,
                    ...,
                  }
              - pattern-not-inside: |
                  &http.Server{
                    ...,
                    ReadTimeout: ...,
                    ...,
                  }
              - pattern-not-inside: |
                  $S = &http.Server{
                    ...,
                  }
                  $S.ReadHeaderTimeout = ...
                  ...
              - pattern-not-inside: |
                  $S = &http.Server{
                    ...,
                  }
                  $S.ReadTimeout = ...
                  ...
              - pattern: |
                  &http.Server{
                    ...,
                  }
    message: |
      Go's `net/http` serve functions may be vulnerable to resource consumption attacks if timeouts
      are not properly configured
      prior to starting the HTTP server. An adversary may open up thousands of connections but never
      complete sending all data,
      or never terminate the connections. This may lead to the server no longer accepting new
      connections.

      To protect against this style of resource consumption attack, timeouts should be set in the
      `net/http` server prior to calling
      the listen or serve functions. What this means is that the default `http.ListenAndServe` and
      `http.Serve` functions should not
      be used in a production setting as they are unable to have timeouts configured. Instead a
      custom `http.Server` object must be
      created with the timeouts configured.

      Example setting timeouts on a `net/http` server:
      ```
      // All values chosen below are dependent on application logic and
      // should be tailored per use-case
      srv := &http.Server{
        Addr: "localhost:8000",
        // ReadHeaderTimeout is the amount of time allowed to read
        // request headers. The connection's read deadline is reset
        // after reading the headers and the Handler can decide what
        // is considered too slow for the body. If ReadHeaderTimeout
        // is zero, the value of ReadTimeout is used. If both are
        // zero, there is no timeout.
        ReadHeaderTimeout: 15 * time.Second,

        // ReadTimeout is the maximum duration for reading the entire
        // request, including the body. A zero or negative value means
        // there will be no timeout.
        //
        // Because ReadTimeout does not let Handlers make per-request
        // decisions on each request body's acceptable deadline or
        // upload rate, most users will prefer to use
        // ReadHeaderTimeout. It is valid to use them both.
        ReadTimeout: 15 * time.Second,

        // WriteTimeout is the maximum duration before timing out
        // writes of the response. It is reset whenever a new
        // request's header is read. Like ReadTimeout, it does not
        // let Handlers make decisions on a per-request basis.
        // A zero or negative value means there will be no timeout.
        WriteTimeout: 10 * time.Second,

        // IdleTimeout is the maximum amount of time to wait for the
        // next request when keep-alives are enabled. If IdleTimeout
        // is zero, the value of ReadTimeout is used. If both are
        // zero, there is no timeout.
        IdleTimeout: 30 * time.Second,
      }

      // For per request timeouts applications can wrap all `http.HandlerFunc(...)` in
      // `http.TimeoutHandler`` and specify a timeout, but note the TimeoutHandler does not
      // start ticking until all headers have been read.

      // Listen with our custom server with timeouts configured
      if err := srv.ListenAndServe(); err != nil {
        log.Fatal(err)
      }
      ```
      For more information on the `http.Server` timeouts, see: https://pkg.go.dev/net/http#Server

      For information on setting request based timeouts, see:
      https://pkg.go.dev/net/http#TimeoutHandler

      For more information on the Slowloris attack see:
      https://en.wikipedia.org/wiki/Slowloris_(computer_security)
    metadata:
      shortDescription: "Allocation of resources without limits or throttling"
      cwe: "CWE-770"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      security-severity: "Low"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_injection_rule-ssrf"
    languages:
      - "go"
    mode: taint
  # At the moment we cannot use the function calls both as sources and sinks 
  # (which would be a valid use case for SSRF); this would require the 
  # https://semgrep.dev/docs/writing-rules/experiments/taint-labels/ which is
  # experimental.
    pattern-sources:
      - patterns:
          - pattern-not-inside: |
              import "testing"
              ...
          - pattern-either:
              - pattern: "os.Stdin"
              - pattern: "os.Getenv(...)"
              - pattern: |
                  ($REQ: *http.Request).$ANY
              - pattern: |
                  ($REQ: http.Request).$ANY
              - patterns:
                  - pattern: "($REQ : *http.Request)"
                  - pattern-inside: |
                      func $FUNC( $W http.ResponseWriter, $R *http.Request, ...) {
                        ...
                      }
    pattern-sinks:
      - pattern: "http.Head(...)"
      - pattern: "http.Get(...)"
      - pattern: "http.Post(...)"
      - pattern: "http.PostForm(...)"
      - pattern: "http.NewRequest($METHOD,...)"
      - pattern: "http.DefaultClient.Head(...)"
      - pattern: "http.DefaultClient.Get(...)"
      - pattern: "http.DefaultClient.Post(...)"
      - pattern: "http.DefaultClient.PostForm(...)"
      - pattern: "http.NewRequestWithContext($CONTEXT, $METHOD, ...)"
      - pattern: "ftp.Dial(...)"
      - pattern: "ldap.DialURL(...)"
      - pattern: "smtp.Dial(...)"
      - pattern: "retryablehttp.NewRequest($METHOD, ..., $BODY)"
      - patterns:
          - pattern-inside: |
              $C := retryablehttp.NewClient()
              ...
          - pattern-either:
              - pattern: "$C.Get(...)"
              - pattern: "$C.Post(..., $BODYTYPE, $BODY)"
              - pattern: "$C.PostForm(..., $VALS)"
              - pattern: "$C.Head(...)"
    pattern-propagators:
      - pattern: $R := $D.NewDecoder($P)
        from: $P
        to: $R
      - pattern: $S.Decode(&$P)
        from: $S
        to: $P
      - pattern: $S.Decode($P)
        from: $S
        to: $P
      - pattern: $S.Unmarshal($B, &$P)
        from: $B
        to: $P
      - pattern: $S.Unmarshal($B, $P)
        from: $B
        to: $P
    message: |
      Server-Side-Request-Forgery (SSRF) exploits backend systems that initiate requests to third
      parties.
      If user input is used in constructing or sending these requests, an attacker could supply
      malicious
      data to force the request to other systems or modify request data to cause unwanted actions.

      Ensure user input is not used directly in constructing URLs or URIs when initiating requests
      to third party
      systems from back end systems. Care must also be taken when constructing payloads using user
      input. Where
      possible restrict to known URIs or payloads. Consider using a server side map where key's are
      used to return
      URLs such as `https://site/goto?key=1` where `{key: 1, url: 'http://some.url/', key: 2, url:
      'http://...'}`.

      If you must use user supplied input for requesting URLs, it is strongly recommended that the
      HTTP client
      chosen allows you to customize and block certain IP ranges at the network level. By blocking
      RFC 1918
      addresses or other network address ranges, you can limit the severity of a successful SSRF
      attack. Care must
      also be taken to block certain protocol or address formatting such as IPv6.

      If you can not block address ranges at the client level, you may want to run the HTTP client
      as a protected
      user, or in a protected network where you can apply IP Table or firewall rules to block access
      to dangerous
      addresses. Finally, if none of the above protections are available, you could also run a
      custom HTTP proxy
      and force all requests through it to handle blocking dangerous addresses.

      Example HTTP client that disallows access to loopback and RFC-1918 addresses
      ```
      // IsDisallowedIP parses the ip to determine if we should allow the HTTP client to continue
      func IsDisallowedIP(hostIP string) bool {
        ip := net.ParseIP(hostIP)
        return ip.IsMulticast() || ip.IsUnspecified() || ip.IsLoopback() || ip.IsPrivate()
      }

      // SafeTransport uses the net.Dial to connect, then if successful check if the resolved
      // ip address is disallowed. We do this due to hosts such as localhost.lol being resolvable to
      // potentially malicious URLs. We allow connection only for resolution purposes.
      func SafeTransport(timeout time.Duration) *http.Transport {
        return &http.Transport{
          DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
            c, err := net.DialTimeout(network, addr, timeout)
            if err != nil {
              return nil, err
            }
            ip, _, _ := net.SplitHostPort(c.RemoteAddr().String())
            if IsDisallowedIP(ip) {
              return nil, errors.New("ip address is not allowed")
            }
            return c, err
          },
          DialTLS: func(network, addr string) (net.Conn, error) {
            dialer := &net.Dialer{Timeout: timeout}
            c, err := tls.DialWithDialer(dialer, network, addr, &tls.Config{})
            if err != nil {
              return nil, err
            }

            ip, _, _ := net.SplitHostPort(c.RemoteAddr().String())
            if IsDisallowedIP(ip) {
              return nil, errors.New("ip address is not allowed")
            }

            err = c.Handshake()
            if err != nil {
              return c, err
            }

            return c, c.Handshake()
          },
          TLSHandshakeTimeout: timeout,
        }
      }

      func httpRequest(requestUrl string) {
        const clientConnectTimeout = time.Second * 10
        httpClient := &http.Client{
          Transport: SafeTransport(clientConnectTimeout),
        }
        resp, err := httpClient.Get(requestUrl)
        if err != nil {
          log.Fatal(err)
        }
        defer resp.Body.Close()
        // work with resp
      }
      ```

      For more information on SSRF see OWASP:
      https://owasp.org/www-community/attacks/Server_Side_Request_Forgery
    metadata:
      shortDescription: "Server Side Request Forgery (SSRF)"
      cwe: "CWE-918"
      owasp:
        - "A1:2017-Injection"
        - "A10:2021-Server-Side Request Forgery"
      security-severity: "Medium"
      category: "security"
      related-packages: ['pkg:go/github.com/go-ldap/ldap', 'pkg:go/github.com/jlaffaye/ftp', 'pkg:go/github.com/hashicorp/go-retryablehttp']
      license: Apache 2.0 (c) gosec
    severity: "WARNING"


  - id: "go_injection_rule-template-injection"
    languages:
      - "go"
    patterns:
      - pattern-either:
          - patterns:
              - pattern: "template.HTML($IN)"
              - pattern-not: "template.HTML(\"...\")"
          - patterns:
              - pattern: "template.JS($IN)"
              - pattern-not: "template.JS(\"...\")"
          - patterns:
              - pattern: "template.URL($IN)"
              - pattern-not: "template.URL(\"...\")"
          - patterns:
              - pattern: "template.HTMLAttr($IN)"
              - pattern-not: "template.HTMLAttr(\"...\")"
    message: |
      Cross Site Scripting (XSS) is an attack which exploits a web application or system to treat
      user input
      as markup or script code. It is important to encode the data depending on the specific context
      it
      is used in. There are at least six context types:

      - Inside HTML tags `<div>context 1</div>`
      - Inside attributes: `<div class="context 2"></div>`
      - Inside event attributes `<button onclick="context 3">button</button>`
      - Inside script blocks: `<script>var x = "context 4"</script>`
      - Unsafe element HTML assignment: `element.innerHTML = "context 5"`
      - Inside URLs: `<iframe src="context 6"></iframe><a href="context 6">link</a>`

      Script blocks alone have multiple ways they need to be encoded. Extra care must be taken if
      user input
      is ever output inside of script tags.

      User input that is displayed within the application must be encoded, sanitized or validated
      to ensure it cannot be treated as HTML or executed as Javascript code. Care must also be
      taken
      to not mix server-side templating with client-side templating, as the server-side templating
      will
      not encode things like {{ 7*7 }} which may execute client-side templating features.

      It is _NOT_ advised to encode user input prior to inserting into a data store. The data will
      need to be
      encoded depending on context of where it is output. It is much safer to force the displaying
      system to
      handle the encoding and not attempt to guess how it should be encoded.

      Use of the following template types with user input denotes a security risk:

      - [template.HTML](https://pkg.go.dev/html/template#HTML)
      - [template.JS](https://pkg.go.dev/html/template#JS)
      - [template.URL](https://pkg.go.dev/html/template#URL)
      - [template.HTMLAttr](https://pkg.go.dev/html/template#HTMLAttr)

      Either remove these types from the application or hardcode as const strings prior
      to conversion:
      ```
      testTemplate, err := template.New("testTemplate").Funcs(template.FuncMap{
        "SafeHTML": func() template.HTML {
          const safeHTML = "<div>hardcoded, safe html</div>"
          return template.HTML(safeHTML)
        },
      }).Parse(`<html><body>{{ SafeHTML }}</body>`)
      if err != nil {
        log.Fatal(err)
      }

      if err := testTemplate.Execute(os.Stdout, nil); err != nil {
        log.Fatal(err)
      }
      ```
    metadata:
      shortDescription: "Improper neutralization of input during web page generation ('Cross-site Scripting')"
      cwe: "CWE-79"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "Medium"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_leak_rule-pprof-endpoint"
    languages:
      - "go"
    patterns:
      - pattern-inside: |
          import (
            "net/http/pprof"
          )
          ...
      - pattern-either:
          - pattern: "http.ListenAndServe(...)"
          - pattern: "http.ListenAndServeTLS(...)"
          - pattern: "http.Serve(...)"
          - pattern: "http.ServeTLS(...)"
    message: |
      Go has a built in profiling service that is enabled by starting an HTTP server with
      `net/http/pprof` imported. The `/debug/pprof` endpoint does not require any
      authentication and can be accessed by anonymous users. This profiling endpoint
      can leak sensitive information and should not be enabled in production.

      To remediate this, remove the `net/http/pprof` import from the file.
    metadata:
      shortDescription: "Active debug code (pprof enabled)"
      cwe: "CWE-489"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      security-severity: "Medium"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "ERROR"
  - id: "go_memory_rule-integer-overflow"
    languages:
      - "go"
    patterns:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  $X, ... := strconv.Atoi(...)
                  ...
              - pattern-either:
                  - pattern: "int32($X)"
                  - pattern: "int16($X)"
    message: |
      Golang's `int` type size depends on the architecture of where the application is running. For
      32-bit systems, `int` is
      32-bit, for 64-bit systems, `int` will be 64-bit. By calling `strconv.Atoi` with a large
      number, the integer may overflow
      if the `int` return value is type converted into a smaller type (`int32` or `int16`). This
      could cause unexpected application
      behavior depending on how the resultant value is used.

      Prior to running any type conversion, check that the value returned from `strconv.Atoi` will
      fit in the resulting integer.

      Example of checking the return value before type conversion:
      ```
      bigValue, _ := strconv.Atoi("32768")
      if bigValue > math.MaxInt16 {
        log.Fatal("value too large to fit in int16")
      }
      value := int16(bigValue)
      fmt.Println(value)
      ```

      For more information on integer min/max constants see: https://pkg.go.dev/math#pkg-constants
    metadata:
      shortDescription: "Integer overflow or wraparound"
      cwe: "CWE-190"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "Medium"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "ERROR"
  - id: "go_memory_rule-memoryaliasing"
    languages:
      - "go"
    patterns:
      - pattern-either:
          - pattern: |
              for ..., $ARG := range $SLICE {
                <... &($ARG) ...>
              }
          - pattern: |
              for ..., $ARG := range $SLICE {
                <... func() { <... &$ARG ...> } ...>
              }
          - pattern: |
              for ..., $ARG := range $SLICE {
                <... $X(..., <... &$ARG ...>, ...) ...>
              }
      - pattern-not: |
          for ..., $ARG := range $SLICE {
            <... *$ARG ...>
          }
      - pattern-not-inside: |-
          for ..., $ARG := range $SLICE { return ... }
    message: |
      Go's `for ... range` statements create an iteration variable for each iteration of the loop.
      By taking the address of this iteration variable, the value of the address will be re-used
      and always point to the same location in memory. This can have unexpected behavior if the
      address is stored or re-used.

      This can be fixed by:
      - Not referencing the address of the variable
      - Re-assigning the iteration variable to a new variable
      - Using the address of the indexed variable

      Example not referencing the address:
      ```
      type someStruct struct {
        x int
      }

      for _, n := range []someStruct{{1}, {2}, {3}, {4}} {
        fmt.Printf("%d\n", n.x)
      }
      ```

      Example reassigning the iteration variable to a new variable:
      ```
      type someStruct struct {
        x int
      }

      for _, n := range []someStruct{{1}, {2}, {3}, {4}} {
        p := n
        fmt.Printf("%p\n", &p)
      }
      ```

      Example using the address of the indexed variable:
      ```
      type someStruct struct {
        x int
      }

      structData := []someStruct{{1}, {2}, {3}, {4}}
      for idx := range structData {
        fmt.Printf("%p\n", &structData[idx])
      }
      ```

      For more information on how the `for ... range` statement works see:
      https://go.dev/ref/spec#For_statements
    metadata:
      shortDescription: "Incorrect access of indexable resource ('Range Error')"
      cwe: "CWE-118"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      security-severity: "Info"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_network_rule-bind-to-all-interfaces"
    languages:
      - "go"
    patterns:
      - pattern-either:
          - pattern: "net.Listen(..., \"$ADDR\")"
          - pattern: "tls.Listen(..., \"$ADDR\", ...)"
      - metavariable-regex:
          metavariable: "$ADDR"
          regex: "^(0\\.0\\.0\\.0|\\[::\\])?(:[0-9]*)?$"
    message: |
      Binding to all network interfaces can potentially open up a service to
      traffic on unintended interfaces, that may not be properly documented or
      secured. By passing "0.0.0.0" as the address to the `Listen` family of functions,
      the application will bind to all interfaces.

      Consider passing in the interface ip address through an environment variable,
      configuration file, or by determining the primary interface(s) IP address.

      Example getting the IP address from an environment variable `IP_ADDRESS`:
      ```
      addr := os.Getenv("IP_ADDRESS")
      listener, err := net.Listen("tcp", addr)
      if err != nil {
        log.Fatal(err)
      }
      ```
    metadata:
      shortDescription: "Binding to an unrestricted IP address"
      cwe: "CWE-1327"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      security-severity: "Low"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_sql_rule-concat-sqli"
    languages:
      - "go"
    mode: taint
    pattern-sources:
      - patterns:
          - pattern: "fmt.Sprintf(...)"
          - pattern-not: |
              fmt.Sprintf("...", "...")
      - patterns:
          - pattern: |
              "..." + $X
          - pattern-not: |
              "..." + "..."
      - pattern: |
          ($SB : strings.Builder).String()
    pattern-sinks:
      - patterns:
          - pattern: "$DB.$METHOD(...)"
          - metavariable-regex:
              metavariable: $METHOD
              regex: "^(Exec(Context)?|Query(Context)?|QueryRow(Context)?)$"
    message: |
      SQL Injection is a critical vulnerability that can lead to data or system compromise. By
      dynamically generating SQL query strings, user input may be able to influence the logic of
      the SQL statement. This could lead to an adversary accessing information they should
      not have access to or in some circumstances, being able to execute OS functionality or code.

      Replace all dynamically generated SQL queries with parameterized queries. In situations where
      dynamic queries must be created, never use direct user input, but instead use a map or
      dictionary of valid values and resolve them using a user supplied key.

      For example, some database drivers do not allow parameterized queries for `>` or `<` comparison
      operators. In these cases, do not use a user supplied `>` or `<` value, but rather have the
      user
      supply a `gt` or `lt` value. The alphabetical values are then used to look up the `>` and `<`
      values to be used in the construction of the dynamic query. The same goes for other queries
      where
      column or table names are required but cannot be parameterized.

      Example using parameterized queries with `sql.Query`:
      ```
      rows, err := db.Query("SELECT * FROM users WHERE userName = ?", userName)
      if err != nil {
          return nil, err
      }
      defer rows.Close()
      for rows.Next() {
        // ... process rows
      }
      ```

      For more information on SQL Injection see OWASP:
      https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
    metadata:
      shortDescription: "Improper neutralization of special elements used in an SQL command ('SQL Injection')"
      cwe: "CWE-89"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_subproc_rule-subproc"
    languages:
      - "go"
    patterns:
      - pattern-either:
          - patterns:
              - pattern: "exec.CommandContext($CTX, $EXE, ...)"
              - pattern-not: "exec.CommandContext($CTX, \"...\", ...)"
          - patterns:
              - pattern: "exec.Command($EXE, ...)"
              - pattern-not: "exec.Command(\"...\", ...)"
          - patterns:
              - pattern: "syscall.ForkExec($EXE, ...)"
              - pattern-not: "syscall.ForkExec(\"...\", ...)"
          - patterns:
              - pattern: "syscall.StartProcess($EXE, ...)"
              - pattern-not: "syscall.StartProcess(\"...\", ...)"
    message: |
      OS command injection is a critical vulnerability that can lead to a full system
      compromise as it may allow an adversary to pass in arbitrary commands or arguments
      to be executed.

      User input should never be used in constructing commands or command arguments
      to functions which execute OS commands. This includes filenames supplied by
      user uploads or downloads.

      Ensure your application does not:

      - Use user-supplied information in the process name to execute.
      - Use user-supplied information in an OS command execution function which does
      not escape shell meta-characters.
      - Use user-supplied information in arguments to OS commands.

      The application should have a hardcoded set of arguments that are to be passed
      to OS commands. If filenames are being passed to these functions, it is
      recommended that a hash of the filename be used instead, or some other unique
      identifier. It is strongly recommended that a native library that implements
      the same functionality be used instead of using OS system commands, due to the
      risk of unknown attacks against third party commands.

      If operating in Windows environments, when specifying the OS command, ensure
      the application uses the full path
      information, otherwise the OS may attempt to look up which process to execute
      and could be vulnerable to untrusted search path vulnerabilities (CWE-426).

      Example of safely executing an OS command:
      ```
      userData := []byte("user data")
      // create a temporary file in the application specific directory
      f, err := ioutil.TempFile("/var/app/restricted", "temp-*.dat")
      if err != nil {
        log.Fatal(err)
      }

      if _, err := f.Write(userData); err != nil {
        log.Fatal(err)
      }

      if err := f.Close(); err != nil {
        log.Fatal(err)
      }

      // pass the full path to the binary and the name of the temporary file
      // instead of any user supplied filename
      out, err := exec.Command("/bin/cat", f.Name()).Output()
      if err != nil {
        log.Fatal(err)
      }
      ```

      For more information on OS command injection, see OWASP's guide:
      https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html
    metadata:
      shortDescription: "Improper neutralization of special elements used in an OS command ('OS Command Injection')"
      cwe: "CWE-78"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "WARNING"
  - id: "go_unsafe_rule-unsafe"
    languages:
      - "go"
    patterns:
      - pattern-either:
          - pattern: "unsafe.Alignof(...)"
          - pattern: "unsafe.Offsetof(...)"
          - pattern: "unsafe.Sizeof(...)"
          - pattern: "unsafe.Pointer(...)"
    message: |
      The `unsafe` package in Go allows low-level access to memory management features.
      This includes pointers and direct access to memory. The Go compiler will no longer
      be able to enforce type safety when working with the `unsafe` pointer types.

      While powerful, access to these functions can lead to many security related issues
       such as:

      - [Buffer overflows](https://owasp.org/www-community/vulnerabilities/Buffer_Overflow) which
      can lead to code execution.
      - [Use after free](https://owasp.org/www-community/vulnerabilities/Using_freed_memory) which
      can lead to code execution.
      - [Information/Memory leaks](https://owasp.org/www-community/vulnerabilities/Memory_leak)
      which can leak sensitive information, including data which can
      defeat other protection mechanisms or cause the system to run out of memory.

      Unless required, all calls to the `unsafe` package should be removed.
    metadata:
      shortDescription: "Use of inherently dangerous function (unsafe package)"
      cwe: "CWE-242"
      owasp:
        - "A9:2017-Using Components with Known Vulnerabilities"
        - "A06:2021-Vulnerable and Outdated Components"
      security-severity: "High"
      category: "security"
      license: Apache 2.0 (c) gosec
    severity: "INFO"
  - id: html_django_rule_reflected_xss
    languages:
      - html
    severity: ERROR
    message: |
      Global disabling of autoescape in Django HTML Template with {% autoescape off/None %} can lead to reflected XSS attacks if a user controlled input is used in the template. 
      Cancelling escaping of a user controlled variable with | escapejs or | safe can lead to XSS attacks, as the variable can contain javascript code and would be executed. Make sure not to use escapejs on user-controlled variables. 
      Always use quotes ("..." or '...') around template variables in HTML attributes, so that Django's auto-escaping will HTML-escape the value: '{{variable}}'
    metadata:
      cwe: CWE-79
      owasp:
        - A7:2017-Cross-Site Scripting (XSS)
        - A03:2021-Injection
      related-packages:
        - pkg:pypi/django
      category: security
      shortDescription: Improper neutralization of user input rendered in HTML ('XSS')
      security-severity: High
      confidence: Medium
      license: License Not Found
    patterns:
      - pattern-either:
          - pattern-regex: '\{\% autoescape off \%\}'
          - pattern-regex: '\{\% autoescape None \%\}'
          - pattern-regex: '\{\{.* \| escapejs'
          - pattern-regex: '\{\{.* \| safe\s'
          - pattern: <$BALISE ... $CLASS={{ $INPUT }} ...>
      - pattern-not: <$BALISE ... $CLASS='{{ $INPUT }}' ...>
  - id: html_generic_rule_reflected_xss
    languages:
      - html
    severity: ERROR
    message: |
      User controlled variable in <script> tags get executed in document.write() or eval() methods, leading to XSS vulnerability. 
      User controlled variable gets executed with .innerHTML and .outerHTML, leading to XSS vulnerability.
      User controlled variable in CSS <style> can be executed with :expression(). 
      It can also be executed if written in backsticks ``. 
      A url in (href|src|action|data|http-equiv|style|background-image) can lead to XSS attacks if containing user controlled input and if not properly sanitized. Make sure to sanitize the user controlled urls in python using django.utils.http.urlencode to mitigate XSS vulnerability. 
      Before using a user controlled variable in these functions, make sure to escape it in python using django.utils.html.escapejs. Avoid as much as possible using this dangerous methods. 

    metadata:
      cwe: CWE-79
      owasp:
        - A7:2017-Cross-Site Scripting (XSS)
        - A03:2021-Injection
      category: security
      shortDescription: Improper neutralization of user input rendered in HTML ('XSS')
      security-severity: High
      confidence: Low
      license: License Not Found
    pattern-either:
      - patterns:
          - pattern-inside: |
              <script> 
                ...
              </script>
          - pattern-either:
              - pattern-regex: document\.write\(\s*[^'"]*\(([^'"]*)\);
              - pattern-regex: eval([^'"]*)
      - pattern-regex: document\.write\("\<script\>.*".*"\<\/script\>"\);
      - pattern-regex: (\.(inner|outer)HTML)
      - patterns:
          - pattern-inside: |
              <style>
              ...
              </style>
          - pattern-regex: \:\s?expression\(.*\)
      - pattern-regex: '\`.*\{\$.*\}.*\`'     # dynamic content in backsticks 
      - pattern-regex: '\<.*(href|src|action|data|http\-equiv|style)\=\"?\{\{(?!.*\|.*escape).*\}\}'
      - pattern-regex: style\="background\-image\:\s?url\('\{\{



  - id: html_tornado_rule_reflected_xss
    languages:
      - html
    severity: ERROR
    message: |
      Global disabling of autoescaping in Tornado HTML templates with {% autoescape None %} can lead to XSS attack if the template contains user-controlled input. Avoid global unescaping, prefer disabling locally. 
      {% raw ... %} and {{! ... }} disable HTML auto escaping. Make sure not to use it on user controlled inputs. 
    metadata:
      cwe: CWE-79
      owasp:
        - A7:2017-Cross-Site Scripting (XSS)
        - A03:2021-Injection
      related-packages:
        - pkg:pypi/tornado
      category: security
      shortDescription: Improper neutralization of user input rendered in HTML ('XSS')
      security-severity: High
      confidence: Low
      license: License Not Found
    pattern-either:
      - pattern-regex: '\{\% autoescape None \%\}'
      - pattern-regex: '\{\% raw .*'
      - pattern-regex: '\{\{\! .*'

          
  - id: "java_cookie_rule-CookieInsecure"
    languages:
      - "java"
    patterns:
      - pattern: |
          $X.servlet.http.Cookie $C = new $X.servlet.http.Cookie(..., ...);
          ...
          ($X.servlet.http.HttpServletResponse $RESP).addCookie($C);
      - pattern-not-inside: |
          $X.servlet.http.Cookie $C = new $X.servlet.http.Cookie(..., ...);
          ...
          $C.setSecure(true);
          ...
          ($X.servlet.http.HttpServletResponse $RESP).addCookie($C);
    message: |
      The `Secure` attribute when set to `true` protects the cookie value from being being
      transmitted over clear text
      communication paths such as HTTP. By enabling this protection, the cookie will only be sent
      over HTTPS.

      Example of protecting a `Cookie`:
      ```
      // Create an Secure cookie.
      Cookie someCookie = new Cookie("SomeCookieName", "SomeValue");
      // Set Secure flag to true
      someCookie.setSecure(true);
      ```

      For more information see:
      https://jakarta.ee/specifications/servlet/4.0/apidocs/javax/servlet/http/cookie#setSecure-boolean-

      Session cookies should be configured with the following security directives:

      - [HTTPOnly](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
      - [SameSite](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite)
      - [Secure](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
    severity: "WARNING"
    metadata:
      shortDescription: "Sensitive cookie in HTTPS session without 'Secure' attribute"
      category: "security"
      cwe: "CWE-614"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      technology:
        - "java"
      security-severity: "Low"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: MIT (c) GitLab Inc.
  - id: "java_cookie_rule-HttpResponseSplitting"
    languages:
      - "java"
    mode: "taint"
    pattern-sanitizers:
      - patterns:
          - pattern-inside: |
              $STR.replaceAll($REPLACER, "...");
              ...
          - pattern: "$STR"
          - metavariable-regex:
              metavariable: "$REPLACER"
              regex: ".*\\[(?=.*\\\\r)(?=.*\\\\n).*\\]\\+"
      - pattern: "org.apache.commons.text.StringEscapeUtils.escapeJava($STR);"
    pattern-sinks:
      - pattern: "new javax.servlet.http.Cookie(\"$KEY\", ...);"
      - patterns:
          - pattern-inside: |
              $C = new javax.servlet.http.Cookie("$KEY", ...);
              ...
          - pattern: "$C.setValue(...);"
    pattern-sources:
      - pattern: "(javax.servlet.http.HttpServletRequest $REQ).getParameter(...);"
      - pattern: "(javax.servlet.http.HttpServletRequest $REQ).getParameterNames();"
      - pattern: "(javax.servlet.http.HttpServletRequest $REQ).getParameterValues(...);"
      - pattern: "(javax.servlet.http.HttpServletRequest $REQ).getParameterMap();"
      - pattern: "(javax.servlet.http.HttpServletRequest $REQ).getHeader(...);"
      - pattern: "(javax.servlet.http.HttpServletRequest $REQ).getPathInfo();"
    message: |
      HTTP Response Splitting is a vulnerability where Carriage Return (CR `\r`) and Line Feed (LF
      `\n`)
      characters are introduced into an HTTP header from user-supplied input. By injecting the
      `\r\n`
      character sequence, an adversary could potentially modify how the response is interpreted by
      the
      client or any downstream caching services. This could allow an adversary to poison the cache
      data or execute Cross-Site Scripting (XSS) attacks.

      Some Java application servers such as [Apache Tomcat](https://tomcat.apache.org/) as of version
      8.0, newer versions of Jetty and other servers that implement the [RFC 6265 Standard](https://datatracker.ietf.org/doc/html/rfc6265) will
      disallow `\r' and '\n` characters characters from being set in cookies. If your application server does not
      automatically provide this functionality, user-supplied input that is used in cookie keys or
      values must be validated.

      Example of validating cookies to only allow valid characters:
      ```
      // throws an IllegalArgumentException if the provided value contains invalid characters
      public void validateRfc6265CookieValue(String value) throws IllegalArgumentException {
          char[] chars = value.toCharArray();

          // iterate over every character
          for (int i = 0; i < chars.length; i++) {
              char c = chars[i];

              // check for any characters below 0x21 as well as: '"' ',' ';' '\' and 0x7f.
              if (c < 0x21 || c == '"' || c == ',' || c == ';' || c == '\\' || c == 0x7f) {
                  throw new IllegalArgumentException("Invalid character in cookie detected:
      {0}".format(Integer.toString(c)));
              }
          }
      }
      ```

      Alternatively, you could use a string escape package such as
      [Apache Commons Text](https://commons.apache.org/proper/commons-text/) to escape the input:
      ```
      public String escapeValue(String value) {
        return StringEscapeUtils.escapeJava(value);
      }
      ```

      For more information on response splitting attacks see OWASP:
      https://owasp.org/www-community/attacks/HTTP_Response_Splitting
    severity: "WARNING"
    metadata:
      shortDescription: "Improper neutralization of CRLF sequences in HTTP headers ('HTTP Response Splitting')"
      category: "security"
      cwe: "CWE-113"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "java"
      security-severity: "High"
      related-packages: ['pkg:maven/org.apache.commons.text/commons-text']
      license: MIT (c) GitLab Inc.
  - id: "java_cookie_rule-RequestParamToHeader"
    languages:
      - "java"
    mode: "taint"
    pattern-sanitizers:
      - patterns:
          - pattern-inside: |
              $STR.replaceAll("$INPUT", "...");
              ...
          - pattern: "$STR"
          - metavariable-regex:
              metavariable: "$INPUT"
              regex: ".*\\[(?=.*\\\\r)(?=.*\\\\n).*\\]\\+"
      - pattern: "org.apache.commons.text.StringEscapeUtils.unescapeJava(...);"
    pattern-sinks:
      - pattern: "($X.servlet.http.HttpServletResponse $RES).setHeader(\"$KEY\", ...);"
      - pattern: "($X.servlet.http.HttpServletResponse $RES).addHeader(\"$KEY\", ...);"
      - pattern: "($X.servlet.http.HttpServletResponseWrapper $WRP).setHeader(\"$KEY\", ...);"
      - pattern: "($X.servlet.http.HttpServletResponseWrapper $WRP).addHeader(\"$KEY\", ...);"
    pattern-sources:
      - pattern: "($X.servlet.http.HttpServletRequest $REQ).getParameter(...);"
      - pattern: "($X.servlet.http.HttpServletRequest $REQ).getParameterNames();"
      - pattern: "($X.servlet.http.HttpServletRequest $REQ).getParameterValues(...);"
      - pattern: "($X.servlet.http.HttpServletRequest $REQ).getParameterMap();"
      - pattern: "($X.servlet.http.HttpServletRequest $REQ).getHeader(...);"
      - pattern: "($X.servlet.http.HttpServletRequest $REQ).getPathInfo();"
    message: |
      HTTP Response Splitting is a vulnerability where Carriage Return (CR `\r`) and Line Feed (LF
      `\n`)
      characters are introduced into an HTTP header from user-supplied input. By injecting the
      `\r\n`
      character sequence, an adversary could potentially modify how the response is interpreted by
      the
      client or any down stream caching services. This could allow an adversary to poison the cache
      data or execute Cross-Site Scripting (XSS) attacks.

      Some Java application servers such as [Apache Tomcat](https://tomcat.apache.org/) will
      automatically encode
      characters from being set in response headers as a space `0x20` character. If your application
      server does
      not automatically provide this functionality, user-supplied input that is used in header keys
      or values must be
      validated.

      Example of validating headers to only allow valid characters:
      ```
      // throws an IllegalArgumentException if the provided value contains invalid characters
      public void validateHeader(String value) throws IllegalArgumentException {
          char[] chars = value.toCharArray();

          // iterate over every character
          for (int i = 0; i < chars.length; i++) {
              char c = chars[i];

              // check for any characters below 0x21 as well as: '"' ',' ';' '\' and 0x7f.
              if (c < 0x21 || c == '"' || c == ',' || c == ';' || c == '\\' || c == 0x7f) {
                  throw new IllegalArgumentException("Invalid character in cookie detected:
      {0}".format(Integer.toString(c)));
              }
          }
      }
      ```

      Alternatively, you could use a string escape package such as
      [Apache Commons Text](https://commons.apache.org/proper/commons-text/) to escape the input:
      ```
      public String escapeValue(String value) {
        return StringEscapeUtils.escapeJava(value);
      }
      ```

      For more information on response splitting attacks see OWASP:
      https://owasp.org/www-community/attacks/HTTP_Response_Splitting
    severity: "ERROR"
    metadata:
      shortDescription: "Improper neutralization of CRLF sequences in HTTP headers ('HTTP Response Splitting')"
      category: "security"
      cwe: "CWE-113"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "java"
      security-severity: "High"
      related-packages: ['pkg:maven/org.apache.commons.text/commons-text']
      license: MIT (c) GitLab Inc.
  - id: "java_cors_rule-PermissiveCORSInjection"
    languages:
      - "java"
    mode: "taint"
    pattern-sources:
      - pattern: "(HttpServletRequest $REQ).getParameter(...)"
      - pattern: "(HttpServletRequest $REQ).getHeader(...)"
      - pattern: "(HttpServletRequest $REQ).getPathInfo()"
      - pattern: "(HttpServletRequest $REQ).getQueryString()"
      - pattern: "(HttpServletRequest $REQ).getAttribute(...)"
      - pattern: "(HttpServletRequest $REQ).getSession().getAttribute(...)"
      - pattern: "(HttpServletRequest $REQ).getServletContext().getAttribute(...)"
      - pattern: "(HttpServletRequest $REQ).getParameterValues(...)"
      - pattern: "(HttpServletRequest $REQ).getParameterNames()"
      - pattern: "(HttpServletRequest $REQ).getParameterMap()"
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern: "(HttpServletResponse $RES).setHeader(\"$HEADER\", ...)"
              - pattern: "(HttpServletResponse $RES).addHeader(\"$HEADER\", ...)"
          - metavariable-regex:
              metavariable: "$HEADER"
              regex: "(?i)(Access-Control-Allow-Origin)"
    message: |
      This application potentially allows user-supplied input into the value of the
      `Access-Control-Allow-Origin` response header. This header is part of the
      [Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) CORS
      specification. By allowing user input to specify which domains can communicate with this
      server,
      an adversary could exploit a weakness in this server to force clients to send credentials (such
      as session
      identifiers) to the adversary's server.

      For the above attack to work, the application would need to suffer from an additional
      vulnerability,
      such as Cross-Site Scripting (XSS).

      To remediate this issue, do not use user-supplied information when calling
      `HttpServletResponse.setHeader` or `HttpServletResponse.addHeader`
      for the `Access-Control-Allow-Origin` header's value. Instead, hardcode the allowed domain(s)
      and reference them in a lookup
      table:
      Example allowing dynamic but safe domains in `Access-Control-Allow-Origin`:

      ```
        // this data should be in the class constructor or taken from a trusted datasource
        Map<String, String> allowedDomains = new HashMap();
        allowedDomains.put("sub1", "sub1.example.com");
        allowedDomains.put("sub2", "sub2.example.com");

        // extract the allowedDomain parameters value as a key to look up which domain to provide
      via the allowedDomains map
        // if not found, sets sub1 as the default
        String headerValue = allowedDomains.getOrDefault(request.getParameter("allowedDomain"),
      allowedDomains.get("sub1"));

        // add the header with our trusted sub1.example.com or sub2.example.com domains.
        response.addHeader("Access-Control-Allow-Origin", headerValue);
      }
      ```

      For more information on `Access-Control-Allow-Origin` see:
      https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
    severity: "ERROR"
    metadata:
      shortDescription: "Permissive cross-domain policy with untrusted domains"
      cwe: "CWE-942"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "java"
      security-severity: "Low"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: MIT (c) GitLab Inc.
  - id: "java_crypto_rule-BlowfishKeySize"
    languages:
      - "java"
    patterns:
      - pattern-inside: |
          $KEYGEN = javax.crypto.KeyGenerator.getInstance("Blowfish", ...);
          ...
      - pattern: $KEYGEN.init($KEY_SIZE)
      - metavariable-comparison:
          comparison: "int($KEY_SIZE) < 128"
          metavariable: "$KEY_SIZE"
    message: |
      The Blowfish encryption algorithm was meant as a drop-in replacement for DES and was created in
      1993. Smaller key sizes may make the ciphertext vulnerable to [birthday
      attacks](https://en.wikipedia.org/wiki/Birthday_attack). While no known attacks against
      Blowfish
      exist, it should never be used to encrypt files over 4GB in size. If possible consider
      using AES as the instance of `KeyGenerator` instead of Blowfish.

      To remediate the small key size, pass a value such as 256 to the `KeyGenerator.init(keySize)`
      method.

      Example setting a larger key size and changing to `KeyGenerator` to AES:
      ```
      public static void aesKeyGenerator() throws java.security.NoSuchAlgorithmException {
          // Use the AES algorithm for key generation
          KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");

          // Set the key size here
          keyGenerator.init(256);

          // get the raw bytes of the key
          byte[] key = keyGenerator.generateKey().getEncoded();

          // pass the key bytes to create a SecretKeySpec
          SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
      }
      ```

      Example setting a larger key size for Blowfish:
      ```
      public static void blowFishKeyGenerator() throws java.security.NoSuchAlgorithmException {
          // Use the Blowfish algorithm for key generation
          KeyGenerator keyGenerator = KeyGenerator.getInstance("Blowfish");

          // Set the key size here
          keyGenerator.init(256);

          // get the raw bytes of the key
          byte[] key = keyGenerator.generateKey().getEncoded();

          // pass the key bytes to create a SecretKeySpec
          SecretKeySpec secretKeySpec = new SecretKeySpec(key, "Blowfish");
      }
      ```

      For more information on Java Cryptography see:
      https://docs.oracle.com/en/java/javase/15/security/java-cryptography-architecture-jca-reference-guide.html
    severity: "WARNING"
    metadata:
      category: "security"
      shortDescription: "Inadequate encryption strength"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-326"
      technology:
        - "java"
      security-severity: "Medium"
      related-packages: ['pkg:maven/javax.crypto', 'pkg:maven/org.bouncycastle']
      license: MIT (c) GitLab Inc.
  - id: "java_crypto_rule-CipherDESInsecure"
    languages:
      - "java"
    patterns:
      - pattern-either:
          - pattern: javax.crypto.Cipher.getInstance("$ALG", ...)
          - pattern: |
              $PROP = (java.util.Properties $P).getProperty(..., "$ALG");
              ...
              javax.crypto.Cipher.getInstance($PROP, ...);
      - metavariable-regex:
          metavariable: $ALG
          regex: "^DES(/|$)"
    message: |
      DES, TripleDES and RC2 are all considered broken or insecure cryptographic algorithms.
      Newer algorithms  apply message integrity to validate ciphertext has not been tampered
      with. Consider using `ChaCha20Poly1305` instead as it is easier and faster than the
      alternatives such as `AES-256-GCM`.

      For older applications that don't have support for `ChaCha20Poly1305`,
      `AES-256-GCM` is recommended, however it has many drawbacks:
      - Slower than `ChaCha20Poly1305`.
      - Catastrophic failure if nonce values are reused.

      Example using `ChaCha20Poly1305`:
      ```
      public encrypt() throws Exception {
          chaChaEncryption("Secret text to encrypt".getBytes(StandardCharsets.UTF_8));
      }

      public SecureRandom getSecureRandomDRBG() throws NoSuchAlgorithmException {
          // Use DRBG according to
      http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf
          return SecureRandom.getInstance("DRBG",
                  // Security strength in bits (default is 128)
                  DrbgParameters.instantiation(256,
                      // Set prediction resistance and re-seeding
                      DrbgParameters.Capability.PR_AND_RESEED,
                      // Set the personalization string (optional, not necessary)
                      "some_personalization_string".getBytes()
                  )
          );
      }

      public Cipher getChaCha20Poly1305(int mode, byte[] ivKey, byte[] secretKey) throws
      NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
      InvalidAlgorithmParameterException  {
          // Get a DRBG random number generator instance
          SecureRandom random = getSecureRandomDRBG();
          // Create a ChaCha20-Poly1305 cipher instance
          Cipher chaChaCipher = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
          // Create our parameterSpec using our ivKey
          AlgorithmParameterSpec parameterSpec = new IvParameterSpec(ivKey);
          // Create a SecretKeySpec using our secretKey
          SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "ChaCha20");
          // Initialize and return the cipher for the provided mode
          chaChaCipher.init(mode, secretKeySpec, parameterSpec, random);
          return chaChaCipher;
      }

      public void chaChaEncryption(byte[] plainText) throws NoSuchAlgorithmException,
      NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
          // Get a DRBG random number generator instance
          SecureRandom random = getSecureRandomDRBG();
          // Create secretKey
          byte[] secretKey = new byte[32];
          random.nextBytes(secretKey);
          // Create an IV Key
          byte[] ivKey = new byte[12];
          random.nextBytes(ivKey);

          // Create a chaCha encryption cipher instance
          Cipher chaChaEncryptor = getChaCha20Poly1305(Cipher.ENCRYPT_MODE, ivKey, secretKey);

          // Encrypt the text using ChaCha20Poly1305
          byte[] cipherText = null;
          try {
              cipherText = chaChaEncryptor.doFinal(plainText);
          } catch (IllegalBlockSizeException | BadPaddingException e) {
              System.out.println("failed to encrypt text");
              return;
          }
          System.out.println("encrypted: " + Base64.getEncoder().encodeToString(cipherText));

           // Create a chaCha decryption cipher instance
          Cipher chaChaDecryptor = getChaCha20Poly1305(Cipher.DECRYPT_MODE, ivKey, secretKey);

          // Decrypt the text
          byte[] decryptedText = null;
          try {
              decryptedText = chaChaDecryptor.doFinal(cipherText);
          } catch (IllegalBlockSizeException | BadPaddingException e) {
              System.out.println("failed to decrypt text");
              return;
          }
          System.out.println("decrypted: " + new String(decryptedText, StandardCharsets.UTF_8));
      }
      ```

      For more information on Java Cryptography see:
      https://docs.oracle.com/en/java/javase/15/security/java-cryptography-architecture-jca-reference-guide.html
    severity: "WARNING"
    metadata:
      shortDescription: "Inadequate encryption strength"
      category: "security"
      cwe: "CWE-326"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      technology:
        - "java"
      security-severity: "Medium"
      related-packages: ['pkg:maven/javax.crypto']
      license: MIT (c) GitLab Inc.
  - id: "java_crypto_rule-CipherDESedeInsecure"
    languages:
      - "java"
    patterns:
      - pattern-either:
          - pattern: javax.crypto.Cipher.getInstance("$ALG", ...)
          - pattern: |
              $PROP = (java.util.Properties $P).getProperty(..., "$ALG");
              ...
              javax.crypto.Cipher.getInstance($PROP, ...);
      - metavariable-regex:
          metavariable: $ALG
          regex: "DESede(/|$)"
    message: |
      DES, TripleDES and RC2 are all considered broken or insecure cryptographic algorithms.
      Newer algorithms  apply message integrity to validate ciphertext has not been tampered
      with. Consider using `ChaCha20Poly1305` instead as it is easier and faster than the
      alternatives such as `AES-256-GCM`.

      For older applications that don't have support for `ChaCha20Poly1305`,
      `AES-256-GCM` is recommended, however it has many drawbacks:
      - Slower than `ChaCha20Poly1305`.
      - Catastrophic failure if nonce values are reused.

      Example using `ChaCha20Poly1305`:
      ```
      public encrypt() throws Exception {
          chaChaEncryption("Secret text to encrypt".getBytes(StandardCharsets.UTF_8));
      }

      public SecureRandom getSecureRandomDRBG() throws NoSuchAlgorithmException {
          // Use DRBG according to
      http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf
          return SecureRandom.getInstance("DRBG",
                  // Security strength in bits (default is 128)
                  DrbgParameters.instantiation(256,
                      // Set prediction resistance and re-seeding
                      DrbgParameters.Capability.PR_AND_RESEED,
                      // Set the personalization string (optional, not necessary)
                      "some_personalization_string".getBytes()
                  )
          );
      }

      public Cipher getChaCha20Poly1305(int mode, byte[] nonceKey, byte[] secretKey) throws
      NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
      InvalidAlgorithmParameterException  {
          // Get a DRBG random number generator instance
          SecureRandom random = getSecureRandomDRBG();
          // Create a ChaCha20-Poly1305 cipher instance
          Cipher chaChaCipher = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
          // Create our parameterSpec using our nonceKey
          AlgorithmParameterSpec parameterSpec = new IvParameterSpec(nonceKey);
          // Create a SecretKeySpec using our secretKey
          SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "ChaCha20");
          // Initialize and return the cipher for the provided mode
          chaChaCipher.init(mode, secretKeySpec, parameterSpec, random);
          return chaChaCipher;
      }

      public void chaChaEncryption(byte[] plainText) throws NoSuchAlgorithmException,
      NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
          // Get a DRBG random number generator instance
          SecureRandom random = getSecureRandomDRBG();
          // Create secretKey
          byte[] secretKey = new byte[32];
          random.nextBytes(secretKey);
          // Create an IV nonceKey
          byte[] nonceKey = new byte[12];
          random.nextBytes(nonceKey);
          // Create a chaCha encryption cipher instance
          Cipher chaChaEncryptor = getChaCha20Poly1305(Cipher.ENCRYPT_MODE, nonceKey, secretKey);
          // Encrypt the text using ChaCha20Poly1305
          byte[] cipherText = null;
          try {
              cipherText = chaChaEncryptor.doFinal(plainText);
          } catch (IllegalBlockSizeException | BadPaddingException e) {
              System.out.println("failed to encrypt text");
              return;
          }
          System.out.println("encrypted: " + Base64.getEncoder().encodeToString(cipherText));
          // Create a chaCha decryption cipher instance
          Cipher chaChaDecryptor = getChaCha20Poly1305(Cipher.DECRYPT_MODE, nonceKey, secretKey);
          // Decrypt the text
          byte[] decryptedText = null;
          try {
              decryptedText = chaChaDecryptor.doFinal(cipherText);
          } catch (IllegalBlockSizeException | BadPaddingException e) {
              System.out.println("failed to decrypt text");
              return;
          }
          System.out.println("decrypted: " + new String(decryptedText, StandardCharsets.UTF_8));
      }
      ```

      For more information on Java Cryptography see:
      https://docs.oracle.com/en/java/javase/15/security/java-cryptography-architecture-jca-reference-guide.html
    severity: "WARNING"
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      category: "security"
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      technology:
        - "java"
      security-severity: "Medium"
      related-packages: ['pkg:maven/javax.crypto']
      license: MIT (c) GitLab Inc.
  - id: "java_crypto_rule-CipherECBMode"
    languages:
      - "java"
    patterns:
      - pattern-either:
          - pattern: javax.crypto.Cipher.getInstance("$ALG", ...)
          - pattern: |
              $PROP = (java.util.Properties $P).getProperty(..., "$ALG");
              ...
              javax.crypto.Cipher.getInstance($PROP, ...);
      - metavariable-comparison:
          metavariable: $ALG
          comparison: |
            $ALG in (
              "AES/ECB/NoPadding" "AES/ECB/PKCS5Padding"
              "DES/ECB/NoPadding" "DES/ECB/PKCS5Padding"
              "DESede/ECB/NoPadding" "DESede/ECB/PKCS5Padding"
              "AES/ECB/PKCS7Padding"
            )
    message: |
      Cryptographic algorithms provide many different modes of operation, only some of which provide
      message integrity. Without message integrity it could be possible for an adversary to attempt
      to tamper with the ciphertext which could lead to compromising the encryption key. Newer
      algorithms
      apply message integrity to validate ciphertext has not been tampered with.

      Instead of using an algorithm that requires configuring a cipher mode, an algorithm
      that has built-in message integrity should be used. Consider using `ChaCha20Poly1305` or
      `AES-256-GCM` instead.

      For older applications that don't have support for `ChaCha20Poly1305`, `AES-256-GCM` is
      recommended, however it has many drawbacks:
        - Slower than `ChaCha20Poly1305`.
        - Catastrophic failure if nonce values are reused.

      Example using `ChaCha20Poly1305`:
      ```
      public encrypt() throws Exception {
          chaChaEncryption("Secret text to encrypt".getBytes(StandardCharsets.UTF_8));
      }

      public SecureRandom getSecureRandomDRBG() throws NoSuchAlgorithmException {
          // Use DRBG according to
      http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf
          return SecureRandom.getInstance("DRBG",
                  // Security strength in bits (default is 128)
                  DrbgParameters.instantiation(256,
                      // Set prediction resistance and re-seeding
                      DrbgParameters.Capability.PR_AND_RESEED,
                      // Set the personalization string (optional, not necessary)
                      "some_personalization_string".getBytes()
                  )
          );
      }

      public Cipher getChaCha20Poly1305(int mode, byte[] ivKey, byte[] secretKey) throws
      NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
      InvalidAlgorithmParameterException  {
          // Get a DRBG random number generator instance
          SecureRandom random = getSecureRandomDRBG();
          // Create a ChaCha20-Poly1305 cipher instance
          Cipher chaChaCipher = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
          // Create our parameterSpec using our ivKey
          AlgorithmParameterSpec parameterSpec = new IvParameterSpec(ivKey);
          // Create a SecretKeySpec using our secretKey
          SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "ChaCha20");
          // Initialize and return the cipher for the provided mode
          chaChaCipher.init(mode, secretKeySpec, parameterSpec, random);
          return chaChaCipher;
      }

      public void chaChaEncryption(byte[] plainText) throws NoSuchAlgorithmException,
      NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
          // Get a DRBG random number generator instance
          SecureRandom random = getSecureRandomDRBG();
          // Create secretKey
          byte[] secretKey = new byte[32];
          random.nextBytes(secretKey);
          // Create an IV Key
          byte[] ivKey = new byte[12];
          random.nextBytes(ivKey);

          // Create a chaCha encryption cipher instance
          Cipher chaChaEncryptor = getChaCha20Poly1305(Cipher.ENCRYPT_MODE, ivKey, secretKey);

          // Encrypt the text using ChaCha20Poly1305
          byte[] cipherText = null;
          try {
              cipherText = chaChaEncryptor.doFinal(plainText);
          } catch (IllegalBlockSizeException | BadPaddingException e) {
              System.out.println("failed to encrypt text");
              return;
          }
          System.out.println("encrypted: " + Base64.getEncoder().encodeToString(cipherText));

           // Create a chaCha decryption cipher instance
          Cipher chaChaDecryptor = getChaCha20Poly1305(Cipher.DECRYPT_MODE, ivKey, secretKey);

          // Decrypt the text
          byte[] decryptedText = null;
          try {
              decryptedText = chaChaDecryptor.doFinal(cipherText);
          } catch (IllegalBlockSizeException | BadPaddingException e) {
              System.out.println("failed to decrypt text");
              return;
          }
          System.out.println("decrypted: " + new String(decryptedText, StandardCharsets.UTF_8));
      }
      ```

      For more information on Java Cryptography see:
      https://docs.oracle.com/en/java/javase/15/security/java-cryptography-architecture-jca-reference-guide.html
    severity: "ERROR"
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      category: "security"
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      technology:
        - "java"
      security-severity: "Medium"
      related-packages: ['pkg:maven/javax.crypto']
      license: MIT (c) GitLab Inc.
  - id: "java_crypto_rule-CipherIntegrity"
    languages:
      - "java"
    patterns:
      - pattern-either:
          - pattern: javax.crypto.Cipher.getInstance("$ALG", ...)
          - pattern: |
              $PROP = (java.util.Properties $P).getProperty(..., "$ALG");
              ...
              javax.crypto.Cipher.getInstance($PROP, ...);
      - metavariable-comparison:
          metavariable: $ALG
          comparison: |
            $ALG in (
              "AES"
              "AES/CBC/NoPadding" "AES/CBC/PKCS5Padding" "AES/CBC/PKCS7Padding"
              "AES/CFB/NoPadding" "AES/CFB/PKCS5Padding" "AES/CFB/PKCS7Padding"
              "AES/CTR/NoPadding"
              "AES/ECB/NoPadding" "AES/ECB/PKCS5Padding" "AES/ECB/PKCS7Padding"
              "AES/OFB/NoPadding" "AES/OFB/PKCS5Padding" "AES/OFB/PKCS7Padding"
              "ARCFOUR"
              "DES"
              "DES/CBC/NoPadding" "DES/CBC/PKCS5Padding"
              "DES/ECB/NoPadding" "DES/ECB/PKCS5Padding"
              "DESede"
              "DESede/CBC/NoPadding" "DESede/CBC/PKCS5Padding"
              "DESede/ECB/NoPadding" "DESede/ECB/PKCS5Padding"
              "RC4"
            )
    message: |
      Cryptographic algorithms provide many different modes of operation, only some of which provide
      message integrity. Without message integrity it could be possible for an adversary to attempt
      to tamper with the ciphertext which could lead to compromising the encryption key. Newer
      algorithms
      apply message integrity to validate ciphertext has not been tampered with.

      Instead of using an algorithm that requires configuring a cipher mode, an algorithm
      that has built-in message integrity should be used. Consider using `ChaCha20Poly1305` or
      `AES-256-GCM` instead.

      For older applications that don't have support for `ChaCha20Poly1305`, `AES-256-GCM` is
      recommended, however it has many drawbacks:
        - Slower than `ChaCha20Poly1305`.
        - Catastrophic failure if nonce values are reused.

      Example using `ChaCha20Poly1305`:
      ```
      public encrypt() throws Exception {
          chaChaEncryption("Secret text to encrypt".getBytes(StandardCharsets.UTF_8));
      }

      public SecureRandom getSecureRandomDRBG() throws NoSuchAlgorithmException {
      // Use DRBG according to
      http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf
          return SecureRandom.getInstance("DRBG",
                  // Security strength in bits (default is 128)
                  DrbgParameters.instantiation(256,
                      // Set prediction resistance and re-seeding
                      DrbgParameters.Capability.PR_AND_RESEED,
                      // Set the personalization string (optional, not necessary)
                      "some_personalization_string".getBytes()
                  )
          );
      }

      public Cipher getChaCha20Poly1305(int mode, byte[] ivKey, byte[] secretKey) throws
      NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
      InvalidAlgorithmParameterException  {
          // Get a DRBG random number generator instance
          SecureRandom random = getSecureRandomDRBG();
          // Create a ChaCha20-Poly1305 cipher instance
          Cipher chaChaCipher = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
          // Create our parameterSpec using our ivKey
          AlgorithmParameterSpec parameterSpec = new IvParameterSpec(ivKey);
          // Create a SecretKeySpec using our secretKey
          SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "ChaCha20");
          // Initialize and return the cipher for the provided mode
          chaChaCipher.init(mode, secretKeySpec, parameterSpec, random);
          return chaChaCipher;
      }

      public void chaChaEncryption(byte[] plainText) throws NoSuchAlgorithmException,
      NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
          // Get a DRBG random number generator instance
          SecureRandom random = getSecureRandomDRBG();
          // Create secretKey
          byte[] secretKey = new byte[32];
          random.nextBytes(secretKey);
          // Create an IV Key
          byte[] ivKey = new byte[12];
          random.nextBytes(ivKey);

          // Create a chaCha encryption cipher instance
          Cipher chaChaEncryptor = getChaCha20Poly1305(Cipher.ENCRYPT_MODE, ivKey, secretKey);

          // Encrypt the text using ChaCha20Poly1305
          byte[] cipherText = null;
          try {
              cipherText = chaChaEncryptor.doFinal(plainText);
          } catch (IllegalBlockSizeException | BadPaddingException e) {
              System.out.println("failed to encrypt text");
              return;
          }
          System.out.println("encrypted: " + Base64.getEncoder().encodeToString(cipherText));

           // Create a chaCha decryption cipher instance
          Cipher chaChaDecryptor = getChaCha20Poly1305(Cipher.DECRYPT_MODE, ivKey, secretKey);

          // Decrypt the text
          byte[] decryptedText = null;
          try {
              decryptedText = chaChaDecryptor.doFinal(cipherText);
          } catch (IllegalBlockSizeException | BadPaddingException e) {
              System.out.println("failed to decrypt text");
              return;
          }
          System.out.println("decrypted: " + new String(decryptedText, StandardCharsets.UTF_8));
      }
      ```

      For more information on Java Cryptography see:
      https://docs.oracle.com/en/java/javase/15/security/java-cryptography-architecture-jca-reference-guide.html
    severity: "ERROR"
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      category: "security"
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      technology:
        - "java"
      security-severity: "Medium"
      related-packages: ['pkg:maven/javax.crypto']
      license: MIT (c) GitLab Inc.
  - id: "java_crypto_rule-CipherPaddingOracle"
    languages:
      - "java"
    patterns:
      - pattern-either:
          - pattern: javax.crypto.Cipher.getInstance("$ALG", ...)
          - pattern: |
              $PROP = (java.util.Properties $P).getProperty(..., "$ALG");
              ...
              javax.crypto.Cipher.getInstance($PROP, ...);
      - metavariable-comparison:
          metavariable: $ALG
          comparison: |
            $ALG in (
              "AES/CBC/PKCS5Padding" "DES/CBC/PKCS5Padding" "DESede/CBC/PKCS5Padding" "AES/CBC/PKCS7Padding"
            )
    message: |
      Cryptographic block ciphers can be configured to pad individual blocks if there is not enough
      input data to match the size of the block. This specific mode of CBC used in combination with
      PKCS5Padding is susceptible to padding oracle attacks. An adversary could potentially decrypt
      the message if the system exposed the difference between plaintext with invalid padding or
      valid padding. The distinction between valid and invalid padding is usually revealed through
      distinct error messages being returned for each condition.

      Consider switching to a more secure cipher that doesn't require padding and builds in message
      authentication integrity directly into the algorithm.

      Consider using `ChaCha20Poly1305` or
      `AES-256-GCM` instead.

      For older applications that don't have support for `ChaCha20Poly1305`, `AES-256-GCM` is
      recommended, however it has many drawbacks:
        - Slower than `ChaCha20Poly1305`.
        - Catastrophic failure if nonce values are reused.

      Example using `ChaCha20Poly1305`:
      ```
      public encrypt() throws Exception {
          chaChaEncryption("Secret text to encrypt".getBytes(StandardCharsets.UTF_8));
      }

      public SecureRandom getSecureRandomDRBG() throws NoSuchAlgorithmException {
          // Use DRBG according to
      http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf
          return SecureRandom.getInstance("DRBG",
                  // Security strength in bits (default is 128)
                  DrbgParameters.instantiation(256,
                      // Set prediction resistance and re-seeding
                      DrbgParameters.Capability.PR_AND_RESEED,
                      // Set the personalization string (optional, not necessary)
                      "some_personalization_string".getBytes()
                  )
          );
      }

      public Cipher getChaCha20Poly1305(int mode, byte[] ivKey, byte[] secretKey) throws
      NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
      InvalidAlgorithmParameterException  {
          // Get a DRBG random number generator instance
          SecureRandom random = getSecureRandomDRBG();
          // Create a ChaCha20-Poly1305 cipher instance
          Cipher chaChaCipher = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
          // Create our parameterSpec using our ivKey
          AlgorithmParameterSpec parameterSpec = new IvParameterSpec(ivKey);
          // Create a SecretKeySpec using our secretKey
          SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "ChaCha20");
          // Initialize and return the cipher for the provided mode
          chaChaCipher.init(mode, secretKeySpec, parameterSpec, random);
          return chaChaCipher;
      }

      public void chaChaEncryption(byte[] plainText) throws NoSuchAlgorithmException,
      NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
          // Get a DRBG random number generator instance
          SecureRandom random = getSecureRandomDRBG();
          // Create secretKey
          byte[] secretKey = new byte[32];
          random.nextBytes(secretKey);
          // Create an IV Key
          byte[] ivKey = new byte[12];
          random.nextBytes(ivKey);

          // Create a chaCha encryption cipher instance
          Cipher chaChaEncryptor = getChaCha20Poly1305(Cipher.ENCRYPT_MODE, ivKey, secretKey);

          // Encrypt the text using ChaCha20Poly1305
          byte[] cipherText = null;
          try {
              cipherText = chaChaEncryptor.doFinal(plainText);
          } catch (IllegalBlockSizeException | BadPaddingException e) {
              System.out.println("failed to encrypt text");
              return;
          }
          System.out.println("encrypted: " + Base64.getEncoder().encodeToString(cipherText));

           // Create a chaCha decryption cipher instance
          Cipher chaChaDecryptor = getChaCha20Poly1305(Cipher.DECRYPT_MODE, ivKey, secretKey);

          // Decrypt the text
          byte[] decryptedText = null;
          try {
              decryptedText = chaChaDecryptor.doFinal(cipherText);
          } catch (IllegalBlockSizeException | BadPaddingException e) {
              System.out.println("failed to decrypt text");
              return;
          }
          System.out.println("decrypted: " + new String(decryptedText, StandardCharsets.UTF_8));
      }
      ```

      For more information on padding oracle attacks see:
      https://en.wikipedia.org/wiki/Padding_oracle_attack

      For more information on Java Cryptography see:
      https://docs.oracle.com/en/java/javase/15/security/java-cryptography-architecture-jca-reference-guide.html
    severity: "ERROR"
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      category: "security"
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      technology:
        - "java"
      security-severity: "Medium"
      related-packages: ['pkg:maven/javax.crypto']
      license: MIT (c) GitLab Inc.
  - id: "java_crypto_rule-CustomMessageDigest"
    languages:
      - "java"
    patterns:
      - pattern: |
          class $CLAZZ extends java.security.MessageDigest {
            ...
          }
    message: |
      The application was found implementing a custom `java.security.MessageDigest`. It is
      strongly recommended that a standard Digest algorithm be chosen instead as implementing
      a digest by hand is error-prone. The National Institute of Standards and
      Technology (NIST) recommends the use of SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, or
      SHA-512/256.

      Example of creating a SHA-384 hash:
      ```
      // Create a MessageDigest using the SHA-384 algorithm
      MessageDigest sha384Digest = MessageDigest.getInstance("SHA-384");
      // Call update with your data
      sha384Digest.update(input);
      // Only call digest once all data has been fed into the update sha384digest instance
      byte[] output = sha384Digest.digest();
      // output base64 encoded version of the hash
      System.out.println("hash: " + Base64.getEncoder().encodeToString(output));
      ```
    severity: "WARNING"
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      category: "security"
      cwe: "CWE-327"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A04:2021-Insecure Design"
      technology:
        - "java"
      security-severity: "Medium"
      related-packages: ['pkg:maven/java.security']
      license: MIT (c) GitLab Inc.
  - id: "java_crypto_rule-HazelcastSymmetricEncryption"
    languages:
      - "java"
    patterns:
      - pattern: "new com.hazelcast.config.SymmetricEncryptionConfig()"
    message: |
      The network communications for Hazelcast is configured to use a deprecated symmetric cipher.
      Consider using TLS/SSL when establishing communications across the Hazelcast cluster.

      For more information on configuring TLS/SSL for Hazelcast see:
      https://docs.hazelcast.com/imdg/4.2/security/tls-ssl
    severity: "WARNING"
    metadata:
      shortDescription: "Inadequate encryption strength"
      category: "security"
      cwe: "CWE-326"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      technology:
        - "java"
      security-severity: "Medium"
      related-packages: ['pkg:maven/com.hazelcast/hazelcast']
      license: MIT (c) GitLab Inc.
  - id: "java_crypto_rule-InsufficientKeySizeRsa"
    languages:
      - "java"
    patterns:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  $GEN = KeyPairGenerator.getInstance($ALG, ...);
                  ...
              - pattern-either:
                  - pattern: "$VAR.initialize($SIZE, ...);"
                  - pattern: "new java.security.spec.RSAKeyGenParameterSpec($SIZE,...);"
              - metavariable-comparison:
                  comparison: "$SIZE < 2048"
                  metavariable: "$SIZE"
              - metavariable-regex:
                  metavariable: "$ALG"
                  regex: "\"(RSA|DSA)\""
    message: |
      The application is generating an RSA key that is less than the recommended 2048 bits.
      The National Institute of Standards and Technology (NIST) deprecated signing Digital
      Certificates that contained RSA Public Keys of 1024 bits in December 2010. While
      1024-bit RSA keys have not been factored yet, advances in compute may make it possible
      in the near future.

      Consider upgrading to the newer asymmetric algorithm such as `Ed25519` which handles
      the complexities of generating key pairs and choosing correct key sizes for you:
      ```
      public static KeyPair generateEd25519() throws NoSuchAlgorithmException {
          // Choose Ed25519 for KeyPairGenerator Instance
          KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("Ed25519");
          // Generate a KeyPair and return
          return keyPairGenerator.generateKeyPair();
      }
      ```

      Otherwise use a key size greater than 2048 when generating RSA keys:
      ```
      public static KeyPair generateRSA() throws NoSuchAlgorithmException {
          // Choose RSA for KeyPairGenerator Instance
          KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
          // Initialize with 2048 key size
          keyPairGenerator.initialize(2048);
          // Generate a KeyPair and return
          return keyPairGenerator.generateKeyPair();
      }
      ```

      For more information on Ed25519 see: http://ed25519.cr.yp.to/

      For more information on Java Cryptography see:
      https://docs.oracle.com/en/java/javase/15/security/java-cryptography-architecture-jca-reference-guide.html
    metadata:
      shortDescription: "Inadequate encryption strength"
      category: "security"
      cwe: "CWE-326"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      related-packages: ['pkg:maven/java.security']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"

  - id: "java_crypto_rule-NullCipher"
    languages:
      - "java"
    pattern: "new javax.crypto.NullCipher()"
    message: |
      The application was found creating a `NullCipher` instance. `NullCipher` implements the
      `Cipher` interface by returning ciphertext identical to the supplied plaintext. This means
      any data passed to the `doFinal(...)` or `update(...)` methods will not actually encrypt
      the input.

      Remove the NullCipher reference and replace with a legitimate `Cipher` instance such as
      `ChaCha20-Poly1305`

      Example using `ChaCha20Poly1305`:
      ```
      public encrypt() throws Exception {
          chaChaEncryption("Secret text to encrypt".getBytes(StandardCharsets.UTF_8));
      }

      public SecureRandom getSecureRandomDRBG() throws NoSuchAlgorithmException {
          // Use DRBG according to
      http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf
          return SecureRandom.getInstance("DRBG",
                  // Security strength in bits (default is 128)
                  DrbgParameters.instantiation(256,
                      // Set prediction resistance and re-seeding
                      DrbgParameters.Capability.PR_AND_RESEED,
                      // Set the personalization string (optional, not necessary)
                      "some_personalization_string".getBytes()
                  )
          );
      }

      public Cipher getChaCha20Poly1305(int mode, byte[] ivKey, byte[] secretKey) throws
      NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
      InvalidAlgorithmParameterException  {
          // Get a DRBG random number generator instance
          SecureRandom random = getSecureRandomDRBG();
          // Create a ChaCha20-Poly1305 cipher instance
          Cipher chaChaCipher = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
          // Create our parameterSpec using our ivKey
          AlgorithmParameterSpec parameterSpec = new IvParameterSpec(ivKey);
          // Create a SecretKeySpec using our secretKey
          SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "ChaCha20");
          // Initialize and return the cipher for the provided mode
          chaChaCipher.init(mode, secretKeySpec, parameterSpec, random);
          return chaChaCipher;
      }

      public void chaChaEncryption(byte[] plainText) throws NoSuchAlgorithmException,
      NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
          // Get a DRBG random number generator instance
          SecureRandom random = getSecureRandomDRBG();
          // Create secretKey
          byte[] secretKey = new byte[32];
          random.nextBytes(secretKey);
          // Create an IV Key
          byte[] ivKey = new byte[12];
          random.nextBytes(ivKey);

          // Create a chaCha encryption cipher instance
          Cipher chaChaEncryptor = getChaCha20Poly1305(Cipher.ENCRYPT_MODE, ivKey, secretKey);

          // Encrypt the text using ChaCha20Poly1305
          byte[] cipherText = null;
          try {
              cipherText = chaChaEncryptor.doFinal(plainText);
          } catch (IllegalBlockSizeException | BadPaddingException e) {
              System.out.println("failed to encrypt text");
              return;
          }
          System.out.println("encrypted: " + Base64.getEncoder().encodeToString(cipherText));

           // Create a chaCha decryption cipher instance
          Cipher chaChaDecryptor = getChaCha20Poly1305(Cipher.DECRYPT_MODE, ivKey, secretKey);

          // Decrypt the text
          byte[] decryptedText = null;
          try {
              decryptedText = chaChaDecryptor.doFinal(cipherText);
          } catch (IllegalBlockSizeException | BadPaddingException e) {
              System.out.println("failed to decrypt text");
              return;
          }
          System.out.println("decrypted: " + new String(decryptedText, StandardCharsets.UTF_8));
      }
      ```

      For more information on Java Cryptography see:
      https://docs.oracle.com/en/java/javase/15/security/java-cryptography-architecture-jca-reference-guide.html
    severity: "WARNING"
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      category: "security"
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      technology:
        - "java"
      security-severity: "Medium"
      related-packages: ['pkg:maven/javax.crypto']
      license: MIT (c) GitLab Inc.
  - id: "java_crypto_rule-RsaNoPadding"
    languages:
      - "java"
    patterns:
      - pattern-either:
          - pattern: javax.crypto.Cipher.getInstance("$ALG", ...)
          - pattern: |
              $PROP = (java.util.Properties $P).getProperty(..., "$ALG");
              ...
              javax.crypto.Cipher.getInstance($PROP, ...);
      - metavariable-regex:
          metavariable: $ALG
          regex: "(?i)^RSA/.*NoPadding$"
    message: |
      The software uses the RSA algorithm but does not incorporate Optimal Asymmetric
      Encryption Padding (OAEP). By not enabling padding, the algorithm maybe vulnerable
      to [chosen plaintext attacks](https://en.wikipedia.org/wiki/Chosen-plaintext_attack).

      To enable OAEP mode, pass `RSA/ECB/OAEPWithSHA-256AndMGF1Padding` to the `Cipher.getInstance`
      method.

      Example encrypting and decrypting a message using RSA with OAEP:
      ```
      public static void encryptWithRSA() throws InvalidKeyException, NoSuchAlgorithmException,
      NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
          // Generate an RSA Public and Private Key Pair
          KeyPair keyPair = generateRSAKeys();
          // Create a Cipher instance using RSA, ECB with OAEP
          Cipher rsaEncryptor = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
          // Initialize to ENCRYPT_MODE with the public key
          rsaEncryptor.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
          // Encrypt our secret message
          byte[] cipherText = rsaEncryptor.doFinal("Some secret
      message".getBytes(StandardCharsets.UTF_8));

          // Create a Cipher instance using RSA, ECB with OAEP
          Cipher rsaDecryptor = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
          // Initialize to DECRYPT_MODE with the private key
          rsaDecryptor.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
          // Decrypt the secret message
          byte[] plainText = rsaDecryptor.doFinal(cipherText);
          // Debug output
          System.out.println(new String(plainText));
      }
      ```
      More information on Optimal asymmetric encryption padding:
      https://en.wikipedia.org/wiki/Optimal_asymmetric_encryption_padding

      For more information on Java Cryptography see:
      https://docs.oracle.com/en/java/javase/15/security/java-cryptography-architecture-jca-reference-guide.html
    metadata:
      shortDescription: "Use of RSA algorithm without OAEP"
      category: "security"
      cwe: "CWE-780"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      related-packages: ['pkg:maven/java.security', 'pkg:maven/javax.crypto']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "java_crypto_rule-WeakMessageDigest"
    languages:
      - "java"
    patterns:
      - pattern-either:
          - pattern: java.security.MessageDigest.getInstance("$ALG", ...)
          - pattern: java.security.Signature.getInstance("$ALG", ...)
          - pattern: |
              $PROP = (java.util.Properties $P).getProperty(..., "$ALG");
              ...
              java.security.MessageDigest.getInstance("$ALG", ...);
          - pattern: |
              $PROP = (java.util.Properties $P).getProperty(..., "$ALG");
              ...
              java.security.Signature.getInstance("$ALG", ...);
      - metavariable-comparison:
          metavariable: $ALG
          comparison: |
            $ALG in (
              "MD2" "MD4" "MD5" "MD5withRSA"
              "SHA-1" "SHA1withRSA" "SHA1withDSA"
            )
    message: |
      The application was found using an insecure or risky digest or signature algorithm. Both MD5
      and SHA1 hash algorithms have been found to be vulnerable to producing collisions.
      This means
      that two different values, when hashed, can lead to the same hash value. If the application is
      trying
      to use these hash methods for storing passwords, then it is recommended to switch to a
      password hashing
      algorithm such as Argon2id or PBKDF2.
      strongly recommended that a standard Digest algorithm be chosen instead as implementing
      a digest by hand is error-prone.

      Example of creating a SHA-384 hash:
      ```
      // Create a MessageDigest using the SHA-384 algorithm
      MessageDigest sha384Digest = MessageDigest.getInstance("SHA-384");
      // Call update with your data
      sha384Digest.update(input);
      // Only call digest once all data has been fed into the update sha384digest instance
      byte[] output = sha384Digest.digest();
      // output base64 encoded version of the hash
      System.out.println("hash: " + Base64.getEncoder().encodeToString(output));
      ```

      For more information on secure password storage see OWASP:
      https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
    severity: "WARNING"
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm (SHA1/MD5)"
      category: "security"
      cwe: "CWE-327"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A04:2021-Insecure Design"
      technology:
        - "java"
      security-severity: "Medium"
      related-packages: ['pkg:maven/java.security']
      license: MIT (c) GitLab Inc.
  - id: "java_crypto_rule-WeakTLSProtocol-DefaultHttpClient"
    languages:
      - "java"
    patterns:
      - pattern: "new org.apache.http.impl.client.DefaultHttpClient();"
    message: |
      The `org.apache.http.impl.client.DefaultHttpClient` does not verify the hostnames upon connection.

      This allows for an adversary who is in between the application and the target host to intercept
      potentially sensitive information or transmit malicious data.

      Do not use the `org.apache.http.impl.client.DefaultHttpClient();` as it is deprecated. Instead
      use the new `java.net.http.HttpClient` that was introduced in Java 9.

      Example connecting to a host that will automatically do TLS validation:
      ```
      // Create a new java.net.http.HttpClient
      HttpClient httpClient = HttpClient.newHttpClient();
      // Create a HttpRequest builder
      HttpRequest request = HttpRequest.newBuilder()
              // Create a URI for a website which requires TLS
              .uri(URI.create("https://www.example.com/"))
              // Build the request
              .build();

      // Use the httpClient to send the request and use an HttpResponse.BodyHandlers String type
      HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
      // Debug print
      System.out.println(response);
      ```
    metadata:
      shortDescription: "Improper certificate validation"
      category: "security"
      cwe: "CWE-295"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.httpcomponents.client5/httpclient5']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "java_crypto_rule-WeakTLSProtocol-SSLContext"
    languages:
      - "java"
    patterns:
      - pattern-either:
          - pattern: javax.net.ssl.SSLContext.getInstance("$PROTO", ...)
          - pattern: |
              $PROP = (java.util.Properties $P).getProperty(..., "$PROTO");
              ...
              javax.net.ssl.SSLContext.getInstance($PROP, ...);
      - metavariable-comparison:
          metavariable: $PROTO
          comparison: |
            $PROTO not in ("TLS" "TLSv1.2" "TLSv1.3" "DTLSv1.2" "DTLSv1.3")
    message: |
      Avoid initializing SSLContext with insecure protocols like `SSL`, `SSLv2`, or `SSLv3`.
      These protocols are outdated and do not validate certificates by default. Additionally,
      these older `SSL` versions have many known security issues.

      Instead, use secure protocols like `TLSv1.2` or `TLSv1.3`.
      ```
      SSLContext context = SSLContext.getInstance("TLSv1.3");
      ```
      For more information on see OWASP:
         - https://owasp.org/www-project-web-security-testing-guide/v41/4-Web_Application_Security_Testing/09-Testing_for_Weak_Cryptography/01-Testing_for_Weak_SSL_TLS_Ciphers_Insufficient_Transport_Layer_Protection
    metadata:
      shortDescription: "Improper certificate validation"
      category: "security"
      cwe: "CWE-295"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/javax.net.ssl']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "java_crypto_rule-WeakTLSProtocolVersion"
    languages:
      - "java"
    patterns:
      - pattern-either:
          - pattern-inside: |
              import javax.net.ssl.*;
              ...
          - pattern-inside: |
              import javax.net.ssl.SSLContext;
              ...
      - pattern-either:
          - pattern-inside: |
              SSLContext.getInstance("$UNSAFE_VERSION");
          - pattern-inside: |
              SSLContext.getInstance(...);
              ...
              $ENGINE.setEnabledProtocols(new String[]{...,"$UNSAFE_VERSION",...});
      - pattern-not-inside: |
          $C = SSLContext.getInstance(...);
          ...
          $ENGINE.setEnabledProtocols(new String[]{...,"TLSv1.2",...});
      - pattern-not-inside: |
          $C = SSLContext.getInstance(...);
          ...
          $ENGINE.setEnabledProtocols(new String[]{...,"TLSv1.3",...});
      - pattern-not-inside: |
          $C = SSLContext.getInstance(...);
          ...
          $ENGINE.setEnabledProtocols(new String[]{...,"DTLSv1.2",...});
      - pattern-not-inside: |
          $C = SSLContext.getInstance(...);
          ...
          $ENGINE.setEnabledProtocols(new String[]{...,"DTLSv1.3",...});
      - metavariable-regex:
          metavariable: "$UNSAFE_VERSION"
          regex: "^(TLS|(D)?TLSv1.(0|1))$"
    message: |
      The application was found enabling insecure TLS protocol versions. When enabling protocol
      versions for an `SSLContext`, only the following versions should be allowed:
      - TLSv1.2
      - TLSv1.3
      - DTLSv1.2
      - DTLSv1.3

      To mitigate potential security risks, it is strongly advised to enforce TLS 1.2 as the minimum
      protocol version and disallow older versions such as TLS 1.0. Do note that newer versions of
      Java do not even support TLS 1.0 and will throw `NoSuchAlgorithmException`. Versions of TLS
      prior to 1.2 could expose the connection to downgrade attacks, where an adversary intercepts
      the
      connection and alters the requested protocol version to be a less secure one.

      In many scenarios, relying on the default system configuration does not meet compliance
      standards. This is due to the application being deployed across diverse systems with varying
      configurations and Java versions. While the default value may be secure on modern and
      up-to-date systems, it may not hold true for older systems. Consequently, it is highly
      recommended to explicitly define a secure configuration in all cases.

      Example configuring an SSLContext with TLSv1.2:
      ```
      // Create an SSLContext with TLSv1.2 explicitly
      SSLContext tlsContext = SSLContext.getInstance("TLSv1.2"); // or TLSv1.3, DTLSv1.2, DTLSv1.3

      // Alternatively, set the enabled protocols
      SSLContext serverSslContext = SSLContext.getInstance("TLS");
      SSLEngine serverEngine = serverSslContext.createSSLEngine();
      // Calling setEnabledProtocols will override the original context's configured protocol version
      serverEngine.setEnabledProtocols(new String[]{ "TLSv1.2" });
      ```

      For more information on `SSLContext` see:
      - https://docs.oracle.com/en/java/javase/11/docs/api/java.base/javax/net/ssl/SSLContext.html

      For more information on MiTM attacks see:
      - https://owasp.org/www-community/attacks/Manipulator-in-the-middle_attack
    metadata:
      shortDescription: "Inadequate encryption strength"
      category: "security"
      cwe: "CWE-326"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      related-packages: ['pkg:maven/javax.net.ssl']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "java_endpoint_rule-HostnameVerifier"
    languages:
      - "java"
    message: |
      The `HostnameVerifier` has been set to always return `true`. This effectively 
      disables the validation of server or client certificates. This could allow an 
      adversary who is in between the application and the target host to launch a Man 
      in the middle attack (MITM) i.e intercept potentially sensitive information or 
      inject malicious content into the communication stream.

      To mitigate this vulnerability and enhance the security of your application, it is 
      strongly advised to adhere to the default HostnameVerifier settings. This ensures 
      that the validation mechanism remains intact, providing a crucial layer of security 
      against unauthorized interception and data manipulation.

      Implementing the default HostnameVerifier can be achieved with the following code 
      snippet:
      ```
      // Use the default HostnameVerifier
      HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
      connection.setHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier());
      ```
      For more information on TLS security, refer the following OWASP documentation:
      https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html
    metadata:
      shortDescription: "Improper certificate validation"
      category: "security"
      cwe: "CWE-295"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/javax.net.ssl']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          class $V implements HostnameVerifier {
            ...
          }
      - pattern-either:
          - pattern: |
              boolean verify(...) {
                ...
                return true;
                ...
              }
      - pattern-not:
          patterns:
            - pattern: |
                boolean verify(...) {
                    ...
                    return $VAR;
                    ...
                  }
            - metavariable-regex:
                metavariable: $VAR
                regex: ^((?!true).)*$
            - pattern-not: |
                boolean verify(...) {
                    $VAR = true;
                    ...
                    return $VAR;
                    ...
                  }
    severity: "WARNING"
  - id: "java_endpoint_rule-UnvalidatedRedirect"
    languages:
      - "java"
    message: |
      Unvalidated redirects occur when an application redirects a user to a
      destination URL specified by a user supplied parameter that is not validated.
      Such vulnerabilities can be used to facilitate phishing attacks.

      To avoid open redirect vulnerabilities in Java, one effective strategy is to
      only allow redirection to URLs that are pre-defined in a safe list. This safe
      list can be implemented using a collection like a Map, List, or Dictionary,
      where you store all the valid URLs or URL patterns. When a redirect request is
      made, you can check if the requested URL is in this safe list before proceeding 
      with the redirection. For example:

      ```
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            private List<String> safeUrls = new ArrayList<>();
            safeUrls.add("/home");
            safeUrls.add("/user/profile");
            safeUrls.add("/dashboard");
            
            String redirectUrl = request.getParameter("url");

            if (safeUrls.contains(redirectUrl)) {
                response.sendRedirect(redirectUrl);
            } else {
                response.sendRedirect("/errorPage");
            }
        }"
      ```  
    mode: "taint"
    pattern-sources:
      - patterns:
          - pattern: |
              $URL = ($X.servlet.http.HttpServletRequest $REQ).$M(...);
          - metavariable-regex:
              metavariable: "$M"
              regex: "(getParameter|getCookies|getHeader|getHeaders|getHeaderNames|getPathInfo|getPathTranslated|getContextPath|getQueryString|getRemoteUser|getRequestedSessionId|getRequestURI|getRequestURL|getServletPath|getParts|getPart|getReader)"
    pattern-sinks:
      - pattern-either:
          - pattern: |
              ($X.servlet.http.HttpServletResponse $RES).sendRedirect($URL)
          - pattern: |
              ($X.servlet.http.HttpServletResponse $RES).addHeader("Location", $URL)
    pattern-sanitizers:
      - patterns:
          - pattern-inside: |
              if ($SAFE.contains($URL)){
                ...
              }
          - pattern-either:
              - pattern: |
                  ($X.servlet.http.HttpServletResponse $RES).sendRedirect($URL)
              - pattern: |
                  ($X.servlet.http.HttpServletResponse $RES).addHeader("Location", $URL)
    metadata:
      category: "security"
      cwe: "CWE-601"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      shortDescription: "URL redirection to untrusted site ('Open Redirect')"
      security-severity: "Info"
      related-packages: ['pkg:maven/javax.servlet']
      license: MIT (c) GitLab Inc.
    severity: "ERROR"

  - id: "java_endpoint_rule-X509TrustManager"
    languages:
      - "java"
    message: |
      The `X509TrustManager` has been configured to return null. This effectively disables the
      validation of server or client certificates. This could allow an adversary who is in 
      between the application and the target host to launch a Man in the middle attack (MITM) i.e 
      intercept potentially sensitive information or inject malicious content into the 
      communication stream.

      Consider using the 
      default `TrustManager` instead of implementing a custom one. If you must override
      the default verification process, implement proper TrustManager verification for
      `checkServerTrusted` and `checkClientTrusted` by throwing `CertificateException` if 
      the certificate is invalid.

      For most applications, using the default TrustManager provided by the Java runtime is 
      sufficient and recommended. Following is an example using the built in `TrustManagerFactory` 
      to manage validating certificate chains:
      ```
      // Use the default TrustManagerFactory
      TrustManagerFactory trustManagerFactory =
      TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
      // Use default system KeyStore, alternatively pass in your own keystore.
      trustManagerFactory.init((KeyStore) null);
      // Create SSLContext for TLS connections
      SSLContext tlsContext = SSLContext.getInstance("TLS");
      // Initialize the tlsContext with our trust manager and a SecureRandom number generator.
      tlsContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
      ```
      For more information on TLS security, refer the following OWASP documentation:
      https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html
    metadata:
      shortDescription: "Improper certificate validation"
      category: "security"
      cwe: "CWE-295"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/javax.net.ssl']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          class $V implements X509TrustManager {
            ...
          }
      - pattern-either:
          - pattern: "public void checkClientTrusted(...) {}"
          - pattern: "public void checkServerTrusted(...) {}"
          - patterns:
              - pattern-either:
                  - pattern: |
                      X509Certificate[] getAcceptedIssuers() {
                        ...
                        return null;
                        ...
                      }
              - pattern-not:
                  patterns:
                    - pattern: |
                        X509Certificate[] getAcceptedIssuers() {
                            ...
                            return $VAR;
                            ...
                          }
                    - metavariable-regex:
                        metavariable: $VAR
                        regex: ^((?!null).)*$
                    - pattern-not: |
                        X509Certificate[] getAcceptedIssuers() {
                            $VAR = null;
                            ...
                            return $VAR;
                            ...
                          }
    severity: "WARNING"
  - id: "java_file_rule-FileUploadFileName"
    languages:
      - "java"
    message: |
      The filename provided by the FileUpload API can be tampered with
      which could lead to unauthorized access or file inclusion vulnerabilities.
      To mitigate this risk, it is essential to conduct rigorous validation of the
      filenames provided by clients. This validation should ensure that the filename
      adheres to a predefined structure, is devoid of potentially dangerous characters
      (such as forward slashes / and backslashes \), and corresponds to an authorized
      file only.

      For example, as a remediation strategy, the application could:
      1. Sanitize Filenames: Create a function to sanitize filenames by removing
         or replacing unauthorized characters, including path traversal sequences (../ or ..\).
      2. Allowlist Validation: Implement a allowlist approach, allowing only filenames
         that match a specific pattern or are part of a predefined list.
      3. Use Server-Generated Filenames: Rather than relying on client-provided filenames,
         generate unique names server-side for storing files.
      4. Verify File Paths: Ensure files are being saved in the correct,
         intended directory, and prevent redirection to unauthorized directories.

      Example remediation:
      ```
        public class FileUploadHandler {

              protected void doPost(HttpServletRequest request, HttpServletResponse response)
                    throws ServletException, IOException {

                Part filePart = request.getPart("file");
                String fileName = filePart.getSubmittedFileName();

                // removes any path information from the filename
                String sanitizedFileName = sanitizeFileName(fileName);
                if (!isFileNameAllowed(sanitizedFileName)) {
                    throw new SecurityException("Invalid file name");
                }

                // Generate a unique file name for storage
                String storedFileName = UUID.randomUUID().toString() + ".txt";

                Path targetPath = Paths.get("uploads").resolve(storedFileName);
                Files.copy(fileContent, targetPath, StandardCopyOption.REPLACE_EXISTING);
            }

            private String sanitizeFileName(String fileName) {
                return Paths.get(fileName).getFileName().toString();
            }

            private boolean isFileNameAllowed(String fileName) {
                return fileName.matches("[a-zA-Z0-9._-]+");
            }
        }
      ```
    pattern-either:
      - patterns:
          - pattern-inside: |
              $FILES = (ServletFileUpload $SFU).parseRequest(($X.servlet.http.HttpServletRequest $REQ));
              ...
              for(FileItem $ITEM : $FILES) {
                ...
              }
          - pattern: $ITEM.getName()
      - pattern: ($X.servlet.http.Part $PART).getSubmittedFileName()
    metadata:
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      cwe: "CWE-22"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Info"
      category: "security"
      related-packages: ['pkg:maven/org.apache.commons.fileupload/commons-fileupload']
      technology:
        - "java"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "java_file_rule-FilenameUtils"
    languages:
      - "java"
    message: |
      The filename provided by the FileUpload API can be tampered with by the client to reference
      unauthorized files. The provided filename should be properly validated to ensure it's properly
      structured, contains no unauthorized path characters (e.g., / \), and refers to an authorized
      file.

      The application was found to take a parameter from user input to construct a path name. If an
      unfiltered parameter is passed to this file API, files from an arbitrary filesystem location
      could be read. When data from an unstrusted source is untrusted source is used to construct
      a file path, an attacker could potentially gain access to restrcited files locations outside
      the relevant context.

      For example, if the application tries to access the users profile picture based on their user
      name by concatenating the username to the filepath:

      "images/userprofiles/" + username

      The expected result of this would be "images/userprofiles/alice", however an attacker could
      use a malicious input such as "../../../etc/passwd" to gain access to and/or manipulate
      sensitive information

      Assume all input is malicious. Use an "accept known good" input validation strategy.

      Inputs can be sanitized by using the getName() method with concat() method to remove the 
      potentially malicious path traversal and limit the scope to a restricted directory. Or 
      input can also be sanitized by using resolve() method alongwith startsWith() method to 
      verify that the base path of the file is safe and expected.

      Example of limiting path traversal using getName:

      ```
      protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

          String input = req.getHeader("input");

          input = getName(input);
          
          String safePath = concat(basePath, input);

          // Read the contents of the file
          File file = new File(safePath);
      }
      ```
    metadata:
      category: "security"
      cwe: "CWE-22"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.apache.commons.io/commons-io']
      technology:
        - "java"
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sanitizers:
      - pattern: |
          $NAME = org.apache.commons.io.FilenameUtils.getName(...);
          ...
          $SAFE = concat($BASE, $NAME);
      - pattern: |
          $RET $FUN(...){
            ...
            $PATH = $BP.resolve(...);
            ...
            if(!$PATH.startsWith(...)) {
              throw new $EXC(...);
            }
            ...
          }
    pattern-sources:
      - pattern: "(HttpServletRequest $REQ)"
    pattern-sinks:
      - pattern: "org.apache.commons.io.FilenameUtils.concat(...)"
      - pattern: "org.apache.commons.io.FilenameUtils.getFullPath(...)"
      - pattern: "org.apache.commons.io.FilenameUtils.getFullPathNoEndSeparator(...)"
      - pattern: "org.apache.commons.io.FilenameUtils.getPath(...)"
      - pattern: "org.apache.commons.io.FilenameUtils.getPathNoEndSeparator(...)"
      - pattern: "org.apache.commons.io.FilenameUtils.normalize(...)"
      - pattern: "org.apache.commons.io.FilenameUtils.normalizeNoEndSeparator(...)"
      - pattern: "org.apache.commons.io.FilenameUtils.normalizeNoEndSeparator(...)"
      - pattern: "org.apache.commons.io.FilenameUtils.removeExtension(...)"
      - pattern: "org.apache.commons.io.FilenameUtils.separatorsToSystem(...)"
      - pattern: "org.apache.commons.io.FilenameUtils.separatorsToUnix(...)"
      - pattern: "org.apache.commons.io.FilenameUtils.separatorsToWindows(...)"
    severity: "WARNING"
  - id: java_inject_rule-CommandInjection
    languages:
      - java
    mode: taint
    pattern-sources:
      - pattern: (javax.servlet.http.HttpServletRequest $R).$METHOD(...)
      - pattern: (java.util.Scanner $S).$METHOD(...)
      - pattern: (java.util.stream.Stream).$METHOD(...)
      - pattern: (java.util.StringJoiner $SJ).toString(...)
      - pattern: (java.sql.ResultSet.getString $R).$METHOD(...)
      - pattern: (java.lang.System $S).getProperty(...)
      - pattern: (java.lang.System $S).getenv(...)
      - pattern: (java.lang.StringBuilder $SB).toString(...)
      - pattern: (java.io.FileInputStream $F).read(...)
      - pattern: (java.io.FileReader $F).read(...)
      - pattern: (java.net.Socket $S).getInputStream(...)
      - pattern: (java.net.Socket $S).getOutputStream(...)
      - pattern: (java.net.DatagramSocket $S).receive(...)
      - pattern: (java.net.DatagramSocket $S).getInputStream(...)
      - pattern: java.nio.file.Files.readAllBytes(...)
      - pattern: java.nio.file.Files.readAllLines(...)
      - pattern: java.nio.file.Files.lines(...)
      - pattern: java.nio.file.Files.newBufferedReader(...)
      - pattern: org.apache.commons.io.IOUtils.toString(...)
      - pattern: org.apache.commons.io.IOUtils.readLines(...)
      - pattern: org.apache.commons.io.IOUtils.toByteArray(...)
      - pattern: (com.fasterxml.jackson.databind.ObjectMapper $OM).readValue(...)
      - pattern: (com.fasterxml.jackson.databind.ObjectMapper $OM).treeToValue(...)
      - pattern: $CLASS.$METHOD(..., (javax.servlet.http.HttpServletRequest $R), ...)
      - pattern: $FUNC(..., (javax.servlet.http.HttpServletRequest $R), ...)
      - patterns:
          - pattern-inside: $FUNC(..., String $X, ...) { ... }
          - focus-metavariable: $X
    pattern-propagators:
      - pattern: $LIST.add($X)
        from: $X
        to: $LIST
      - pattern: $MAP.put(..., $X)
        from: $X
        to: $MAP
      - pattern: $STR.concat($X)
        from: $X
        to: $STR
      - pattern: $STR = String.format(..., $X, ...)
        from: $X
        to: $STR
      - pattern: $STR = String.join(..., $X, ...)
        from: $X
        to: $STR
    pattern-sinks:
      - pattern: (ProcessBuilder $PB).command(...)
      - pattern: new ProcessBuilder(...)
      - pattern: (Runtime $R).exec(...)
    message: |
      OS command injection is a critical vulnerability that can lead to a full system
      compromise as it may allow an adversary to pass in arbitrary commands or arguments
      to be executed.

      User input should never be used in constructing commands or command arguments
      to functions which execute OS commands. This includes filenames supplied by
      user uploads or downloads.

      Ensure your application does not:

      - Use user-supplied information in the process name to execute.
      - Use user-supplied information in an OS command execution function which does
      not escape shell meta-characters.
      - Use user-supplied information in arguments to OS commands.

      The application should have a hardcoded set of arguments that are to be passed
      to OS commands. If filenames are being passed to these functions, it is
      recommended that a hash of the filename be used instead, or some other unique
      identifier. It is strongly recommended that a native library that implements
      the same functionality be used instead of using OS system commands, due to the
      risk of unknown attacks against third party commands.

      When specifying the OS command, ensure the application uses the full path
      information, otherwise the OS may attempt to look up which process to execute
      and could be vulnerable to untrusted search path vulnerabilities (CWE-426).

      Example of safely executing an OS command:
      ```
      public static void executeCommand(String userFileData) throws java.io.IOException {
          // Generate a random filename, do not use user input
          String fileName = UUID.randomUUID().toString();
          // Create a Buffered/FileWriter
          BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
          // Write the user content to our random file
          writer.write(userFileData);
          // Close the file to flush contents
          writer.close();
          // Create the process builder with a hardcoded path to the binary, and our randomly
      generated filename
          ProcessBuilder processBuilder = new ProcessBuilder("/opt/app/path", fileName);
          // Start the process
          Process process = processBuilder.start();
          // Handle/redirect output of process here
          // ...
      }
      ```

      For more information on OS command injection, see OWASP's guide:
      https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html
    severity: "WARNING"
    metadata:
      shortDescription: "Improper neutralization of special elements used in an OS command ('OS Command Injection')"
      category: "security"
      cwe: "CWE-78"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "java"
      security-severity: "High"
      related-packages: ['pkg:maven/java.lang']
      license: MIT (c) GitLab Inc.
  - id: "java_inject_rule-ELInjection"
    languages:
      - "java"
    message: |
      This rule identifies potential Expression Language (EL) injection vulnerabilities within Java applications. 
      The rule targets use of `createValueExpression`, `createMethodExpression`, `ELProcessor.eval`, `getValue`, 
      and `setValue` methods, particularly when input to these methods is not a hardcoded string, indicating dynamic 
      evaluation of potentially untrusted input. 

      `createValueExpression` creates a `ValueExpression` object which gets evaluated upon calling methods like 
      `getValue()` and `setValue()` or a Lambda `invoke()` i.e. it evaluates the expression passed to the 
      `createValueExpression` method.

      Similarly, `createMethodExpression` creates a `MethodExpression` object which gets evaluated upon calling 
      methods like `invoke()` and `getMethodInfo()`.
      `ELProcessor.eval`, `getValue()`, and `setValue()` methods all evaluate their expressions which are passed 
      as parameters.

      Calling these method directly with user-supplied input may allow an adversary to execute arbitrary Java 
      code, including OS system commands. Never call these methods directly with user-supplied input. Consider 
      alternate methods such as a lookup table to take user input and resolve hardcoded values.

      Secure example:

      ```
      import javax.el.ELProcessor;
      import java.util.Set;

      public class SafeELHandling {
          private static final Set<String> ALLOWED_VALUES = Set.of("value1", "value2", "value3");

          public void processInput(String userInput) {
              // Validate user input against the allowlist
              if (!ALLOWED_VALUES.contains(userInput)) {
                  throw new IllegalArgumentException("Invalid input");
              }
              
              ELProcessor elProcessor = new ELProcessor();
              elProcessor.defineBean("userInput", userInput);
              
              // Example EL expression using the safe, predefined input
              String result = (String) elProcessor.eval(userInput);
          }
      }
      ```
    metadata:
      category: "security"
      cwe: "CWE-917"
      shortDescription: "Improper neutralization of special elements used in an expression language statement ('Expression
        Language Injection')"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "java"
      security-severity: "Info"
      related-packages: ['pkg:maven/javax.el']
      license: MIT (c) GitLab Inc.
    pattern-either:
      - patterns:
          - pattern: |
              (ExpressionFactory $EXP).createValueExpression((ELContext $CTX), $EXPR,
              ...)
          - pattern-not: |
              (ExpressionFactory $EXP).createValueExpression((ELContext $CTX), "...",
              ...)
      - patterns:
          - pattern: |
              (ExpressionFactory $EXP).createMethodExpression((ELContext $CTX), $EXPR,
              ...)
          - pattern-not: |
              (ExpressionFactory $EXP).createMethodExpression((ELContext $CTX), "...",
              ...)
      - patterns:
          - pattern: |
              ($X.el.ELProcessor $P).eval(...)
          - pattern-not: |
              ($X.el.ELProcessor $P).eval("...", ...)
      - patterns:
          - pattern: |
              ($X.el.ELProcessor $P).getValue(...)
          - pattern-not: |
              ($X.el.ELProcessor $P).getValue("...", ...)
      - patterns:
          - pattern: |
              ($X.el.ELProcessor $P).setValue(...)
          - pattern-not: |
              ($X.el.ELProcessor $P).setValue("...", "...")   
    severity: "WARNING"
  - id: "java_inject_rule-FileDisclosureRequestDispatcher"
    languages:
      - "java"
    mode: "taint"
    pattern-sources:
      - pattern: "(javax.servlet.http.HttpServletRequest $VAR).getParameter(...)"
      - pattern: "(javax.servlet.http.HttpServletRequest $VAR).getParameterNames();"
      - pattern: "(javax.servlet.http.HttpServletRequest $VAR).getParameterValues(...);"
      - pattern: "(javax.servlet.http.HttpServletRequest $VAR).getParameterMap();"
      - pattern: "(javax.servlet.http.HttpServletRequest $VAR).getHeader(...);"
      - pattern: "(javax.servlet.http.HttpServletRequest $VAR).getPathInfo();"
    pattern-sinks:
      - patterns:
          - pattern-not-inside: |
              $VAL = $MAP.getOrDefault(..., "...");
              ...
          - pattern-inside: |
              $REQ = $HTTP.getRequestDispatcher(...);
              ...
          - pattern-either:
              - pattern: "$REQ.include($FST, $SND)"
              - pattern: "$REQ.forward($FST, $SND)"
    message: |
      The `HttpRequest.getRequestDispatcher()`'s `include` and `forward` methods will return
      any file that is resolvable within the web application context. This includes the `web.xml`
      file, any compiled classes, `jsp` files, and additional JAR or WAR libraries that are
      accessible.

      Never pass user-supplied input directly to any of these methods. Use a lookup table or
      hardcode
      which views or paths the user should be directed to. Another option is to use a simple HTTP
      redirect by returning an empty response body with a 301 status code and a `Location` redirect
      header. In Java servlets, this can be done by using the `response.sendRedirect(...)` method.

      Example using a redirect instead of a `RequestDispatcher`:
      ```
      // Create a look up table or pull from a data source
      HashMap<String, String> lookupTable = new HashMap<>();
      lookupTable.put("key1", "/Resource1");
      lookupTable.put("key2", "/Resource2");
      // Get user input
      String userInput = request.getParameter("key");
      // Look up resource to redirect to from the user input
      String redirectValue = lookupTable.getOrDefault(userInput, "/Resource1");
      // Redirect the user
      response.sendRedirect(redirectValue);
      ```
    metadata:
      shortDescription: "Files or directories accessible to external parties"
      category: "security"
      cwe: "CWE-552"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "HIGH"
      related-packages: ['pkg:maven/javax.servlet']
      license: MIT (c) GitLab Inc.
    severity: "ERROR"
  - id: "java_inject_rule-FileDisclosureSpringFramework"
    languages:
      - "java"
    mode: "taint"
    pattern-sources:
      - pattern: "(javax.servlet.http.HttpServletRequest $VAR).getParameter(...)"
      - pattern: "(javax.servlet.http.HttpServletRequest $VAR).getParameterNames();"
      - pattern: "(javax.servlet.http.HttpServletRequest $VAR).getParameterValues(...);"
      - pattern: "(javax.servlet.http.HttpServletRequest $VAR).getParameterMap();"
      - pattern: "(javax.servlet.http.HttpServletRequest $VAR).getHeader(...);"
      - pattern: "(javax.servlet.http.HttpServletRequest $VAR).getPathInfo();"
    pattern-sinks:
      - patterns:
          - pattern-not-inside: |
              $FST = $MAP.getOrDefault(..., "...");
                ...
          - pattern: "new org.springframework.web.servlet.ModelAndView($FST, ...);"
          - focus-metavariable: "$FST"
      - patterns:
          - pattern-not-inside: |
              $FST = $MAP.getOrDefault(..., "...");
              ...
          - pattern-inside: |
              $MVC = new org.springframework.web.servlet.ModelAndView();
              ...
          - pattern: "$MVC.setViewName(...);"
    message: |
      The `org.springframework.web.servlet.ModelAndView` class may
      potentially allow access to restricted files if called with user-supplied input.

      The ModelAndView class looks up a view by name to resolve a `.jsp`
      file. If this view name comes from user-supplied input, it could be abused to attempt
      to return a JSP view that the user should not have access to.

      Use a lookup table or hardcode which views or paths the user should be directed to.

      Example using a lookup table to resolve a view from a Spring MVC application:
      ```
      @RequestMapping(value="/mvc", method=RequestMethod.GET)
      public ModelAndView mvc(HttpServletRequest request, HttpServletResponse response, Model model)
       {
        // Create a look up table or pull from a data source
        HashMap<String, String> lookupTable = new HashMap<>();
        lookupTable.put("key1", "view1");
        lookupTable.put("key2", "view2");
        // Get user input
        String userInput = request.getParameter("key");
        // Look up view from the user input
        String viewValue = lookupTable.getOrDefault(userInput, "Resource1");
        // return the new model and view
        return new ModelAndView(viewValue);
      }
      ```

      Example using a redirect instead of a `RequestDispatcher` in Spring:
      ```
      @RequestMapping(value="/mvc", method=RequestMethod.GET)
      public void mvc(HttpServletRequest request, HttpServletResponse response, Model model)
       {
        // Create a look up table or pull from a data source
        HashMap<String, String> lookupTable = new HashMap<>();
        lookupTable.put("key1", "view1");
        lookupTable.put("key2", "view2");
        // Get user input
        String userInput = request.getParameter("key");
        // Look up resource to redirect to from the user input
        String redirectValue = lookupTable.getOrDefault(userInput, "/Resource1");
        // return the new model and view
        response.sendRedirect(redirectValue);
      }
      ```
    metadata:
      shortDescription: "Files or directories accessible to external parties"
      category: "security"
      cwe: "CWE-552"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "HIGH"
      related-packages: ['pkg:maven/org.springframework/spring-web']
      license: MIT (c) GitLab Inc.
    severity: "ERROR"
  - id: "java_inject_rule-HttpParameterPollution"
    languages:
      - "java"
    mode: "taint"
    pattern-sources:
      - pattern: "(HttpServletRequest $REQ).getParameter(...)"
    pattern-sanitizers:
      - pattern: "java.net.URLEncoder.encode(...)"
      - pattern: "com.google.common.net.UrlEscapers.urlPathSegmentEscaper().escape(...)"
    pattern-sinks:
      - pattern: "new org.apache.http.client.methods.HttpGet(...)"
      - pattern: "new org.apache.commons.httpclient.methods.GetMethod(...)"
      - pattern: "(org.apache.commons.httpclient.methods.GetMethod $GM).setQueryString(...)"
    message: |
      The application was found including unvalidated user input into a URL, which could lead to
      HTTP Parameter Pollution (HPP) or worse, Server Side Request Forgery (SSRF). This could
      allow an adversary to override the value of a URL or a request parameter.  HTTP Parameter
      Pollution
      (HPP) attacks consist of injecting encoded query string delimiters into other existing
      parameters. If a web
      application does not properly sanitize the user input, an adversary may modify the logic of
      these
      requests to other applications.

      To remediate this issue, never allow user input directly into creation of a URL or URL
      parameter. Consider
      using a map to look up user-supplied information and return exact values to be used in the
      generation of
      requests.

      Example using a map to look up a key to be used in a HTTP request:
      ```
      HashMap<String, String> lookupTable = new HashMap<>();
      lookupTable.put("key1", "value1");
      lookupTable.put("key2", "value2");
      String userInput = request.getParameter("key");

      // Create a CloseableHttpClient, ideally any requests issued should be done
      // out-of-band from the servlet request itself (such as using a separate thread/scheduler
      system)
      try (final CloseableHttpClient httpClient = HttpClients.createDefault()) {
          // Lookup the value from our user input from our lookupTable
          String value = lookupTable.getOrDefault(userInput, "value1");
          // Construct the url, with the hardcoded url and only pass in the value from the
      lookupTable,
          // not direct user input
          final HttpGet httpget = new HttpGet("https://example.com/getId?key="+value);
          // Execute the request
          CloseableHttpResponse clientResponse = httpClient.execute(httpget);
          // Read the response
          byte[] responseData = clientResponse.getEntity().getContent().readAllBytes();
          // Handle the response
          // ...
      }
      ```

      If using a map is not possible, the user-supplied input must be encoded prior to use, and
      never allow full
      URLs:
      ```
      // Get user input
      String userInput = request.getParameter("key");
      // Encode the string using java.net.URLEncoder with the UTF-8 character set
      String encodedString = java.net.URLEncoder.encode(userInput, StandardCharsets.UTF_8);
      // Create a CloseableHttpClient, ideally any requests issued should be done
      // out-of-band from the servlet request itself (such as using a separate thread/scheduler
      system)
      try (final CloseableHttpClient httpClient = HttpClients.createDefault()) {
        // Construct the url, with the hardcoded url and only pass in the encoded value, never a
      full URL
        final HttpGet httpget = new HttpGet("https://example.com/getId?key="+encodedString);
        // Execute the request
        CloseableHttpResponse clientResponse = httpClient.execute(httpget);
        // Read the response
        byte[] responseData = clientResponse.getEntity().getContent().readAllBytes();
        // handle the response
      }
      ```

      For more information on SSRF see OWASP:
      https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html

      For more information on HTTP Parameter Pollution see:
      https://en.wikipedia.org/wiki/HTTP_parameter_pollution
    severity: "ERROR"
    metadata:
      shortDescription: "Improper neutralization of argument delimiters in a command ('Argument Injection')"
      category: "security"
      cwe: "CWE-88"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "java"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.httpcomponents.client5/httpclient5']
      license: MIT (c) GitLab Inc.
  - id: "java_inject_rule-LDAPInjection"
    languages:
      - "java"
    mode: "taint"
    pattern-sinks:
      - pattern: "javax.naming.ldap.LdapName(...)"
      - pattern: "(javax.naming.directory.Context $C).lookup(...)"
      - pattern: "(javax.naming.Context $C).lookup(...)"
      - patterns:
          - pattern-inside: |-
              (com.unboundid.ldap.sdk.LDAPConnection $C).search($QUERY, ...)
          - pattern: "$QUERY"
      - patterns:
          - pattern-either:
              - pattern: "$CTX.lookup(...)"
              - patterns:
                  - pattern-inside: |-
                      $CTX.search($QUERY, ...)
                  - pattern: "$QUERY"
              - patterns:
                  - pattern-inside: |-
                      $CTX.search($NAME, $FILTER, ...)
                  - pattern: "$FILTER"
          - metavariable-pattern:
              metavariable: "$CTX"
              pattern-either:
                - pattern: "(DirContext $C)"
                - pattern: "(InitialDirContext $IDC)"
                - pattern: "(LdapContext $LC)"
                - pattern: "(EventDirContext $EDC)"
                - pattern: "(LdapCtx $LC)"
                - pattern: "(javax.naming.directory.DirContext $C)"
                - pattern: "(javax.naming.directory.InitialDirContext $IDC)"
                - pattern: "(javax.naming.ldap.LdapContext $LC)"
                - pattern: "(javax.naming.event.EventDirContext $EDC)"
                - pattern: "(com.sun.jndi.ldap.LdapCtx $LC)"
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern-inside: |-
                      $CTX.list($QUERY, ...)
                  - pattern: "$QUERY"
              - patterns:
                  - pattern-inside: |-
                      $CTX.lookup($QUERY, ...)
                  - pattern: "$QUERY"
              - patterns:
                  - pattern-inside: |-
                      $CTX.search($QUERY, ...)
                  - pattern: "$QUERY"
              - patterns:
                  - pattern-inside: |-
                      $CTX.search($NAME, $FILTER, ...)
                  - pattern: "$FILTER"
          - metavariable-pattern:
              metavariable: "$CTX"
              pattern-either:
                - pattern: "(LdapTemplate $LT)"
                - pattern: "(LdapOperations $LO)"
                - pattern: "(org.springframework.ldap.core.LdapTemplate $LT)"
                - pattern: "(org.springframework.ldap.core.LdapOperations $LO)"
    pattern-sources:
      - patterns:
          - pattern-inside: |
              $FUNC(..., $VAR, ...) {
                ...
              }
          - pattern: "$VAR"
      - patterns:
          - pattern-inside: |
              $FUNC(..., $X, ...) {
                ...
                $VAR = ... + $X;
                ...
              }
          - pattern: "$VAR"
    message: |
      LDAP injection attacks exploit LDAP queries to influence how data is returned by
      the LDAP server.

      Later versions of Java's `InitialDirContext.search` introduced a four argument method, one of
      which is the `filterArg` parameter. The `filterArg` will be automatically encoded when
      querying
      the LDAP server. If this method signature is not available, the application must encode the
      LDAP strings manually.

      More details on the four argument `search` method can be found here:
      https://docs.oracle.com/en/java/javase/20/docs/api/java.naming/javax/naming/directory/InitialDirContext.html#search(javax.naming.Name,java.lang.String,java.lang.Object[],javax.naming.directory.SearchControls)

      To encode the string manually, it is recommended that all input passed to LDAP querying
      systems
      encode the following values:

      - Any occurrence of the null character must be escaped as “\00”.
      - Any occurrence of the open parenthesis character must be escaped as “\28”.
      - Any occurrence of the close parenthesis character must be escaped as “\29”.
      - Any occurrence of the asterisk character must be escaped as “\2a”.
      - Any occurrence of the backslash character must be escaped as “\5c”.

      Example function that safely encodes user-supplied input to be used in an LDAP query.
      ```
      public static String encodeLDAPString(String input) {
        // Note the \ character is replaced first
        CharSequence[] chars = new CharSequence[] { "\\", "\0", "(", ")", "*" };
        CharSequence[] encoded = new CharSequence[] { "\\5c", "\\00", "\\28", "\\29", "\\2a" };
        // Iterate over each character sequence, replacing the raw value with an encoded version of
      it
        for (int i = 0; i < chars.length; i++)
        {
            // re-assign to input
            input = input.replace(chars[i], encoded[i]);
        }
        // return our modified input string
        return input;
      }
      ```

      Example code that using the `filterArgs` parameter which automatically encodes for us:
      ```
      // Create a properties to hold the ldap connection details
      Properties props = new Properties();
      // Use the com.sun.jndi.ldap.LdapCtxFactory factory provider
      props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
      // The LDAP server URL
      props.put(Context.PROVIDER_URL, "ldap://ldap.example.org:3889");
      // User details for the connection
      props.put(Context.SECURITY_PRINCIPAL, "cn=admin,dc=example,dc=org");
      // LDAP account password
      String ldapAccountPassword = getAccountPasswordFromSecureStoreOrKMS();
      // Pass in the LDAP password
      props.put(Context.SECURITY_CREDENTIALS, ldapAccountPassword);

      // Create the LDAPContext
      InitialDirContext ldapContext = new InitialDirContext(props);
      // Example using SUBTREE_SCOPE SearchControls
      SearchControls searchControls = new SearchControls();
      searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);

      // Get user input for query
      String userQuery = someUserInput;
      // Use searchArguments to hold the user-supplied input
      Object[] searchArguments = new Object[]{userQuery};
      // Hardcode the BaseDN, use the {0} format specifier to use the searchArguments array value,
      and pass in the search controls.
      // searchArguments automatically encode
      NamingEnumeration answer = ldapContext.search("dc=example,dc=org", "(cn={0})",
      searchArguments, searchControls);
      // Process the response answer
      while (answer.hasMoreElements()) {
        ...
      }
      ```

      For more information on LDAP Injection see OWASP:
      https://cheatsheetseries.owasp.org/cheatsheets/LDAP_Injection_Prevention_Cheat_Sheet.html
    severity: "WARNING"
    metadata:
      shortDescription: "Improper neutralization of special elements used in an LDAP query ('LDAP Injection')"
      category: "security"
      cwe: "CWE-90"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "java"
      security-severity: "Medium"
      related-packages: ['pkg:maven/com.unboundid/unboundid-ldapsdk']
      license: MIT (c) GitLab Inc.
  - id: "java_inject_rule-OgnlInjection"
    languages:
      - "java"
    mode: "taint"
    pattern-sources:
      - patterns:
          - pattern-inside: |
              $FUNC(..., $VAR, ...) {
                ...
              }
          - metavariable-pattern:
              metavariable: "$VAR"
              pattern-either:
                - pattern: "(String $S)"
                - pattern: "(Map<String, ?> $M)"
                - pattern: "(Map<String, String> $M)"
                - pattern: "(Map<String, Object> $M)"
          - pattern: "$VAR"
    pattern-sinks:
      - patterns:
          - pattern-inside: |-
              com.opensymphony.xwork2.util.TextParseUtil.translateVariables($VAL, ...)
          - pattern: "$VAL"
      - patterns:
          - pattern-inside: |-
              com.opensymphony.xwork2.util.TextParseUtil.translateVariablesCollection($VAL, ...)
          - pattern: "$VAL"
      - pattern: "com.opensymphony.xwork2.util.TextParseUtil.shallBeIncluded(...)"
      - pattern: "com.opensymphony.xwork2.util.TextParseUtil.commaDelimitedStringToSet(...)"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.util.TextParser $P).evaluate($VAR, $VAL, ...)
          - pattern: "$VAL"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.util.OgnlTextParser $P).evaluate($VAR, $VAL, ...)
          - pattern: "$VAL"
      - pattern: "(com.opensymphony.xwork2.ognl.OgnlReflectionProvider $P).getGetMethod($CLZ, ...)"
      - pattern: "(com.opensymphony.xwork2.ognl.OgnlReflectionProvider $P).getSetMethod($CLZ, ...)"
      - pattern: "(com.opensymphony.xwork2.ognl.OgnlReflectionProvider $P).getField($CLZ, ...)"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.ognl.OgnlReflectionProvider $P).setProperties($MAP, ...)
          - pattern: "$MAP"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.ognl.OgnlReflectionProvider $P).setProperty($VAL, ...)
          - pattern: "$VAL"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.ognl.OgnlReflectionProvider $P).getValue($VAL, ...)
          - pattern: "$VAL"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.ognl.OgnlReflectionProvider $P).setValue($VAL, ...)
          - pattern: "$VAL"
      - pattern: "(com.opensymphony.xwork2.util.reflection.ReflectionProvider $P).getGetMethod($CLZ, ...)"
      - pattern: "(com.opensymphony.xwork2.util.reflection.ReflectionProvider $P).getSetMethod($CLZ, ...)"
      - pattern: "(com.opensymphony.xwork2.util.reflection.ReflectionProvider $P).getField($CLZ, ...)"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.util.reflection.ReflectionProvider $P).setProperties($MAP, ...)
          - pattern: "$MAP"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.util.reflection.ReflectionProvider $P).setProperty($VAR, ...)
          - pattern: "$VAR"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.util.reflection.ReflectionProvider $P).getValue($VAR, ...)
          - pattern: "$VAR"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.util.reflection.ReflectionProvider $P).setValue($VAR, ...)
          - pattern: "$VAR"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.ognl.OgnlUtil $P).setProperties($MAP, ...)
          - pattern: "$MAP"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.ognl.OgnlUtil $P).setProperty($VAR, ...)
          - pattern: "$VAR"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.ognl.OgnlUtil $P).getValue($VAR, ...)
          - pattern: "$VAR"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.ognl.OgnlUtil $P).setValue($VAR, ...)
          - pattern: "$VAR"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.ognl.OgnlUtil $P).callMethod($VAR, ...)
          - pattern: "$VAR"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.ognl.OgnlUtil $P).compile($VAR, ...)
          - pattern: "$VAR"
      - pattern: "(org.apache.struts2.util.VelocityStrutsUtil $P).evaluate(...)"
      - pattern: "org.apache.struts2.util.StrutsUtil.findString(...)"
      - pattern: "org.apache.struts2.util.StrutsUtil.findValue(..., $VAL)"
      - pattern: "org.apache.struts2.util.StrutsUtil.getText(...)"
      - pattern: "org.apache.struts2.util.StrutsUtil.translateVariables(...)"
      - patterns:
          - pattern-inside: |-
              org.apache.struts2.util.StrutsUtil.makeSelectList($VAR, ...)
          - pattern: "$VAR"
      - patterns:
          - pattern-inside: |-
              (org.apache.struts2.views.jsp.ui.OgnlTool $T).findValue($VAR, ...)
          - pattern: "$VAR"
      - pattern: "(com.opensymphony.xwork2.util.ValueStack $V).findString(...)"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.util.ValueStack $V).findValue($VAR, ...)
          - pattern: "$VAR"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.util.ValueStack $V).setValue($VAR, ...)
          - pattern: "$VAR"
      - patterns:
          - pattern-inside: |-
              (com.opensymphony.xwork2.util.ValueStack $V).setParameter($VAR, ...)
          - pattern: "$VAR"
    message: |
      The Object Graph Navigation Language (OGNL) is an expression language that allows access to
      Java objects and properties stored in an ActionContext. Usage of these low-level
      functions is discouraged because they can effectively execute strings as code, leading to
      remote code execution vulnerabilities. Consider using struts tags when processing
      user-supplied input and templates.

      Much like the Struts security guide recommending to not use raw `${}` EL expressions,
      do not call or use the following OGNL packages with user-supplied input:

      - `com.opensymphony.xwork2.ognl`
      - `com.opensymphony.xwork2.util`
      - `com.opensymphony.xwork2.util.reflection`
      - `org.apache.struts2.util.StrutsUtil`

      For more information on Struts2 security see:
      https://struts.apache.org/security/#do-not-use-incoming-untrusted-user-input-in-forced-expression-evaluation
    severity: "WARNING"
    metadata:
      shortDescription: "Expression injection (OGNL)"
      category: "security"
      cwe: "CWE-917"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "java"
      security-severity: "High"
      related-packages: ['pkg:maven/com.opensymphony.xwork2/xwork-core']
      license: MIT (c) GitLab Inc.
  - id: "java_inject_rule-SpotbugsPathTraversalAbsolute"
    languages:
      - "java"
    mode: "taint"
    pattern-sources:
      - pattern: (javax.servlet.http.HttpServletRequest $R).$METHOD(...)
      - pattern: (java.util.Scanner $S).$METHOD(...)
      - pattern: (java.util.stream.Stream).$METHOD(...)
      - pattern: (java.util.StringJoiner $SJ).toString(...)
      - pattern: (java.sql.ResultSet.getString $R).$METHOD(...)
      - pattern: (java.lang.System $S).getProperty(...)
      - pattern: (java.lang.System $S).getenv(...)
      - pattern: (java.lang.StringBuilder $SB).toString(...)
      - pattern: (java.io.FileInputStream $F).read(...)
      - pattern: (java.io.FileReader $F).read(...)
      - pattern: (java.net.Socket $S).getInputStream(...)
      - pattern: (java.net.Socket $S).getOutputStream(...)
      - pattern: (java.net.DatagramSocket $S).receive(...)
      - pattern: (java.net.DatagramSocket $S).getInputStream(...)
      - pattern: java.nio.file.Files.readAllBytes(...)
      - pattern: java.nio.file.Files.readAllLines(...)
      - pattern: java.nio.file.Files.lines(...)
      - pattern: java.nio.file.Files.newBufferedReader(...)
      - pattern: org.apache.commons.io.IOUtils.toString(...)
      - pattern: org.apache.commons.io.IOUtils.readLines(...)
      - pattern: org.apache.commons.io.IOUtils.toByteArray(...)
      - pattern: (com.fasterxml.jackson.databind.ObjectMapper $OM).readValue(...)
      - pattern: (com.fasterxml.jackson.databind.ObjectMapper $OM).treeToValue(...)
      - pattern: $CLASS.$METHOD(..., (javax.servlet.http.HttpServletRequest $R), ...)
      - pattern: $FUNC(..., (javax.servlet.http.HttpServletRequest $R), ...)
      - patterns:
          - pattern-inside: $FUNC(..., @RequestParam String $X, ...) { ... }
          - focus-metavariable: $X
    pattern-propagators:
      - pattern: $LIST.add($X)
        from: $X
        to: $LIST
      - pattern: $MAP.put(..., $X)
        from: $X
        to: $MAP
      - pattern: $STR.concat($X)
        from: $X
        to: $STR
      - pattern: $STR = String.format(..., $X, ...)
        from: $X
        to: $STR
      - pattern: $STR = String.join(..., $X, ...)
        from: $X
        to: $STR
    pattern-sanitizers:
      - pattern: "org.apache.commons.io.FilenameUtils.getName(...)"
    pattern-sinks:
      - patterns:
          - pattern-inside: |
              $U = new java.net.URI($VAR)
          - pattern-either:
              - pattern-inside: |-
                  new java.io.File($U)
              - pattern-inside: |-
                  java.nio.file.Paths.get($U)
          - pattern: "$VAR"
      - patterns:
          - pattern-inside: |-
              new java.io.RandomAccessFile($INPUT,...)
          - pattern: "$INPUT"
      - pattern: "new java.io.FileReader(...)"
      - pattern: "new javax.activation.FileDataSource(...)"
      - pattern: "new java.io.FileInputStream(...)"
      - pattern: "new java.io.FileOutputStream(...)"
      - pattern: "new java.io.File(...)"
      - pattern: "java.nio.file.Paths.get(...)"
      - pattern: "java.io.File.createTempFile(...)"
      - pattern: "java.io.File.createTempDirectory(...)"
      - pattern: "java.nio.file.Files.createTempFile(...)"
      - pattern: "java.nio.file.Files.createTempDirectory(...)"
      - patterns:
          - pattern: "$SRC.$METHOD(...)"
          - metavariable-pattern:
              metavariable: "$SRC"
              pattern-either:
                - pattern: "getClass()"
                - pattern: "getClass().getClassLoader()"
                - pattern: "(ClassLoader $C)"
                - pattern: "(Class $C)"
                - pattern: "$CLZ.getClassLoader()"
          - metavariable-pattern:
              metavariable: "$METHOD"
              pattern-either:
                - pattern: "getResourceAsStream"
                - pattern: "getResource"
      - patterns:
          - pattern-inside: |-
              new java.io.FileWriter($PATH, ...)
          - pattern: "$PATH"
      - patterns:
          - pattern-inside: |-
              new java.io.FileOutputStream($PATH, ...)
          - pattern: "$PATH"
    message: |
      The application dynamically constructs file or path information. If the path
      information comes from user input, it could be abused to read sensitive files,
      access other users' data, or aid in exploitation to gain further system access.

      User input should never be used in constructing paths or files for interacting
      with the filesystem. This includes filenames supplied by user uploads or downloads.
      If possible, consider hashing user input or replacing it with unique values and
      use `Path.resolve` to resolve and validate the path information
      prior to processing any file functionality.

      Example using `Path.resolve` and not allowing direct user input:
      ```
      // Class to store our user data along with a randomly generated file name
      public static class UserData {
          private String userFileNameUnsafe;
          private String fileName;
          public UserData(String userFileName) {
              this.userFileNameUnsafe = userFileName;
              // Generate a random ID for the filename
              this.fileName = UUID.randomUUID().toString();
          }
          public String getUserFileNameUnsafe() { return userFileNameUnsafe; };
          public String getFileName() { return fileName; };
      }

      public static void main(String[] args) throws Exception {
          // User input, saved only as a reference
          UserData userData = new UserData("..\\test.txt");
          // Restrict all file processing to this directory only
          String base = "/var/app/restricted";
          Path basePath = Paths.get(base);
          // Resolve the full path, but only use our random generated filename
          Path fullPath = basePath.resolve(userData.getFileName());
          // verify the path is contained within our basePath
          if (!fullPath.startsWith(base)) {
              throw new Exception("Invalid path specified!");
          }
          // process / work with file
      }
      ```

      For more information on path traversal issues see OWASP:
      https://owasp.org/www-community/attacks/Path_Traversal
    metadata:
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      cwe: "CWE-22"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Medium"
      technology:
        - "java"
      category: "security"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
    related-packages: ['pkg:maven/org.spotbugs/spotbugs-annotations']
  - id: "java_ldap_rule-AnonymousLDAP"
    languages:
      - "java"
    patterns:
      - pattern-inside: |
          import javax.naming.Context;
          ...
      - pattern: "$ENV.put(Context.SECURITY_AUTHENTICATION, \"none\");"
    message: |
      The application does not provide authentication when communicating an LDAP
      server. It is strongly recommended that the LDAP server be configured with
      authentication and restrict what queries users can execute.

      Example code that authenticates with a remote LDAP server and encodes any
      user-supplied input:
      ```
      // Create a properties to hold the ldap connection details
      Properties props = new Properties();
      // Use the com.sun.jndi.ldap.LdapCtxFactory factory provider
      props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
      // The LDAP server URL
      props.put(Context.PROVIDER_URL, "ldap://ldap.example.org:3889");
      // User details for the connection
      props.put(Context.SECURITY_PRINCIPAL, "cn=admin,dc=example,dc=org");
      // LDAP account password
      String ldapAccountPassword = getAccountPasswordFromSecureStoreOrKMS();
      // Pass in the LDAP password
      props.put(Context.SECURITY_CREDENTIALS, ldapAccountPassword);

      // Create the LDAPContext
      InitialDirContext ldapContext = new InitialDirContext(props);
      // Example using SUBTREE_SCOPE SearchControls
      SearchControls searchControls = new SearchControls();
      searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);

      // Get user input for query
      String userQuery = someUserInput;
      // Use searchArguments to hold the user-supplied input
      Object[] searchArguments = new Object[]{userQuery};
      // Hardcode the BaseDN, use the {0} format specifier to use the searchArguments array value,
      and pass in the search controls.
      // searchArguments automatically encode
      NamingEnumeration answer = ldapContext.search("dc=example,dc=org", "(cn={0})",
      searchArguments, searchControls);
      // Process the response answer
      while (answer.hasMoreElements()) {
        ...
      }
      ```

      For information on enabling authentication, please see your LDAP server's
      documentation.

      For more information on LDAP Injection see OWASP:
      https://cheatsheetseries.owasp.org/cheatsheets/LDAP_Injection_Prevention_Cheat_Sheet.html
    metadata:
      shortDescription: "Missing authentication for critical function (LDAP)"
      category: "security"
      cwe: "CWE-306"
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      security-severity: "High"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "java_password_rule-ConstantDBPassword"
    languages:
      - "java"
    patterns:
      - pattern: "java.sql.DriverManager.getConnection($URI, $USR, \"...\");"
    message: |
      A potential hard-coded password was identified in a database connection string.
      Passwords should not be stored directly in code
      but loaded from secure locations such as a Key Management System (KMS).

      The purpose of using a Key Management System is so access can be audited and keys easily
      rotated
      in the event of a breach. By hardcoding passwords, it will be extremely difficult to determine
      when or if, a key is compromised.

      The recommendation on which KMS to use depends on the environment the application is running
      in:

      - For Google Cloud Platform consider [Cloud Key Management](https://cloud.google.com/kms/docs)
      - For Amazon Web Services consider [AWS Key Management](https://aws.amazon.com/kms/)
      - For on premise or other alternatives to cloud providers, consider [Hashicorp's
      Vault](https://www.vaultproject.io/)
      - For other cloud providers, please see their documentation
    severity: "ERROR"
    metadata:
      shortDescription: "Use of hard-coded password"
      category: "security"
      cwe: "CWE-259"
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      technology:
        - "java"
      security-severity: "Critical"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-jdbc']
      license: MIT (c) GitLab Inc.
  - id: "java_password_rule-EmptyDBPassword"
    languages:
      - "java"
    patterns:
      - pattern: "java.sql.DriverManager.getConnection($URI, $USR, \"\");"
    message: |
      The application does not provide authentication when communicating a database
      server. It is strongly recommended that the database server be configured with
      authentication and restrict what queries users can execute.

      Please see your database server's documentation on how to configure a password.

      Additionally, passwords should not be stored directly in code
      but loaded from secure locations such as a Key Management System (KMS).

      The purpose of using a Key Management System is so access can be audited and keys easily
      rotated
      in the event of a breach. By hardcoding passwords, it will be extremely difficult to determine
      when or if, a key is compromised.

      The recommendation on which KMS to use depends on the environment the application is running
      in:

      - For Google Cloud Platform consider [Cloud Key Management](https://cloud.google.com/kms/docs)
      - For Amazon Web Services consider [AWS Key Management](https://aws.amazon.com/kms/)
      - For on premise or other alternatives to cloud providers, consider [Hashicorp's
      Vault](https://www.vaultproject.io/)
      - For other cloud providers, please see their documentation
    severity: "ERROR"
    metadata:
      shortDescription: "Missing authentication for critical function (database)"
      category: "security"
      cwe: "CWE-306"
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      technology:
        - "java"
      security-severity: "Critical"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-jdbc']
      license: MIT (c) GitLab Inc.
  - id: "java_password_rule-HardcodePassword"
    languages:
      - "java"
    pattern-either:
      - pattern: "new java.security.KeyStore.PasswordProtection(\"...\".toCharArray())"
      - pattern: "java.security.KeyStore.getInstance(...).load(..., \"...\".toCharArray())"
      - pattern: "(java.security.KeyStore $KS).load(..., \"...\".toCharArray())"
      - pattern: "KeyManagerFactory.getInstance(...).init(..., \"...\".toCharArray())"
      - pattern: "(KeyManagerFactory $KMF).init(..., \"...\".toCharArray())"
      - pattern: "PBEKeySpec(\"...\", ...)"
      - pattern: "PasswordAuthentication(\"...\", \"...\")"
      - pattern: "(PasswordCallback $CB).setPassword(\"...\")"
      - pattern: "KerberosKey(...,\"...\",...)"
      - pattern: "java.sql.DriverManager.getConnection(..., \"...\")"
      - pattern: "io.vertx.ext.web.handler.CSRFHandler.create(..., \"...\")"
      - pattern: "$S.setPassword(\"...\")"
    message: |
      A potential hard-coded password was identified in a hard-coded string.
      Passwords should not be stored directly in code
      but loaded from secure locations such as a Key Management System (KMS).

      The purpose of using a Key Management System is so access can be audited and keys easily
      rotated
      in the event of a breach. By hardcoding passwords, it will be extremely difficult to determine
      when or if, a key is compromised.

      The recommendation on which KMS to use depends on the environment the application is running
      in:

      - For Google Cloud Platform consider [Cloud Key Management](https://cloud.google.com/kms/docs)
      - For Amazon Web Services consider [AWS Key Management](https://aws.amazon.com/kms/)
      - For on premise or other alternatives to cloud providers, consider [Hashicorp's
      Vault](https://www.vaultproject.io/)
      - For other cloud providers, please see their documentation
    severity: "ERROR"
    metadata:
      shortDescription: "Use of hard-coded password"
      category: "security"
      cwe: "CWE-259"
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      technology:
        - "java"
      security-severity: "High"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-jdbc']
      license: MIT (c) GitLab Inc.
  - id: "java_perm_rule-DangerousPermissions"
    pattern-either:
      - pattern: |
          $RUNVAR = new RuntimePermission("createClassLoader");
          ...
          (PermissionCollection $PC).add($RUNVAR);
      - pattern: |
          $REFVAR = new ReflectPermission("suppressAccessChecks");
          ...
          (PermissionCollection $PC).add($REFVAR);
      - pattern: "(PermissionCollection $PC).add(new ReflectPermission(\"suppressAccessChecks\"))"
      - pattern: "(PermissionCollection $PC).add(new RuntimePermission(\"createClassLoader\"))"
    languages:
      - "java"
    message: |
      The application was found to permit the `RuntimePermission` of `createClassLoader`,
      `ReflectPermission` of `suppressAccessChecks`, or both.

      By granting the `RuntimePermission` of `createClassLoader`, a compromised application
      could instantiate their own class loaders and load arbitrary classes.

      By granting the `ReflectPermission` of `suppressAccessChecks` an application will no longer
      check Java language access checks on fields and methods of a class. This will effectively
      grant access to protected and private members.

      For more information on `RuntimePermission` see:
      https://docs.oracle.com/javase/8/docs/api/java/lang/RuntimePermission.html

      For more information on `ReflectPermission` see:
      https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/ReflectPermission.html
    metadata:
      shortDescription: "Incorrect permission assignment for critical resource"
      category: "security"
      cwe: "CWE-732"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      confidence: "HIGH"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.springframework.security/spring-security-core']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "java_perm_rule-OverlyPermissiveFilePermissionInline"
    languages:
      - "java"
    patterns:
      - pattern-either:
          - pattern: "java.nio.file.Files.setPosixFilePermissions(..., java.nio.file.attribute.PosixFilePermissions.fromString(\"\
              $PERM_STRING\"));"
          - pattern: |
              $PERMISSIONS = java.nio.file.attribute.PosixFilePermissions.fromString("$PERM_STRING");
              ...
              java.nio.file.Files.setPosixFilePermissions(..., $PERMISSIONS);
      - metavariable-regex:
          metavariable: "$PERM_STRING"
          regex: "[rwx-]{6}[rwx]{1,}"
    message: |
      The application was found setting file permissions to overly permissive values. Consider
      using the following values if the application user is the only process to access
      the file:

      - `r--` - read only access to the file
      - `w--` - write only access to the file
      - `rw-` - read/write access to the file

      Example setting read/write permissions for only the owner of a `Path`:
      ```
      // Get a reference to the path
      Path path = Paths.get("/tmp/somefile");
      // Create a PosixFilePermission set from java.nio.file.attribute
      Set<PosixFilePermission> permissions =
      java.nio.file.attribute.PosixFilePermissions.fromString("rw-------");
      // Set the permissions
      java.nio.file.Files.setPosixFilePermissions(path, permissions);
      ```

      For all other values please see:
      https://en.wikipedia.org/wiki/File-system_permissions#Symbolic_notation
    metadata:
      shortDescription: "Incorrect permission assignment for critical resource"
      cwe: "CWE-732"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      category: "security"
      confidence: "HIGH"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.commons/commons-io']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: java_rule_sql_injection
    languages:
      - java
    severity: ERROR
    message: |
      A SQL Injection vulnerability was detected.  
      A potentially user controlled input was unsafely used in a SQL query using + or .format(ted) or quoted placeholder: '%s'.
      The query was executed using a method or function called something like '.*(?i)(update|query|execute|sql|select|insert|delete|create).*'.
      Never use + or .formatted or quoted placeholder ('%s') to inject a variable in a query!!! Especially if it is user-controlled. 
      When possible, use ORMs that handle inputs separately from the data, like JPA’s Query API for example. 
      Instead, use named (:input) or positional (?1) dynamic parameters: 
        ```jdbcTemplate.update("INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)", id, "Bill", "Gates", "USA");```
      It is safer to use known and tested/validated frameworks as much as possible to avoid building vulnerabilities. Consider user JDBC, Spring, Hibernete... which can be easier to process sql statements safely. 
      Ensure to always verify user controlled input before using it, and ensure to always use parameterized queries.        
    metadata:
      cwe: CWE-89
      owasp:
        - A1:2017-Injection
        - A03:2021-Injection
      category: security
      shortDescription: Unsafe SQL query with non parameterized parameters in Java
      security-severity: High
      confidence: High
      license: License Not Found
    pattern-sources:
      - patterns:
        # source of input: function arg or requested parameter 
          - pattern-either:
          # get user input with java.util.Scanner
              - patterns:
                  - pattern-inside: |
                      $SCAN = new Scanner(...);
                      ...
                  - patterns:
                      - pattern-either:
                          - pattern-inside: |
                              $PARAM = <... $SCAN.$METHOD() ...>;
                              ...
                          - pattern: <... $SCAN.$METHOD() ...>
                      - metavariable-regex:
                          metavariable: $METHOD
                          regex: ^next
          # get user input with javax.swing.JOptionPane
              - pattern-inside: |
                  $PARAM = <... JOptionPane. ... ...> ;
                  ...
              - pattern: |
                  <... JOptionPane. ... ...> 
          # get user input with request
              - patterns:
                  - pattern-either:
                      - pattern-inside: |
                          $PARAM = <... $FORM.$GET(...) ...>;
                          ...
                      - pattern: |
                          <... $FORM.$GET(...) ...>
                      - pattern: |
                          <... $GET(...) ...>
                      - pattern-inside: |
                          $PARAM = <... $GET(...) ...>;
                          ...
                  - metavariable-regex:
                      metavariable: $GET
                      regex: ^get
          # get user input with read...() method using System.in (DataInputStream, BufferedReader, InputStreamReader)
              - patterns:
                  - pattern-either:
                      - pattern-inside: |
                          $DIS = <... System.in ...>;
                          ...
                      - pattern-inside: |
                          $DIS = <... System.console() ...>;
                          ...
                  - patterns:
                      - pattern-either:
                          - pattern: <... $DIS.$METHOD() ...>
                          - pattern-inside: |
                              $PARAM = <... $DIS.$METHOD() ...>;
                              ...
                      - metavariable-regex:
                          metavariable: $METHOD
                          regex: ^read
          # input as a function arg 
              - pattern-inside: |
                  $F(...,  $PARAM, ...){
                    ...
                  }
          # input as a function arg in another method of the same class 
              - pattern-inside: |
                  class $C {
                    ...
                  $F(..., $INPUT, ...){
                    ...
                    this.$PARAM = $INPUT;
                    ...
                    }
                    ...
                    }
         # unsafe queries: user controlled input in a sql request 
          - patterns:
              - pattern-inside: |
                  <... "=~/(?i)^\s*(SELECT|INSERT|UPDATE|DELETE|CREATE|FROM|DROP|ALTER|TRUNCATE|GRANT|REVOKE|SAVEPOINT|ROLLBACK)\s/" ...>
              - pattern-either:
            # "..." + param
                  - pattern: <... ... + $PARAM ...>
                  - pattern: <... ... + $PARAM. ... ...>
            # quotes around the %s placeholders
                  - pattern: <... "=~/.*\'%[a-z]\'.*/" ...>
            # concatenation
                  - pattern: <... "=~/.*%[a-z].*/".formatted(..., $PARAM, ...) ...>
                  - pattern: <... "=~/.*%[a-z].*/".formatted(..., $PARAM. ..., ...) ...>
            # MessageFormat or String or any .format
                  - pattern: <... $M.format(..., $PARAM ) ...>
                  - pattern: <... $M.format(..., $PARAM. ... ) ...>
          - pattern: $PARAM

    pattern-sinks:
      - pattern-either:
          # generic sinks
          - patterns:
              - pattern: |
                  $EXEC
              - metavariable-regex:
                  metavariable: $EXEC
                  regex: (\n|.)*(?i)[a-z0-9\-\_]*(update|query|exec|sql|select|delete|create|commit)[a-z0-9\-\_]*\(
       # Spring JDBC
          - pattern: new PreparedStatementCreatorFactory(...);

    mode: taint


  - id: "java_script_rule-ScriptInjection"
    languages:
      - "java"
    mode: "taint"
    pattern-sinks:
      - patterns:
          - pattern: "(javax.script.ScriptEngine $ENGINE).eval($ARG, ...);"
          - pattern-not: "(javax.script.ScriptEngine $ENGINE).eval(\"...\");"
          - pattern-not: "(javax.script.ScriptEngine $ENGINE).eval(\"...\", (javax.script.Bindings $BINDING));"
      - patterns:
          - pattern-either:
              - pattern: "(javax.script.Invocable $INVC).invokeFunction(..., $ARG)"
              - pattern: "(javax.script.Invocable $INVC).invokeMethod(..., $ARG)"
    pattern-sources:
      - patterns:
          - pattern-inside: |-
              $FUNC(..., $VAR, ...) { ... }
          - pattern: "$VAR"
    message: |
      The application executes an argument using a `ScriptEngine`'s `eval` method. This
      may allow for direct OS commands to be executed as it's possible to pass in strings
      such as `java.lang.Runtime.getRuntime().exec('/bin/sh ...');`.

      Never pass user-supplied input directly to the `eval` function. If possible hardcode all
      JavasScript code or use a lookup table to resolve user input to known values. If none of these
      techniques are possible, use `javax.script.Bindings` to pass input to the script engine.

      Example using `Binding` to safely pass in string values:
      ```
      // Get ECMAScript engine
      ScriptEngine engine = new ScriptEngineManager().getEngineByName("ECMAScript");

      // User input, consisting of first and last name
      String userFirstName = "John";
      String userLastName = "Snow";

      // Create bindings to pass into our script, forcing the values to be String.
      Bindings bindings = engine.createBindings();
      bindings.put("fname", new String(userFirstName));
      bindings.put("lname", new String(userLastName));

      // Example script that concatenates a greeting with the user-supplied input first/last name
      String script = "var greeting='Hello ';" +
      // fname and lname variables will be resolved by our bindings defined above
      "greeting += fname + ' ' + lname;" +
      // prints greeting
      "greeting";

      try {
        // Execute the script, passing in the bindings
        Object bindingsResult = engine.eval(script, bindings);
        // Work with result
        // ...
      } catch (ScriptException e) {
        // Handle exception
        e.printStackTrace();
      }
      ```
    severity: "ERROR"
    metadata:
      shortDescription: "Improper control of generation of code ('Code Injection')"
      category: "security"
      cwe: "CWE-94"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      related-packages: ['pkg:maven/javax.script/javax.script']
      license: MIT (c) GitLab Inc.
  - id: "java_script_rule-SpringSpelExpressionParser"
    languages:
      - "java"
    patterns:
      - pattern: "($PARSER $P).$METHOD($ARG);"
      - pattern-not: "($PARSER $P).$METHOD(\"...\");"
      - metavariable-pattern:
          metavariable: "$PARSER"
          pattern-either:
            - pattern: "org.springframework.expression.spel.standard.SpelExpressionParser"
            - pattern: "org.springframework.expression.ExpressionParser"
      - metavariable-regex:
          metavariable: "$METHOD"
          regex: "(parseExpression|parseRaw)"
    message: |
      The application was found calling SpringFramework's `SpelExpressionParser.parseExpression`.
      Calling this method directly with user-supplied input may allow an adversary to
      execute arbitrary Java code including OS system commands.

      Never call `parseExpression` or `parseRaw` directly with user-supplied input. Consider
      alternate
      methods such as a lookup table to take user input and resolve hardcoded values.

      Later versions of SpringFramework introduced a `SimpleEvaluationContext` which can be
      used to access bound data when calling the `getValue` result of `parseExpression`. This
      `SimpleEvaluationContext` has a reduced set of functionality and can restrict data binding
      to read-only or read-write contexts. An adversary could still access public properties
      or fields on custom types that have been provided to the evaluation context. Use with caution.

      Example using `SimpleEvaluationContext` with a read-write data binding context:
      ```
      @RequestMapping(value="/spel", method=RequestMethod.POST)
      public String spel(@Validated User user, Model model)  {
        // Create the Expression Parser
        SpelExpressionParser parser = new SpelExpressionParser();
        // Parse the expression
        Expression parsedExpression = parser.parseExpression(model.getPossiblyUnsafeData());
        // Create the read-write data binding context
        SimpleEvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
        // Execute the expression, passing in the read-write context
        Object result = parsedExpression.getValue(context);
        // work with the result
        // ...
        return "user";
      }
      ```

      For more information on SimpleEvaluationContext see:
      https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/expression/spel/support/SimpleEvaluationContext.html
    severity: "ERROR"
    metadata:
      shortDescription: "Improper neutralization of special elements used in an expression language statement ('Expression
        Language Injection')"
      category: "security"
      cwe: "CWE-917"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      related-packages: ['pkg:maven/org.springframework/spring-expression']
      license: MIT (c) GitLab Inc.
  - id: "java_smtp_rule-InsecureSmtp"
    languages:
      - "java"
    patterns:
      - pattern-either:
          - pattern-inside: |
              $E = new org.apache.commons.mail.SimpleEmail(...);
              ...
          - pattern-inside: |
              $E = new org.apache.commons.mail.Email(...);
              ...
          - pattern-inside: |
              $E = new org.apache.commons.mail.MultiPartEmail(...);
              ...
          - pattern-inside: |
              $E = new org.apache.commons.mail.HtmlEmail(...);
              ...
          - pattern-inside: |
              $E = new org.apache.commons.mail.ImageHtmlEmail(...);
              ...
      - pattern-not: |
          $E.setSSLOnConnect(true);
          ...
          $E.setSSLCheckServerIdentity(true);
    message: |
      The Apache commons mail client by default does not enable TLS server identity.
      This allows for an adversary who is in between the application and the target host to intercept
      potentially sensitive information or transmit malicious data.

      Enable checking server identity by calling `Email.setSSLCheckServerIdentity(true)`

      Example email client that enables TLS and server identity:
      ```
      // Create an email client
      Email email = new SimpleEmail();
      // Configure the email hostname
      email.setHostName("smtp.mail.example.com");
      // Set the port
      email.setSmtpPort(465);
      // Securely retrieve username and password values
      String username = getUserNameFromKMSorSecretStore();
      String password = getPasswordFromKMSorSecretStore();
      // Configure the Authenticator
      DefaultAuthenticator auth = new DefaultAuthenticator(username, password);
      // Set the authenticator
      email.setAuthenticator(auth);
      // Ensure we use SSL on connect
      email.setSSLOnConnect(true);
      // Ensure we validate server identity
      email.setSSLCheckServerIdentity(true);
      // configure the rest of the email
      email.setFrom("x@example.com");
      email.setSubject("TestMail");
      email.setMsg("This is a test mail ... :-)");
      email.addTo("y@example.com");
      email.send();
      ```
    metadata:
      shortDescription: "Improper validation of certificate with host mismatch"
      category: "security"
      cwe: "CWE-297"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.commons/commons-email']
      license: MIT (c) GitLab Inc.
    severity: "ERROR"
  - id: "java_smtp_rule-SmtpClient"
    languages:
      - "java"
    message: |
      The application was found calling `MimeMessage` methods without encoding
      new line characters. Much like HTTP, Simple Mail Transfer Protocol (SMTP) is a
      text based protocol that uses headers to convey additional directives for how
      email messages should be treated. An adversary could potentially cause email
      messages to be sent to unintended recipients by abusing the CC or BCC headers
      if they were able to inject them.

      To mitigate this issue, `\r\n` (CRLF) character sequences must be escaped
      or encoded prior to being used in any of the `MimeMessage` methods.

      Example that escapes values that come from user input with
      [Apache Commons Text](https://commons.apache.org/proper/commons-text/):
      ```
      // Create a MimeMessage with a javax.mail.Session
      Message message = new MimeMessage(session);
      // Set the from address
      message.setFrom(new InternetAddress("source@example.com"));
      // Set the to address
      message.setRecipients(Message.RecipientType.TO,new InternetAddress[] {new
      InternetAddress("destination@example.com")});
      // Example user input
      String subject = "potentially malicious data";
      String headerValue = "potentially malicious data";
      // Use Apache Commons Text StringEscapeUtils.escapeJava to encode \r\n to \\r\\n.
      message.setSubject(StringEscapeUtils.escapeJava(subject));
      // Use Apache Commons Text StringEscapeUtils.escapeJava to encode \r\n to \\r\\n.
      message.addHeader("HeaderName", StringEscapeUtils.escapeJava(header));
      // Use Apache Commons Text StringEscapeUtils.escapeJava to encode \r\n to \\r\\n.
      message.setDescription(StringEscapeUtils.escapeJava("some description"));
      // Use Apache Commons Text StringEscapeUtils.escapeJava to encode \r\n to \\r\\n.
      message.setDisposition(StringEscapeUtils.escapeJava("some disposition"));
      // Set the mail body text
      message.setText("Some email content.");
      // Send the message
      ```
    patterns:
      - pattern-inside: |
          $M = new MimeMessage(...);
          ...
      - pattern-either:
          - patterns:
              - pattern-either:
                  - pattern: "$M.setSubject($VAR)"
                  - pattern: "$M.addHeader($ARG, $VAR)"
                  - pattern: "$M.addHeader($VAR, $ARG)"
                  - pattern: "$M.setDescription($VAR)"
                  - pattern: "$M.setDisposition($VAR)"
              - metavariable-regex:
                  metavariable: $VAR
                  regex: "^[a-zA-Z_$][a-zA-Z0-9_$]*$"
          - patterns:
              - pattern-either:
                  - pattern: "$M.setSubject($OBJ.$GETTER(...))"
                  - pattern: "$M.setSubject($OBJ.$GETTER(...) + ...)"
                  - pattern: "$M.setSubject(... + $OBJ.$GETTER(...))"
                  - pattern: "$M.setSubject(... + $OBJ.$GETTER(...) + ...)"
                  - pattern: "$M.addHeader($ARG, $OBJ.$GETTER(...))"
                  - pattern: "$M.addHeader($ARG, $OBJ.$GETTER(...) + ...)"
                  - pattern: "$M.addHeader($ARG, ... + $OBJ.$GETTER(...))"
                  - pattern: "$M.addHeader($ARG, ... + $OBJ.$GETTER(...) + ...)"
                  - pattern: "$M.addHeader($OBJ.$GETTER(...), $ARG)"
                  - pattern: "$M.addHeader($OBJ.$GETTER(...) + ..., $ARG)"
                  - pattern: "$M.addHeader(... + $OBJ.$GETTER(...), $ARG)"
                  - pattern: "$M.addHeader(... + $OBJ.$GETTER(...) + ..., $ARG)"
                  - pattern: "$M.setDescription($OBJ.$GETTER(...))"
                  - pattern: "$M.setDisposition($OBJ.$GETTER(...) + ...)"
                  - pattern: "$M.setDisposition(... + $OBJ.$GETTER(...))"
                  - pattern: "$M.setDisposition(... + $OBJ.$GETTER(...) + ...)"
              - metavariable-regex:
                  metavariable: $GETTER
                  regex: ^get
    metadata:
      shortDescription: "Improper neutralization of special elements used in a command"
      category: "security"
      cwe: "CWE-77"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "Low"
      related-packages: ['pkg:maven/org.apache.commons/commons-email']
      license: MIT (c) GitLab Inc.
    severity: "ERROR"
  - id: "java_ssrf_rule-SSRF"
    languages:
      - "java"
    message: |
      Server-Side-Request-Forgery (SSRF) exploits backend systems that initiate requests to third
      parties.
      If user input is used in constructing or sending these requests, an attacker could supply
      malicious
      data to force the request to other systems or modify request data to cause unwanted actions.

      Ensure user input is not used directly in constructing URLs or URIs when initiating requests
      to third party
      systems from back end systems. Care must also be taken when constructing payloads using user
      input. Where
      possible restrict to known URIs or payloads. Consider using a server-side map where keys are
      used to return
      URLs such as `https://site/goto?key=1` where `{key: 1, url: 'http://some.url/', key: 2, url:
      'http://...'}`.

      If you must use user-supplied input for requesting URLs, it is strongly recommended that the
      HTTP client
      chosen allows you to customize and block certain IP ranges at the network level. By blocking
      RFC 1918
      addresses or other network address ranges, you can limit the severity of a successful SSRF
      attack. Care must
      also be taken to block certain protocol or address formatting such as IPv6.

      If you cannot block address ranges at the client level, you may want to run the HTTP client
      as a protected
      user, or in a protected network where you can apply IP Table or firewall rules to block access
      to dangerous
      addresses. Finally, if none of the above protections are available, you could also run a
      custom HTTP proxy
      and force all requests through it to handle blocking dangerous addresses.

      Example using a map to look up a key to be used in a HTTP request:
      ```
      HashMap<String, String> lookupTable = new HashMap<>();
      lookupTable.put("key1", "https://example.com/");
      lookupTable.put("key2", "https://safeurl.com/");
      String userInput = request.getParameter("key");

      // Create a CloseableHttpClient, ideally any requests issued should be done
      // out-of-band from the servlet request itself (such as using a separate thread/scheduler
      system)
      try (final CloseableHttpClient httpClient = HttpClients.createDefault()) {
          // Lookup the value from our user input from our lookupTable
          String value = lookupTable.getOrDefault(userInput, "https://example.com/");
          // Construct the url, with the hardcoded url and only pass in the value from the
      lookupTable,
          // not direct user input
          final HttpGet httpget = new HttpGet(value);
          // Execute the request
          CloseableHttpResponse clientResponse = httpClient.execute(httpget);
          // Read the response
          byte[] responseData = clientResponse.getEntity().getContent().readAllBytes();
          // Handle the response
          // ...
      }
      ```

      If using a map is not possible, the user-supplied input must be encoded prior to use, and
      never allow full
      URLs:
      ```
      // Get user input
      String userInput = request.getParameter("key");
      // Encode the string using java.net.URLEncoder with the UTF-8 character set
      String encodedString = java.net.URLEncoder.encode(userInput, StandardCharsets.UTF_8);
      // Create a CloseableHttpClient, ideally any requests issued should be done
      // out-of-band from the servlet request itself (such as using a separate thread/scheduler
      system)
      try (final CloseableHttpClient httpClient = HttpClients.createDefault()) {
        // Construct the url, with the hardcoded url and only pass in the encoded value, never a
      full URL
        final HttpGet httpget = new HttpGet("https://example.com/getId?key="+encodedString);
        // Execute the request
        CloseableHttpResponse clientResponse = httpClient.execute(httpget);
        // Read the response
        byte[] responseData = clientResponse.getEntity().getContent().readAllBytes();
        // handle the response
      }
      ```

      For more information on SSRF see OWASP:
      https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
    pattern-either:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  import java.net.*;
                  ...
              - pattern-inside: |
                  import java.net.URL;
                  ...
              - pattern-inside: |
                  import java.net.URI;
                  ...
          - pattern: "new $TYPE(...). ... .$FUNC"
          - pattern-not: "new $TYPE(\"...\"). ... .$FUNC"
          - metavariable-pattern:
              metavariable: "$FUNC"
              pattern-either:
                - pattern: "connect"
                - pattern: "GetContent"
                - pattern: "openConnection"
                - pattern: "openStream"
                - pattern: "getContent"
          - metavariable-pattern:
              metavariable: "$TYPE"
              pattern-either:
                - pattern: "URL"
                - pattern: "java.net.URL"
                - pattern: "URI"
                - pattern: "java.net.URI"
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  import java.net.*;
                  ...
              - pattern-inside: |
                  import java.net.InetSocketAddress;
                  ...
          - pattern: |
              new InetSocketAddress(..., $PORT)
          - pattern-not: |
              new InetSocketAddress("...", $PORT)
    metadata:
      shortDescription: "Server-Side Request Forgery (SSRF)"
      category: "security"
      cwe: "CWE-918"
      owasp:
        - "A1:2017-Injection"
        - "A10:2021-Server-Side Request Forgery"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    severity: "ERROR"
  - id: "java_strings_rule-BadHexConversion"
    languages:
      - "java"
    message: |
      The application is using `Integer.toHexString` on a digest array buffer which
      may lead to an incorrect version of values.

      Consider using the `java.util.HexFormat` object introduced in Java 17. For older Java applications
      consider using the `javax.xml.bind.DatatypeConverter`.

      Example using `HexFormat` to create a human-readable string:
      ```
      // Create a MessageDigest using the SHA-384 algorithm
      MessageDigest sha384Digest = MessageDigest.getInstance("SHA-384");
      // Call update with your data
      sha384Digest.update("some input".getBytes(StandardCharsets.UTF_8));
      // Only call digest once all data has been fed into the update sha384digest instance
      byte[] output = sha384Digest.digest();
      // Create a JDK 17 HexFormat object
      HexFormat hex = HexFormat.of();
      // Use formatHex on the byte array to create a string (note that alphabet characters are
      lowercase)
      String hexString = hex.formatHex(output);
      ```

      For more information on DatatypeConverter see:
      https://docs.oracle.com/javase/9/docs/api/javax/xml/bind/DatatypeConverter.html#printHexBinary-byte:A-
    patterns:
      - pattern-inside: |
          $B_ARR = (java.security.MessageDigest $MD).digest(...);
          ...
      - pattern-either:
          - pattern: |
              for(...) {
                ...
                $B = $B_ARR[...];
                ...
                Integer.toHexString($B);
              }
          - pattern: |
              for(...) {
                ...
                Integer.toHexString($B_ARR[...]);
              }
          - pattern: |
              for(byte $B :$B_ARR) {
                ...
                Integer.toHexString($B);
              }
          - pattern: |
              while(...) {
                ...
                Integer.toHexString($B_ARR[...])
              }
          - pattern: |
              do {
                ...
                Integer.toHexString($B_ARR[...])
              } while(...)
          - pattern: |
              while(...) {
                ...
                $B = $B_ARR[...];
                ...
                Integer.toHexString($B);
              }
          - pattern: |
              do {
                ...
                $B = $B_ARR[...];
                ...
                Integer.toHexString($B);
              } while(...)
    metadata:
      shortDescription: "Incorrect type conversion or cast"
      cwe: "CWE-704"
      category: "security"
      confidence: "HIGH"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "java_strings_rule-FormatStringManipulation"
    languages:
      - "java"
    patterns:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  String $INPUT = (HttpServletRequest $REQ).getParameter(...);
                  ...
              - pattern-inside: |
                  String $FORMAT_STR = ... + $INPUT;
                  ...
          - patterns:
              - pattern-inside: |
                  String $INPUT = (HttpServletRequest $REQ).getParameter(...);
                  ...
              - pattern-inside: |
                  String $FORMAT_STR = ... + $INPUT + ...;
                  ...
          - pattern-inside: |
              String $FORMAT_STR = ... + (HttpServletRequest $REQ).getParameter(...) + ...;
              ...
          - pattern-inside: |
              String $FORMAT_STR = ... + (HttpServletRequest $REQ).getParameter(...);
              ...
      - pattern-either:
          - pattern: "String.format($FORMAT_STR, ...);"
          - pattern: "String.format(java.util.Locale.$LOCALE, $FORMAT_STR, ...);"
          - pattern: "(java.util.Formatter $F).format($FORMAT_STR, ...);"
          - pattern: "(java.util.Formatter $F).format(java.util.Locale.$LOCALE, $FORMAT_STR, ...);"
          - pattern: "(java.io.PrintStream $F).printf($FORMAT_STR, ...);"
          - pattern: "(java.io.PrintStream $F).printf(java.util.Locale.$LOCALE, $FORMAT_STR, ...);"
          - pattern: "(java.io.PrintStream $F).format($FORMAT_STR, ...);"
          - pattern: "(java.io.PrintStream $F).format(java.util.Locale.$LOCALE, $FORMAT_STR, ...);"
          - pattern: "System.out.printf($FORMAT_STR, ...);"
          - pattern: "System.out.printf(java.util.Locale.$LOCALE, $FORMAT_STR, ...);"
          - pattern: "System.out.format($FORMAT_STR, ...);"
          - pattern: "System.out.format(java.util.Locale.$LOCALE, $FORMAT_STR, ...);"
    message: |
      The application allows user input to control format string parameters. By passing invalid
      format
      string specifiers an adversary could cause the application to throw exceptions or possibly
      leak
      internal information depending on application logic.

      Never allow user-supplied input to be used to create a format string. Replace all format
      string
      arguments with hardcoded format strings containing the necessary specifiers.

      Example of using `String.format` safely:
      ```
      // Get untrusted user input
      String userInput = request.getParameter("someInput");
      // Ensure that user input is not included in the first argument to String.format
      String.format("Hardcoded string expecting a string: %s", userInput);
      // ...
      ```
    metadata:
      shortDescription: "Use of externally-controlled format string"
      cwe: "CWE-134"
      category: "security"
      confidence: "HIGH"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    severity: "ERROR"
  - id: "java_strings_rule-ModifyAfterValidation"
    languages:
      - "java"
    patterns:
      - pattern: |
          (java.util.regex.Pattern $Y).matcher($VAR);
          ...
          $VAR.$METHOD(...);
      - metavariable-regex:
          metavariable: "$METHOD"
          regex: "(replace|replaceAll|replaceFirst|concat)"
    message: |+
      The application was found matching a variable during a regular expression
      pattern match, and then calling string modification functions after validation has occurred.
      This is usually indicative of a poor input validation strategy as an adversary may attempt to
      exploit the removal of characters.

      For example a common mistake in attempting to remove path characters to protect against path
      traversal is to match '../' and then remove any matches. However, if an adversary were to
      include in their input: '....//' then the `replace`  method would replace the first `../` but
      cause the leading `..` and trailing `/` to join into the final string of `../`, effectively
      bypassing the check.

      To remediate this issue always perform string modifications before any validation of a string.
      It is strongly recommended that strings be encoded instead of replaced or removed prior to
      validation.


      Example replaces `..` before validation. Do note this is still not a recommended method for
      protecting against directory traversal, always use randomly generated IDs or filenames instead:
      ```
      // This is ONLY for demonstration purpose, never use untrusted input
      // in paths, always use randomly generated filenames or IDs.
      String input = "test../....//dir";
      // Use replaceAll _not_ replace
      input = input.replaceAll("\\.\\.", "");
      // Input would be test///dir at this point
      // Create a pattern to match on
      Pattern pattern = Pattern.compile("\\.\\.");
      // Create a matcher
      Matcher match = pattern.matcher(input);
      // Call find to see if .. is still in our string
      if (match.find()) {
          throw new Exception(".. detected");
      }
      // Use the input (but do not modify the string)
      System.out.println(input + " safe");
      ```

      For more information see Carnegie Mellon University's Secure Coding Guide:
      https://wiki.sei.cmu.edu/confluence/display/java/IDS11-J.+Perform+any+string+modifications+before+validation

    metadata:
      shortDescription: "Collapse of data into unsafe value"
      category: "security"
      cwe: "CWE-182"
      confidence: "HIGH"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "java_strings_rule-NormalizeAfterValidation"
    patterns:
      - pattern: |
          $Y = java.util.regex.Pattern.compile("[<>]");
          ...
          $Y.matcher($VAR);
          ...
          java.text.Normalizer.normalize($VAR, ...);
    languages:
      - "java"
    message: |
      The application was found matching a variable during a regular expression
      pattern match, and then calling a Unicode normalize function after validation has occurred.
      This is usually indicative of a poor input validation strategy as an adversary may attempt to
      exploit the normalization process.

      To remediate this issue, always perform Unicode normalization before any validation of a
      string.

      Example of normalizing a string before validation:
      ```
      // User input possibly containing malicious unicode
      String userInput = "\uFE64" + "tag" + "\uFE65";
      // Normalize the input
      userInput = Normalizer.normalize(userInput, Normalizer.Form.NFKC);
      // Compile our regex pattern looking for < or > characters
      Pattern pattern = Pattern.compile("[<>]");
      // Create a matcher from the userInput
      Matcher matcher = pattern.matcher(userInput);
      // See if the matcher matches
      if (matcher.find()) {
          // It did so throw an error
          throw new Exception("found banned characters in input");
      }
      ```

      For more information see Carnegie Mellon University's Secure Coding Guide:
      https://wiki.sei.cmu.edu/confluence/display/java/IDS01-J.+Normalize+strings+before+validating+them
    metadata:
      shortDescription: "Incorrect behavior order: validate before canonicalize"
      category: "security"
      cwe: "CWE-180"
      confidence: "HIGH"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "java_templateinjection_rule-TemplateInjection"
    languages:
      - "java"
    message: |
      The application may allow control over a template string. Providing user input directly in the
      template by
      dynamically creating template strings may allow an adversary to execute arbitrary Java code,
      including
      OS system commands.

      For Velocity, never call `evaluate` with user-supplied input in the template string. Use a
      `VelocityContext`
      object instead to data-bind user-supplied information as it will be treated as an underlying
      data type and not
      template code.

      Example using Apache Velocity's `VelocityContext` and escape tools to pass in user-supplied
      data to a template:
      ```
      // Create a tool manager
      ToolManager manager = new ToolManager(true);
      // Create a context from the tool manager
      Context context = manager.createContext();
      // For demonstration purposes, alternatively configure from a properties file
      context.put("esc", new EscapeTool());
      // For demonstration purposes, create an output buffer
      StringWriter stringWriter = new StringWriter();
      // Get userInput
      String userInput = "potentially malicious data";
      // Use the context to pass in the userInput value
      context.put("userInput", userInput);
      // Pass in the context, the output buffer, a logtag (demo), and the template with userInput
      // making sure to escape it if in the context of HTML.
      Velocity.evaluate(context, stringWriter, "demo", "Hello $esc.html($userInput)");
      // Work with the output buffer
      // ...
      ```

      For other templating engines, please see your framework's documentation.
    pattern-either:
      - patterns:
          - pattern: "org.apache.velocity.app.Velocity.evaluate(..., $VAR)"
          - pattern-not: "org.apache.velocity.app.Velocity.evaluate(..., \"...\")"
      - patterns:
          - pattern-not-inside: |
              $C = (freemarker.template.Configuration $CFG).getTemplate("...");
              ...
          - pattern-inside: |
              $C = (freemarker.template.Configuration $CFG).getTemplate($IN);
              ...
          - pattern: "$C.process(...)"
      - patterns:
          - pattern-inside: |
              import com.mitchellbosecke.pebble.PebbleEngine;
              ...
          - pattern-inside: |
              $C = $T.getTemplate($IN);
              ...
          - pattern-not-inside: |
              $C = $T.getTemplate("...");
              ...
          - pattern: "$C.evaluate(...)"
    metadata:
      shortDescription: "Improper control of generation of code ('Code Injection')"
      category: "security"
      cwe: "CWE-94"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      related-packages: ['pkg:maven/org.apache.velocity/velocity']
      license: MIT (c) GitLab Inc.
    severity: "ERROR"
  - id: "java_unsafe_rule-ExternalConfigControl"
    languages:
      - "java"
    patterns:
      - pattern: |
          $TAINTED = (HttpServletRequest $REQ).getParameter(...);
          ...
          (java.sql.Connection $CONN).setCatalog($TAINTED);
    message: |
      The application was found using user-supplied input in a `java.sql.Connection`'s
      `setCatalog` call. This could allow an adversary to supply a different database for the
      lifetime of the connection. Allowing external control of system settings can disrupt service
      or cause an application to behave in unexpected, and potentially malicious ways. Most likely
      this would only cause an error by providing a nonexistent catalog name.

      It is recommended to not use user-supplied input when selecting the database for an
      applications
      database connection.
    severity: "WARNING"
    metadata:
      shortDescription: "External control of system or configuration setting"
      category: "security"
      cwe: "CWE-15"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "java"
      security-severity: "Low"
      related-packages: ['pkg:maven/org.springframework/spring-jdbc']
      license: MIT (c) GitLab Inc.
  - id: "java_xml_rule-SAMLIgnoreComments"
    languages:
      - "java"
    message: |
      SAML parses attestations as an XML document. By processing XML comments, comment
      fields can end up modifying the interpretation of input fields. This could allow
      an adversary to insert an XML comment to break up the attestation's username
      or other fields, allowing an attacker to bypass authorization or authentication checks.

      To remediate this issue, when using `org.opensaml.xml.parse.BasicParserPool` ensure
      `setIgnoreComments(false)` is not called.

      The default value of `ignoreComments` is true, which is safe. 

      Ref:
      - https://javadoc.io/doc/org.opensaml/xmltooling/latest/org/opensaml/xml/parse/BasicParserPool.html#ignoreComments

      For more information on how this issue can be exploited see:
      https://developer.okta.com/blog/2018/02/27/a-breakdown-of-the-new-saml-authentication-bypass-vulnerability

      For more information on SAML security see OWASP:
      https://cheatsheetseries.owasp.org/cheatsheets/SAML_Security_Cheat_Sheet.html
    metadata:
      shortDescription: "Weak authentication"
      cwe: "CWE-1390"
      category: "security"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.opensaml/xmltooling']
      license: MIT (c) GitLab Inc.
    pattern: "(org.opensaml.xml.parse.BasicParserPool $POOL).setIgnoreComments(false);"
    severity: "WARNING"
  - id: "java_xml_rule-XmlDecoder"
    languages:
      - "java"
    message: |
      Deserialization attacks exploit the process of reading serialized data and turning it back
      into an object. By constructing malicious objects and serializing them, an adversary may
      attempt to:

      - Inject code that is executed upon object construction, which occurs during the
      deserialization process.
      - Exploit mass assignment by including fields that are not normally a part of the serialized
      data but are read in during deserialization.

      Consider safer alternatives such as serializing data in the JSON format. Ensure any format
      chosen allows
      the application to specify exactly which object types are allowed to be deserialized.
      Additionally, when
      deserializing, never deserialize to base object types like `Object` and only cast to the exact
      object
      type that is expected.

      To protect against mass assignment, only allow deserialization of the specific fields that are
      required. If this is not easily done, consider creating an intermediary type that
      can be serialized with only the necessary fields exposed.

      Do note that `XMLEncoder` and `XMLDecoder` are not recommended. If the application must
      use this serialization method, use a custom ClassLoader to prevent loading of arbitrary
      classes:
      ```
      XMLDecoder decoder = new XMLDecoder(inputStream, null, null, new ClassLoader() {
          @Override
          protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
              if (!name.equals(NameOfBeanHere.class.getName()) &&
      !name.equals(XMLDecoder.class.getName())) {
                  throw new RuntimeException("Unauthorized deserialization attempt: " + name);
              }

              return super.loadClass(name, resolve);
          }
      });
      ```

      For more information on XML security see OWASP's guide:
      https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#java

      For more details on deserialization attacks in general, see OWASP's guide:
      https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html

      It should be noted that [tools exist](https://github.com/frohoff/ysoserial) to
      automatically create
      exploit code for these vulnerabilities.
    metadata:
      shortDescription: "Deserialization of untrusted data"
      category: "security"
      cwe: "CWE-502"
      owasp:
        - "A8:2017-Insecure Deserialization"
        - "A08:2021-Software and Data Integrity Failures"
      security-severity: "High"
      related-packages: ['pkg:maven/java/beans']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: |
          (java.beans.XMLDecoder $D).readObject();
      - pattern-not:
          pattern-either:
            - patterns:
                - pattern-inside: |
                    java.beans.XMLDecoder $DEC = new java.beans.XMLDecoder(..., $CL);
                    ...
                - pattern: $DEC.readObject();
                - metavariable-pattern:
                    metavariable: $CL
                    patterns:
                      - pattern: |
                          new ClassLoader(){
                            ...
                            $RET loadClass(String name, boolean resolve){
                              if($X){
                                throw ...
                              }
                              ...
                            }
                            ...
                          }
                      - metavariable-pattern:
                          metavariable: $X
                          pattern-either:
                            - pattern: |
                                !name.equals(...)
                            - pattern: |
                                !$LIST.contains(name)
            - patterns:
                - pattern-inside: |
                    ClassLoader $CLASS_LOADER = $CL;
                    ...
                    java.beans.XMLDecoder $DEC = new java.beans.XMLDecoder(..., $CLASS_LOADER);
                    ...
                - pattern: $DEC.readObject();
                - metavariable-pattern:
                    metavariable: $CL
                    patterns:
                      - pattern: |
                          new ClassLoader(){
                            ...
                            $RET loadClass(String name, boolean resolve){
                              if($X){
                                throw ...
                              }
                              ...
                            }
                            ...
                          }
                      - metavariable-pattern:
                          metavariable: $X
                          pattern-either:
                            - pattern: |
                                !name.equals(...)
                            - pattern: |
                                !$LIST.contains(name)
    severity: "WARNING"
  - id: "java_xml_rule-XsltTransform"
    languages:
      - "java"
    mode: "taint"
    pattern-sources:
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern-inside: |
                      $FUNC(...,String $VAR, ...) {
                        ...
                      }
                  - pattern-either:
                      - pattern: "new FileInputStream(<... $VAR ...>);"
                      - pattern: "getClass().getResourceAsStream(<... $VAR ...>)"
              - patterns:
                  - pattern-inside: |
                      class $CLZ {
                        String $X = "...";
                        ...
                      }
                  - pattern-inside: |
                      $FUNC(...,String $Y, ...) {
                        ...
                      }
                  - pattern-either:
                      - pattern: "new FileInputStream($X + $Y);"
                      - pattern: "getClass().getResourceAsStream($X + $Y)"
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern-inside: |-
                  (javax.xml.transform.TransformerFactory $T).newTransformer($SRC, ...)
              - pattern-inside: |-
                  (javax.xml.transform.Transformer $T).transform($SRC, ...)
          - pattern: "$SRC"
    message: |
      The application performs XSLT translation with potentially malicious input. An adversary who
      is able to influence the
      loaded
      XSL document could call XSL functions or exploit External XML Entity (XXE) attacks that allow
      file
      retrieval or force the parser to connect to arbitrary servers to exfiltrate files. It is
      strongly
      recommended that an alternative approach is used to work with XML data.

      For increased security, never process user-supplied XSL style sheets. If XSLT processing is
      absolutely
      necessary, ensure that `FEATURE_SECURE_PROCESSING` is enabled prior to processing the XSLT
      file:
      ```
      // Create a new TransformerFactory instance
      TransformerFactory transformerFactory = TransformerFactory.newInstance();
      // Enable the FEATURE_SECURE_PROCESSING feature
      transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
      // Read in the XML Source
      Source xmlSource = new StreamSource(new FileInputStream("hardcoded.xml"));
      // Read in the XSL template file
      Source xslSource = new StreamSource(new FileInputStream("hardcoded.xsl"));
      /// Create the transformer object to do the transformation
      Transformer transformer = transformerFactory.newTransformer(xslSource);
      // Create a Result object for output
      Result result = new StreamResult(System.out);
      // Execute the transformation process
      transformer.transform(xmlSource, result);
      ```

      For more information on XML security see OWASP's guide:
      https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#java

      For more information on the secure processing feature see:
      - https://xml.apache.org/xalan-j/features.html#secureprocessing
    metadata:
      shortDescription: "XML injection (aka Blind XPath injection)"
      category: "security"
      cwe: "CWE-91"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.xmlrpc/xmlrpc-client']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: java_xss_injection
    languages: [java]
    severity: ERROR
    mode: taint
    message: |
      A Cross‑Site Scripting (XSS) vulnerability was detected: untrusted data flows from request‑derived variables into server‑side rendering APIs without proper escaping or encoding.

      This can allow attackers to inject malicious scripts that execute in users' browsers, potentially leading to session hijacking, credential theft, or other client-side attacks.

      Remediation:
      - Use appropriate HTML escaping/encoding functions:
        * Spring: HtmlUtils.htmlEscape(userInput)
        * Apache Commons: StringEscapeUtils.escapeHtml4(userInput)
        * OWASP Java Encoder: Encode.forHtml(userInput)
      - Consider context-specific encoding for JavaScript, CSS, or URL contexts
      - Use modern template frameworks with auto-escaping features

    metadata:
      cwe: CWE-79
      owasp: [A03:2021-Injection]
      category: security
      security-severity: High
      confidence: High
      shortDescription: XSS vulnerability in Java web application
      impact: High
      likelihood: High
      related-packages:
        - org.springframework
        - jakarta.faces
        - org.apache.struts2
        - com.google.gwt

      license: License Not Found
    pattern-sources:
      - pattern: $REQ.getParameter(...)
      - pattern: $REQ.getParameterValues(...)
      - pattern: $REQ.getHeader(...)
      - pattern: $REQ.getCookies()

      - pattern: FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(...)
      - pattern: ExternalContext.getRequestParameterMap().get(...)

      - pattern: ActionContext.getContext().getParameters().get(...)

      - patterns:
          - pattern: $PARAM
          - pattern-inside: |
              public $RET $METHOD(..., @RequestParam $TYPE $PARAM, ...) { ... }
      - patterns:
          - pattern: $PARAM
          - pattern-inside: |
              public $RET $METHOD(..., @PathVariable $TYPE $PARAM, ...) { ... }

      - pattern: Window.Location.getParameter(...)

    pattern-sinks:
      - pattern: $RESP.getWriter().print($DATA)
      - pattern: $RESP.getWriter().println($DATA)
      - pattern: $RESP.getWriter().write($DATA)

      - patterns:
          - pattern: $W.write($DATA)
          - pattern-inside: |
              $W = FacesContext.getCurrentInstance().getResponseWriter();

      - pattern: $MODEL.addAttribute($K, $DATA)
      - pattern: $MAV.addObject($K, $DATA)

      - pattern: new FacesMessage($DATA)
      - pattern: new FacesMessage("..." + $DATA + ...)

      - pattern: new HTML($DATA)
      - pattern: new HTMLPanel($DATA)

      - pattern: $MAP.put($K, $DATA)
      - pattern: new JSONString($DATA)

      - pattern: Window.Location.assign($DATA)
      - pattern: new RequestBuilder(RequestBuilder.GET, $DATA)

      - patterns:
          - pattern: $FIELD = $DATA
          - pattern-inside: |
              @ManagedBean
              class $C { ... }

      - patterns:
          - pattern: return $DATA;
          - pattern-inside: |
              public String get$NAME() { ... }



    pattern-sanitizers:
      - pattern: HtmlUtils.htmlEscape(...)
      - pattern: StringEscapeUtils.escapeHtml4(...)
      - pattern: Encode.forHtml(...)
      - pattern: SafeHtmlUtils.fromString(...)
      - pattern: SafeHtmlUtils.htmlEscape(...)
      - pattern: URL.encodeQueryString(...)

      - pattern: $MODEL.addAttribute(..., HtmlUtils.htmlEscape(...))
      - pattern: $MODEL.addAttribute(..., StringEscapeUtils.escapeHtml4(...))
      - pattern: $MODEL.addAttribute(..., Encode.forHtml(...))

      - pattern: $MAP.put(..., HtmlUtils.htmlEscape(...))
      - pattern: $MAP.put(..., StringEscapeUtils.escapeHtml4(...))
      - pattern: $MAP.put(..., Encode.forHtml(...))
      - pattern: new JSONString(SafeHtmlUtils.htmlEscape(...))

      - pattern: new HTML(SafeHtmlUtils.fromString(...))
      - pattern: new HTMLPanel(SafeHtmlUtils.fromString(...))
      - pattern: ObjectMapper().writeValueAsString($VAL)
  - id: "java_xss_rule-WicketXSS"
    languages:
      - "java"
    patterns:
      - pattern-inside: |
          import org.apache.wicket.$A;
          ...
      - pattern: |
          $OBJ.setEscapeModelStrings(false);
    message: |
      The application is disabling Wicket's string escaping functionality by calling
      `setEscapeModelStrings(false)`.
      This could lead to Cross Site Scripting (XSS) if used with user-supplied input. XSS is an
      attack which exploits
       a web application or system to treat    user input
      as markup or script code. It is important to encode the data depending on the specific context
      it
      is used in. There are at least six context types:

      - Inside HTML tags `<div>context 1</div>`
      - Inside attributes: `<div class="context 2"></div>`
      - Inside event attributes `<button onclick="context 3">button</button>`
      - Inside script blocks: `<script>var x = "context 4"</script>`
      - Unsafe element HTML assignment: `element.innerHTML = "context 5"`
      - Inside URLs: `<iframe src="context 6"></iframe><a href="context 6">link</a>`

      Script blocks alone have multiple ways they need to be encoded. Extra care must be taken if
      user input
      is ever output inside of script tags.

      User input that is displayed within the application must be encoded, sanitized or validated
      to ensure it cannot be treated as HTML or executed as JavaScript code. Care must also be
      taken
      to not mix server-side templating with client-side templating, as the server-side templating
      will
      not encode things like {{ 7*7 }} which may execute client-side templating features.

      It is _NOT_ advised to encode user input prior to inserting into a data store. The data will
      need to be
      encoded depending on context of where it is output. It is much safer to force the displaying
      system to
      handle the encoding and not attempt to guess how it should be encoded.

      Use Wicket's built in escaping feature by calling `Component.setEscapeModelStrings(true);`

      For more information on Wicket components see:
      - https://nightlies.apache.org/wicket/apidocs/9.x/org/apache/wicket/Component.html

      For more information on XSS see OWASP:
      - https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
    metadata:
      shortDescription: "Improper neutralization of input during web page generation ('Cross-site Scripting')"
      category: "security"
      cwe: "CWE-79"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
    related-packages: ['pkg:maven/org.apache.wicket/wicket']
  - id: "java_xss_rule-XSSReqParamToServletWriter"
    languages:
      - "java"
    mode: "taint"
    pattern-sources:
      - pattern: (javax.servlet.http.HttpServletRequest $R).$METHOD(...)
      - pattern: (java.util.Scanner $S).$METHOD(...)
      - pattern: (java.util.stream.Stream).$METHOD(...)
      - pattern: (java.util.StringJoiner $SJ).toString(...)
      - pattern: (java.sql.ResultSet.getString $R).$METHOD(...)
      - pattern: (java.lang.System $S).getProperty(...)
      - pattern: (java.lang.System $S).getenv(...)
      - pattern: (java.lang.StringBuilder $SB).toString(...)
      - pattern: (java.io.FileInputStream $F).read(...)
      - pattern: (java.io.FileReader $F).read(...)
      - pattern: (java.net.Socket $S).getInputStream(...)
      - pattern: (java.net.Socket $S).getOutputStream(...)
      - pattern: (java.net.DatagramSocket $S).receive(...)
      - pattern: (java.net.DatagramSocket $S).getInputStream(...)
      - pattern: java.nio.file.Files.readAllBytes(...)
      - pattern: java.nio.file.Files.readAllLines(...)
      - pattern: java.nio.file.Files.lines(...)
      - pattern: java.nio.file.Files.newBufferedReader(...)
      - pattern: org.apache.commons.io.IOUtils.toString(...)
      - pattern: org.apache.commons.io.IOUtils.readLines(...)
      - pattern: org.apache.commons.io.IOUtils.toByteArray(...)
      - pattern: (com.fasterxml.jackson.databind.ObjectMapper $OM).readValue(...)
      - pattern: (com.fasterxml.jackson.databind.ObjectMapper $OM).treeToValue(...)
      - pattern: $CLASS.$METHOD(..., (javax.servlet.http.HttpServletRequest $R), ...)
      - pattern: $FUNC(..., (javax.servlet.http.HttpServletRequest $R), ...)
      - patterns:
          - pattern-inside: $FUNC(..., String $X, ...) { ... }
          - focus-metavariable: $X
    pattern-propagators:
      - pattern: $LIST.add($X)
        from: $X
        to: $LIST
      - pattern: $MAP.put(..., $X)
        from: $X
        to: $MAP
      - pattern: $STR.concat($X)
        from: $X
        to: $STR
      - pattern: $STR = String.format(..., $X, ...)
        from: $X
        to: $STR
      - pattern: $STR = String.join(..., $X, ...)
        from: $X
        to: $STR
    pattern-sanitizers:
      - pattern: org.owasp.encoder.Encode.forHtml(...)
      - pattern: org.owasp.esapi.ESAPI.encoder().encodeForHTML(...)
      - pattern: org.apache.commons.text.StringEscapeUtils.escapeHtml3(...)
      - pattern: org.apache.commons.text.StringEscapeUtils.escapeHtml4(...)
      - pattern: org.owasp.benchmark.helpers.Utils.encodeForHTML(...)
    pattern-sinks:
      - pattern: (javax.servlet.http.HttpServletResponse $R).getWriter().print(...)
      - patterns:
          - pattern-inside: |
              $W = (javax.servlet.http.HttpServletResponse $R).getWriter();
              ...
          - pattern: $W.print(...);
      - pattern: (javax.servlet.http.HttpServletResponse $R).getWriter().println(...)
      - patterns:
          - pattern-inside: |
              $W = (javax.servlet.http.HttpServletResponse $R).getWriter();
              ...
          - pattern: $W.println(...);
      - pattern: (javax.servlet.http.HttpServletResponse $R).getWriter().format(...)
      - patterns:
          - pattern-inside: |
              $W = (javax.servlet.http.HttpServletResponse $R).getWriter();
              ...
          - pattern: $W.format(...);
      - pattern: (javax.servlet.http.HttpServletResponse $R).getWriter().append(...)
      - patterns:
          - pattern-inside: |
              $W = (javax.servlet.http.HttpServletResponse $R).getWriter();
              ...
          - pattern: $W.append(...);
      - pattern: (javax.servlet.http.HttpServletResponse $R).getWriter().write(...)
      - patterns:
          - pattern-inside: |
              $W = (javax.servlet.http.HttpServletResponse $R).getWriter();
              ...
          - pattern: $W.write(...);
    message: |
      The application is returning user-supplied data from an HTTP request directly into an HTTP
      response output
      writer. This could lead to Cross Site Scripting (XSS) if the input were malicious
      script code and the application server is not properly validating the output.

      XSS is an attack which exploits a web application or system to treat user input
      as markup or script code. It is important to encode the data depending on the specific context
      it is used in. There are at least six context types:

      - Inside HTML tags `<div>context 1</div>`
      - Inside attributes: `<div class="context 2"></div>`
      - Inside event attributes `<button onclick="context 3">button</button>`
      - Inside script blocks: `<script>var x = "context 4"</script>`
      - Unsafe element HTML assignment: `element.innerHTML = "context 5"`
      - Inside URLs: `<iframe src="context 6"></iframe><a href="context 6">link</a>`

      Script blocks alone have multiple ways they need to be encoded. Extra care must be taken if
      user input
      is ever output inside of script tags.

      User input that is displayed within the application must be encoded, sanitized or validated
      to ensure it cannot be treated as HTML or executed as Javascript code. Care must also be
      taken
      to not mix server-side templating with client-side templating, as the server-side templating
      will
      not encode things like {{ 7*7 }} which may execute client-side templating features.

      It is _NOT_ advised to encode user input prior to inserting into a data store. The data will
      need to be
      encoded depending on context of where it is output. It is much safer to force the displaying
      system to
      handle the encoding and not attempt to guess how it should be encoded.

      If possible do not use user input directly in the output to the response writer.

      If the application must output user-supplied input, it will need to encode the data depending
      on
      the output context.

      Consider using [Apache Commons Text](https://commons.apache.org/proper/commons-text/)
      `StringEscapeUtils` methods for various context. Please note there is no way to safely
      output script code in most circumstances, regardless of encoding. If calling the HTTP
      response writer directly, ensure that the `Content-Type` is set to `text/plain` so it will
      not be accidentally interpreted by HTML by modern browsers.
      ```
      // Get user input
      String htmlInput = request.getParameter("userInput");
      // Encode the input using the Html4 encoder
      String htmlEncoded = StringEscapeUtils.escapeHtml4(htmlInput);
      // Force the HTTP response to be content type of text/plain so it is not interpreted as HTML
      response.setContentType("text/plain");
      // Ensure UTF-8
      response.setCharacterEncoding("UTF-8");
      // Write response
      response.getWriter().write(htmlEncoded);
      ```

      For more information on XSS see OWASP:
      - https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
    severity: "WARNING"
    metadata:
      shortDescription: "Improper neutralization of input during web page generation ('Cross-site Scripting')"
      category: "security"
      cwe: "CWE-79"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "java"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.commons.text/commons-text']
      license: MIT (c) GitLab Inc.
  - id: "java_xxe_rule-XMLRdr"
    languages:
      - "java"
    message: |
      External XML entities are a feature of XML parsers that allow documents to contain references
      to
      other documents or data. This feature can be abused to read files, communicate with external
      hosts,
      exfiltrate data, or cause a Denial of Service (DoS).

      The XMLReaderFactory has been deprecated. It is recommended that
      [SAXParserFactory](https://docs.oracle.com/javase/9/docs/api/javax/xml/parsers/SAXParserFactory.html)
      be used
      instead. Additionally when using the SAXParser it must be configured to disallow doctypes,
      which will
      protect against the majority of XXE attacks.

      Example creating a SAXParser with disallowing the doctypes feature enabled:
      ```
      // Create a SAXParserFactory
      SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
      // Enable the feature which disallows <!DOCTYPE declarations which includes referencing
      external entities.
      saxParserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
      // Create a new parser from this factory
      SAXParser parser = saxParserFactory.newSAXParser();
      // Parse the XML file, passing in a DefaultHandler (which also includes an empty entityResolve
      method)
      parser.parse(new FileInputStream(new File("bad.xml")), new DefaultHandler());
      ```

      For more information on XML security see OWASP's guide:
      https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#java
    patterns:
      - pattern-inside: |
          $R = XMLReaderFactory.createXMLReader();
          ...
      - pattern-not-inside: |
          $R.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
          ...
      - pattern: "$R.parse(...);"
    metadata:
      shortDescription: "Improper restriction of XML external entity reference ('XXE')"
      category: "security"
      cwe: "CWE-611"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.xmlrpc/xmlrpc-client']
      license: MIT (c) GitLab Inc.
    severity: "ERROR"
  - id: "javascript-rule-xss-angular"
    languages:
      - "typescript"
      - "javascript"
    pattern-either:
      - patterns:
          - pattern-either:
              - pattern: '... .$BYPASS_SECURITY($DATA)'
              - pattern: '... .$BYPASS_SECURITY = ...'
              - pattern: $BYPASS_SECURITY($DATA)
          - metavariable-regex:
              metavariable: $BYPASS_SECURITY
              regex: ^bypassSecurityTrust


    severity: "WARNING"
    message: |
      Use of Angular’s `bypassSecurityTrust...()` functions detected. These APIs disable Angular’s built-in XSS protections.
      If used with untrusted data, this can lead to Cross-Site Scripting (XSS) vulnerabilities.

      To fix:
      - Avoid using `bypassSecurityTrustHtml`, `bypassSecurityTrustScript`, etc. with user input.
      - Sanitize the input using trusted sanitization libraries (e.g., DOMPurify).
      - Use Angular’s binding syntax (`[innerHTML]`) only with safe content.
      For more information on dangerously setting inner HTML see:
      - https://angular.dev/api/platform-browser/DomSanitizer
    metadata:
      cwe: "CWE-79"
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      shortDescription: "Use of bypassSecurityTrust* method in Angular ('Cross-site Scripting')"
      category: "security"
      security-severity: "Medium"
      related-packages: ['pkg:npm/@angular/cli']
      license: License Not Found
  - id: "javascript-rule-xss-react"
    languages:
      - "typescript"
      - "javascript"
    pattern-either:
      - pattern: |
          <$X dangerouslySetInnerHTML=... />
      - pattern: |
          {dangerouslySetInnerHTML: ...}
    severity: "WARNING"
    message: |
      The application was found calling `dangerouslySetInnerHTML` which may lead to Cross Site
      Scripting (XSS). By default, React components will encode the data properly before rendering.
      Calling `dangerouslySetInnerHTML` disables this encoding and allows raw markup
      and JavaScript to be executed.

      XSS is an attack which exploits a web application or system to treat
      user input as markup or script code. It is important to encode the data, depending on the
      specific context it is used in. There are at least six context types:

      - Inside HTML tags `<div>context 1</div>`
      - Inside attributes: `<div class="context 2"></div>`
      - Inside event attributes `<button onclick="context 3">button</button>`
      - Inside script blocks: `<script>var x = "context 4"</script>`
      - Unsafe element HTML assignment: `element.innerHTML = "context 5"`
      - Inside URLs: `<iframe src="context 6"></iframe><a href="context 6">link</a>`

      Script blocks alone have multiple ways they need to be encoded. Extra care must be taken if
      user input
      is ever output inside of script tags.

      User input that is displayed within the application must be encoded, sanitized or validated
      to ensure it cannot be treated as HTML or executed as Javascript code. Care must also be
      taken
      to not mix server-side templating with client-side templating, as the server-side templating
      will
      not encode things like {{ 7*7 }} which may execute client-side templating features.

      It is _NOT_ advised to encode user input prior to inserting into a data store. The data will
      need to be
      encoded depending on context of where it is output. It is much safer to force the displaying
      system to
      handle the encoding and not attempt to guess how it should be encoded.

      Remove the call to `dangerouslySetInnerHTML` or ensure that the data used in this call does
      not come from user-supplied input.

      For more information on dangerously setting inner HTML see:
      - https://react.dev/reference/react-dom/components/common#dangerously-setting-the-inner-html
    metadata:
      cwe: "CWE-79"
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      shortDescription: "Improper neutralization of input during web page generation ('Cross-site Scripting')"
      category: "security"
      security-severity: "Medium"
      related-packages: ['pkg:npm/react']
      license: MIT (c) GitLab Inc.
  - id: "javascript-rule-xss-svelte"
    languages:
      - "typescript"
      - "javascript"
    pattern-either:
      - patterns:
          - pattern-inside: |
              `...`
          - pattern-regex: '{\s*@html'
    severity: "WARNING"
    message: |
      Use of `{@html ...}` detected in Svelte, which renders raw HTML content directly into the DOM.
      This is dangerous if the content is user-controlled, as it can lead to Cross-Site Scripting (XSS).

      To fix:
      - Avoid using `{@html}` with untrusted input.
      - Use a trusted HTML sanitizer (like DOMPurify) before inserting dynamic content.
      - Prefer normal text binding (`{}`) where possible, as it auto-escapes content.

      For more information on dangerously setting inner HTML see:
      - https://svelte.dev/docs/svelte/@html
    metadata:
      cwe: "CWE-79"
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      shortDescription: "Use of {@html * } in Svelte ('Cross-site Scripting')"
      category: "security"
      security-severity: "Medium"
      related-packages: ['pkg:npm/svelte']
      license: License Not Found
  - id: "javascript-rule-xss-vuejs"
    languages:
      - "typescript"
      - "javascript"
    pattern-either:
      - patterns:
          - pattern-inside: |
              `...`
          - pattern-regex: \sv-html=
      - pattern: |
          {...,
          'vue/no-v-html': 'off',
          ...
          }
    severity: "WARNING"
    message: |
      Use of `v-html` directive detected in Vue, which renders raw HTML into the DOM.
      This is dangerous if the input contains user-controlled or untrusted data and can result in XSS.

      Additionally, ESLint rule `vue/no-v-html` appears to be disabled in configuration, reducing visibility of this risk.

       To fix:
      - Avoid `v-html` when possible; prefer `{{ variable }}` which auto-escapes content.
      - If `v-html` must be used, sanitize the data first using a library like DOMPurify.
      - Re-enable `vue/no-v-html` in ESLint to warn developers about unsafe raw HTML usage.
      For more information on dangerously setting inner HTML see:
      - https://vuejs.org/guide/essentials/template-syntax
    metadata:
      cwe: "CWE-79"
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      shortDescription: "Usage of v-html in Vue js ('Cross-site Scripting')"
      category: "security"
      security-severity: "Medium"
      related-packages: ['pkg:npm/vue']
      license: MIT (c) GitLab Inc.
  - id: javascript_buf_rule-buffer-noassert-read
    patterns:
      - pattern: "$OBJ.$METHOD(..., true)"
      - metavariable-regex:
          metavariable: $METHOD
          regex: 
            ^(readUInt8|readUInt16LE|readUInt16BE|readUInt32LE|readUInt32BE|readUInt32BE|readInt8|readInt16LE|readInt16BE|readInt32LE|readInt32BE|readFloatLE|readFloatBE|readDoubleLE|readDoubleBE)$
    severity: "WARNING"
    languages:
      - "javascript"
    metadata:
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-125"
      shortDescription: "Out-of-bounds read"
      security-severity: "MEDIUM"
      category: "security"
      source-rule-url: "https://github.com/nodesecurity/eslint-plugin-security/blob/master/rules/detect-buffer-noassert.js"
      related-packages: ['pkg:npm/buffer']
      license: MIT (c) GitLab Inc.
    message: |
      The application is using Buffer API methods with the `noAssert` 
      parameter set to `true` for the read buffer methods. This disables 
      the bounds checking and could result in reading beyond the end of 
      the buffer, leading to potential memory corruption and security 
      vulnerabilities. When `noAssert` is set to `true`, the methods 
      do not perform bounds checking, allowing the offset to be beyond 
      the end of the buffer. This can lead to out-of-bounds memory access, 
      causing undefined behavior, crashes, or potential security exploits.

      To mitigate the issue, ensure that the `noAssert` parameter is not 
      set to `true`. Always use bounds checking to prevent out-of-bounds 
      memory access. Validate the offset before performing read operations 
      on the buffer.

      Secure Code Example:
      ```
        const buffer = Buffer.alloc(10); // Allocate a buffer of 10 bytes

        function safeBufferMethods(buf) {
          const safeOffset = 5; // An offset within the bounds of the buffer

          // Secure read methods
          if (safeOffset < buf.length) {
            buf.readUInt8(safeOffset);
            buf.readUInt16LE(safeOffset);
            buf.readUInt16BE(safeOffset);
            buf.readUInt32LE(safeOffset);
            buf.readUInt32BE(safeOffset);
            buf.readInt8(safeOffset);
            buf.readInt16LE(safeOffset);
            buf.readInt16BE(safeOffset);
            buf.readInt32LE(safeOffset);
            buf.readInt32BE(safeOffset);
            buf.readFloatLE(safeOffset);
            buf.readFloatBE(safeOffset);
            buf.readDoubleLE(safeOffset);
            buf.readDoubleBE(safeOffset);
          }
        }
        try {
          safeBufferMethods(buffer);
        } catch (error) {
          console.error('Caught an error:', error);
        }
      ```
  - id: javascript_buf_rule-buffer-noassert-write
    patterns:
      - pattern: "$OBJ.$METHOD(..., true)"
      - metavariable-regex:
          metavariable: $METHOD
          regex: 
            ^(writeUInt8|writeUInt16LE|writeUInt16BE|writeUInt32LE|writeUInt32BE|writeInt8|writeInt16LE|writeInt16BE|writeInt32LE|writeInt32BE|writeFloatLE|writeFloatBE|writeDoubleLE|writeDoubleBE)$
    severity: "WARNING"
    languages:
      - "javascript"
    metadata:
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-787"
      shortDescription: "Out-of-bounds write"
      security-severity: "MEDIUM"
      category: "security"
      source-rule-url: "https://github.com/nodesecurity/eslint-plugin-security/blob/master/rules/detect-buffer-noassert.js"
      related-packages: ['pkg:npm/buffer']
      license: MIT (c) GitLab Inc.
    message: |
      The application is using Buffer API methods with the `noAssert` 
      parameter set to `true` for the write buffer methods. This disables 
      the bounds checking and could result in writing beyond the end of 
      the buffer, leading to potential memory corruption and security 
      vulnerabilities. When `noAssert` is set to `true`, the methods 
      do not perform bounds checking, allowing the offset to be beyond 
      the end of the buffer. This can lead to out-of-bounds memory access, 
      causing undefined behavior, crashes, or potential security exploits.

      To mitigate the issue, ensure that the `noAssert` parameter is not 
      set to `true`. Always use bounds checking to prevent out-of-bounds 
      memory access. Validate the offset before performing write operations 
      on the buffer.

      Secure Code Example:
      ```
        const buffer = Buffer.alloc(10); // Allocate a buffer of 10 bytes

        function safeBufferMethods(buf) {
          const safeOffset = 5; // An offset within the bounds of the buffer

          // Secure write methods
          if (safeOffset < buf.length) {
            buf.writeUInt8(0, safeOffset);
            buf.writeUInt16LE(0, safeOffset);
            buf.writeUInt16BE(0, safeOffset);
            buf.writeUInt32LE(0, safeOffset);
            buf.writeUInt32BE(0, safeOffset);
            buf.writeInt8(0, safeOffset);
            buf.writeInt16LE(0, safeOffset);
            buf.writeInt16BE(0, safeOffset);
            buf.writeInt32LE(0, safeOffset);
            buf.writeInt32BE(0, safeOffset);
            buf.writeFloatLE(0, safeOffset);
            buf.writeFloatBE(0, safeOffset);
            buf.writeDoubleLE(0, safeOffset);
            buf.writeDoubleBE(0, safeOffset);
          }
        }

        try {
          safeBufferMethods(buffer);
        } catch (error) {
          console.error('Caught an error:', error);
        }
      ```
  - id: "javascript_buf_rule-detect-new-buffer"
    languages:
      - "javascript"
      - "typescript"
    patterns:
      - patterns:
          - pattern-not-inside: |-
              require("safe-buffer")
              ...
          - pattern-not-inside: |-
              import { Buffer } from "safe-buffer"
              ...
          - pattern-inside: |-
              function $FUNC(..., $X, ...) { ... }
          - pattern: "new Buffer($X,...)"
      - pattern-not: |
          new Buffer("...",...)
      - pattern-not: |
          new Buffer([...] , ...)
    message: |
      The application was found calling  the `new Buffer` constructor which has been deprecated
      since Node 8.
      By passing in a non-literal value, an adversary could allocate large amounts of memory.

      Other issues also exist with the `Buffer` constructor:
      - Older versions would return uninitialized memory, which could contain sensitive information
      - Unable to easily determine what a Buffer contained if passed a non-literal value

      To remediate this issue, use `Buffer.alloc` or `Buffer.from` instead to allocate a new
      `Buffer`.

      Example using `Buffer.alloc` instead of `new Buffer(...)`:
      ```
      // Create a new buffer using Buffer.from
      const buf = Buffer.from([1, 2, 3, 4]);
      // Work with buf
      ```

      For more information on migrating to `Buffer.from()`/`Buffer.alloc()` see:
      - https://nodejs.org/en/docs/guides/buffer-constructor-deprecation
    metadata:
      shortDescription: "Allocation of resources without limits or throttling"
      cwe: "CWE-770"
      category: "security"
      owasp:
        - "A9:2017-Using Components with Known Vulnerabilities"
        - "A06:2021-Vulnerable and Outdated Components"
    # yamllint disable
      source-rule-url: "https://github.com/eslint-community/eslint-plugin-security/blob/main/rules/detect-new-buffer.js"
    # yamllint enable
      security-severity: "Medium"
      related-packages: ['pkg:npm/buffer']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "javascript_dos_rule-non-literal-regexp"
    languages:
      - "javascript"
      - "typescript"
    pattern-either:
      - patterns:
          - pattern: |
              new RegExp(...)
          - pattern-not: |
              new RegExp("...", ...)
          - pattern-not: |
              new RegExp(/.../, ...)
      - patterns:
          - pattern: |
              RegExp(...)
          - pattern-not: |
              RegExp("...", ...)
          - pattern-not: |
              RegExp(/.../, ...)
      - patterns:
          - pattern: |
              "...".$METHOD(...)
          - pattern-not: |
              "...".$METHOD("...")
          - pattern-not: |
              "...".$METHOD(/.../)
          - metavariable-regex:
              metavariable: $METHOD
              regex: "^(match|search)$"
    message: |
      The `RegExp` constructor was called with a non-literal value. If an adversary were able to
      supply a malicious regex, they could cause a Regular Expression Denial of Service (ReDoS)
      against the application. In Node applications, this could cause the entire application to no
      longer be responsive to other users' requests.

      To remediate this issue, never allow user-supplied regular expressions. Instead, the regular 
      expression should be  hardcoded. If this is not possible, consider using an alternative regular
      expression engine such as [node-re2](https://www.npmjs.com/package/re2). RE2 is a safe alternative 
      that does not support backtracking, which is what leads to ReDoS.

      Example using re2 which does not support backtracking (Note: it is still recommended to
      never use user-supplied input):
      ```
      // Import the re2 module
      const RE2 = require('re2');

      function match(userSuppliedRegex, userInput) {
          // Create a RE2 object with the user supplied regex, this is relatively safe
          // due to RE2 not supporting backtracking which can be abused to cause long running
          // queries
          var re = new RE2(userSuppliedRegex);
          // Execute the regular expression against some userInput
          var result = re.exec(userInput);
          // Work with the result
      }
      ```

      For more information on Regular Expression DoS see:
      - https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
    metadata:
      cwe: "CWE-185"
      shortDescription: "Regular expression with non-literal value"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
    related-packages: ['pkg:npm/re2']
  - id: "javascript_eval_rule-eval-with-expression"
    languages:
      - "javascript"
      - "typescript"
    patterns:
      - pattern-either:
          - patterns:
              - pattern: |
                  eval($OBJ)
              - pattern-not: |
                  eval("...")
          - patterns:
              - pattern: |
                  $A.eval($OBJ)
              - pattern-not: |
                  $A.eval("...")
          - patterns:
              - pattern: |
                  (..., eval)($OBJ)
              - pattern-not: |
                  (..., eval)("...")
          - patterns:
              - pattern: |
                  $ALIAS = eval;
                  ...
                  $ALIAS($OBJ)
              - pattern-not: |
                  $ALIAS = eval;
                  ...
                  $ALIAS("...")
          - pattern: |
              new Function(..., "..." + $OBJ + "...")
          - pattern: |
              $BODY = "..." + $OBJ + "..."
              ...
              new Function(..., $BODY)
          - pattern: |
              new Function(..., `...${...}...`)
          - pattern: |
              $BODY = `...${...}...`
              ...
              new Function(..., $BODY)
          - pattern: |
              Function(..., "..." + $OBJ + "...")
          - pattern: |
              $BODY = "..." + $OBJ + "..."
              ...
              Function(..., $BODY)
          - pattern: |
              $BODY = `...${...}...`
              ...
              Function(..., $BODY)
          - pattern: |
              Function(..., `...${...}...`)
          - pattern: |
              setTimeout("..." + $OBJ + "...", ...)
          - pattern: |
              setTimeout(`...${...}...`, ...)
          - pattern: |
              setInterval("..." + $OBJ + "...", ...)
          - pattern: |
              setInterval(`...${...}...`, ...)
      - pattern-not: |
          setTimeout("...", ...)
      - pattern-not: |
          setInterval("...", ...)
    severity: "WARNING"
    message: |
      The application was found calling the `eval` function OR Function()
        constructor OR setTimeout() OR setInterval() methods. If the

        variables or strings or functions passed to these methods contains user-supplied input, an adversary could attempt to execute arbitrary

        JavaScript

        code. This could lead to a full system compromise in Node applications or Cross-site Scripting

        (XSS) in web applications.


        To remediate this issue, remove all calls to above methods and consider alternative methods for

        executing

        the necessary business logic. There is almost no safe method of calling `eval` or other above stated sinks with

        user-supplied input.

        Instead, consider alternative methods such as using property accessors to dynamically access

        values.


        Example using property accessors to dynamically access an object's property:

        ```

        // Define an object

        const obj = {key1: 'value1', key2: 'value2'};

        // Get key dynamically from user input

        const key = getUserInput();

        // Check if the key exists in our object and return it, or a default empty string

        const value = (obj.hasOwnProperty(key)) ? obj[key] : '';

        // Work with the value

        ```


        For more information on why not to use `eval`, and alternatives see:

        - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#never_use_eval!

        Other References:

        - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function

        - https://developer.mozilla.org/en-US/docs/Web/API/setTimeout

        - https://developer.mozilla.org/en-US/docs/Web/API/setInterval
    metadata:
      cwe: "CWE-95"
      shortDescription: "Improper neutralization of directives in dynamically evaluated code ('Eval Injection')"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
    # yamllint disable
      source-rule-url: "https://github.com/nodesecurity/eslint-plugin-security/blob/master/rules/detect-eval-with-expression.js"
    # yamllint enable
      security-severity: "High"
      license: MIT (c) GitLab Inc.
  - id: "javascript_pathtraversal_rule-non-literal-fs-filename"
    languages:
      - "typescript"
      - "javascript"
    patterns:
      - pattern-either:
          - pattern-inside: |
              import $MOD from $IMP
              ...
          - pattern-inside: |
              $MOD = require($IMP)
              ...
      - metavariable-comparison:
          metavariable: "$IMP"
          comparison: "$IMP in ['fs', 'node:fs', 'fs/promises', 'node:fs/promises', 'fs-extra']"
      - pattern-not: "$MOD.appendFile(\"...\", ...)"
      - pattern-not: "$MOD.appendFileSync(\"...\", ...)"
      - pattern-not: "$MOD.chmod(\"...\", ...)"
      - pattern-not: "$MOD.chmodSync(\"...\", ...)"
      - pattern-not: "$MOD.chown(\"...\", ...)"
      - pattern-not: "$MOD.chownSync(\"...\", ...)"
      - pattern-not: "$MOD.createReadStream(\"...\", ...)"
      - pattern-not: "$MOD.createWriteStream(\"...\", ...)"
      - pattern-not: "$MOD.exists(\"...\", ...)"
      - pattern-not: "$MOD.existsSync(\"...\", ...)"
      - pattern-not: "$MOD.lchmod(\"...\", ...)"
      - pattern-not: "$MOD.lchmodSync(\"...\", ...)"
      - pattern-not: "$MOD.lchown(\"...\", ...)"
      - pattern-not: "$MOD.lchownSync(\"...\", ...)"
      - pattern-not: "$MOD.link(\"...\", \"...\", ...)"
      - pattern-not: "$MOD.linkSync(\"...\", \"...\", ...)"
      - pattern-not: "$MOD.lstat(\"...\", ...)"
      - pattern-not: "$MOD.lstatSync(\"...\", ...)"
      - pattern-not: "$MOD.mkdir(\"...\", ...)"
      - pattern-not: "$MOD.mkdirSync(\"...\", ...)"
      - pattern-not: "$MOD.open(\"...\", ...)"
      - pattern-not: "$MOD.openSync(\"...\", ...)"
      - pattern-not: "$MOD.readdir(\"...\", ...)"
      - pattern-not: "$MOD.readdirSync(\"...\", ...)"
      - pattern-not: "$MOD.readFile(\"...\", ...)"
      - pattern-not: "$MOD.readFileSync(\"...\", ...)"
      - pattern-not: "$MOD.readlink(\"...\", ...)"
      - pattern-not: "$MOD.readlinkSync(\"...\", ...)"
      - pattern-not: "$MOD.realpath(\"...\", ...)"
      - pattern-not: "$MOD.realpathSync(\"...\", ...)"
      - pattern-not: "$MOD.rename(\"...\", \"...\", ...)"
      - pattern-not: "$MOD.renameSync(\"...\", \"...\", ...)"
      - pattern-not: "$MOD.rmdir(\"...\", ...)"
      - pattern-not: "$MOD.rmdirSync(\"...\", ...)"
      - pattern-not: "$MOD.stat(\"...\", ...)"
      - pattern-not: "$MOD.statSync(\"...\", ...)"
      - pattern-not: "$MOD.symlink(\"...\", \"...\", ...)"
      - pattern-not: "$MOD.symlinkSync(\"...\", \"...\", ...)"
      - pattern-not: "$MOD.truncate(\"...\", ...)"
      - pattern-not: "$MOD.truncateSync(\"...\", ...)"
      - pattern-not: "$MOD.unlink(\"...\", ...)"
      - pattern-not: "$MOD.unlinkSync(\"...\", ...)"
      - pattern-not: "$MOD.unwatchFile(\"...\", ...)"
      - pattern-not: "$MOD.utimes(\"...\", ...)"
      - pattern-not: "$MOD.utimesSync(\"...\", ...)"
      - pattern-not: "$MOD.watch(\"...\", ...)"
      - pattern-not: "$MOD.watchFile(\"...\", ...)"
      - pattern-not: "$MOD.writeFile(\"...\", ...)"
      - pattern-not: "$MOD.writeFileSync(\"...\", ...)"
      - pattern-either:
          - pattern: "$MOD.appendFile(...)"
          - pattern: "$MOD.appendFileSync(...)"
          - pattern: "$MOD.chmod(...)"
          - pattern: "$MOD.chmodSync(...)"
          - pattern: "$MOD.chown(...)"
          - pattern: "$MOD.chownSync(...)"
          - pattern: "$MOD.createReadStream(...)"
          - pattern: "$MOD.createWriteStream(...)"
          - pattern: "$MOD.exists(...)"
          - pattern: "$MOD.existsSync(...)"
          - pattern: "$MOD.lchmod(...)"
          - pattern: "$MOD.lchmodSync(...)"
          - pattern: "$MOD.lchown(...)"
          - pattern: "$MOD.lchownSync(...)"
          - pattern: "$MOD.link(...)"
          - pattern: "$MOD.linkSync(...)"
          - pattern: "$MOD.lstat(...)"
          - pattern: "$MOD.lstatSync(...)"
          - pattern: "$MOD.mkdir(...)"
          - pattern: "$MOD.mkdirSync(...)"
          - pattern: "$MOD.open(...)"
          - pattern: "$MOD.openSync(...)"
          - pattern: "$MOD.readdir(...)"
          - pattern: "$MOD.readdirSync(...)"
          - pattern: "$MOD.readFile(...)"
          - pattern: "$MOD.readFileSync(...)"
          - pattern: "$MOD.readlink(...)"
          - pattern: "$MOD.readlinkSync(...)"
          - pattern: "$MOD.realpath(...)"
          - pattern: "$MOD.realpathSync(...)"
          - pattern: "$MOD.rename(...)"
          - pattern: "$MOD.renameSync(...)"
          - pattern: "$MOD.rmdir(...)"
          - pattern: "$MOD.rmdirSync(...)"
          - pattern: "$MOD.stat(...)"
          - pattern: "$MOD.statSync(...)"
          - pattern: "$MOD.symlink(...)"
          - pattern: "$MOD.symlinkSync(...)"
          - pattern: "$MOD.truncate(...)"
          - pattern: "$MOD.truncateSync(...)"
          - pattern: "$MOD.unlink(...)"
          - pattern: "$MOD.unlinkSync(...)"
          - pattern: "$MOD.unwatchFile(...)"
          - pattern: "$MOD.utimes(...)"
          - pattern: "$MOD.utimesSync(...)"
          - pattern: "$MOD.watch(...)"
          - pattern: "$MOD.watchFile(...)"
          - pattern: "$MOD.writeFile(...)"
          - pattern: "$MOD.writeFileSync(...)"
    message: |
      The application dynamically constructs file or path information. If the path
      information comes from user-supplied input, it could be abused to read sensitive files,
      access other users' data, or aid in exploitation to gain further system access.

      User input should never be used in constructing paths or files for interacting
      with the filesystem. This includes filenames supplied by user uploads or downloads.
      If possible, consider hashing user input or using unique values and
      use `path.normalize` to resolve and validate the path information
      prior to processing any file functionality.

      Example using `path.normalize` and not allowing direct user input:
      ```
      // User input, saved only as a reference
      // id is a randomly generated UUID to be used as the filename
      const userData = {userFilename: userSuppliedFilename, id: crypto.randomUUID()};
      // Restrict all file processing to this directory only
      const basePath = '/app/restricted/';

      // Create the full path, but only use our random generated id as the filename
      const joinedPath = path.join(basePath, userData.id);
      // Normalize path, removing any '..'
      const fullPath = path.normalize(joinedPath);
      // Verify the fullPath is contained within our basePath
      if (!fullPath.startsWith(basePath)) {
          console.log("Invalid path specified!");
      }
      // Process / work with file
      // ...
      ```

      For more information on path traversal issues see OWASP:
      https://owasp.org/www-community/attacks/Path_Traversal
    metadata:
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      cwe: "CWE-22"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Medium"
      category: "security"
      related-packages: ['pkg:npm/fs', 'pkg:npm/fs-extra']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "javascript_random_rule-pseudo-random-bytes"
    languages:
      - "javascript"
      - "typescript"
    pattern: "crypto.pseudoRandomBytes"
    severity: "WARNING"
    message: |
      Depending on the context, generating weak random numbers may expose cryptographic functions,
      which rely on these numbers, to be exploitable. When generating numbers for sensitive values
      such as tokens, nonces, and cryptographic keys, it is recommended that the `randomBytes` method
      of the `crypto` module be used instead of `pseudoRandomBytes`.

      Example using `randomBytes`:
      ```
      // Generate 256 bytes of random data
      const randomBytes = crypto.randomBytes(256);
      ```

      For more information on JavaScript Cryptography see:
      https://nodejs.org/api/crypto.html#cryptorandombytessize-callback
    metadata:
      cwe: "CWE-338"
      shortDescription: "Use of cryptographically weak pseudo-random number generator (PRNG)"
      category: "security"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
    # yamllint disable
      source-rule-url: "https://github.com/nodesecurity/eslint-plugin-security/blob/master/rules/detect-pseudoRandomBytes.js"
    # yamllint enable
      security-severity: "Medium"
      related-packages: ['pkg:npm/crypto']
      license: MIT (c) GitLab Inc.
  - id: "javascript_require_rule-non-literal-require"
    languages:
      - "javascript"
      - "typescript"
    patterns:
      - pattern: "require($OBJ)"
      - pattern-not: "require('...')"
    severity: "WARNING"
    message: |
      The application was found to dynamically import a module by calling `require` using a
      non-literal string. An adversary might be able to read the first line of
      arbitrary files. If they had write access to the file system, they may also be able to
      execute arbitrary code.

      To remediate this issue, use a hardcoded string literal when calling `require`. Never call it
      it with dynamically created variables or user-supplied data.
    metadata:
      cwe: "CWE-95"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      shortDescription: "Improper neutralization of directives in dynamically evaluated code ('Eval Injection')"
      category: "security"
    # yamllint disable
      source-rule-url: "https://github.com/nodesecurity/eslint-plugin-security/blob/master/rules/detect-non-literal-require.js"
    # yamllint enable
      security-severity: "Low"
      related-packages: ['pkg:npm/require-from-string']
      license: MIT (c) GitLab Inc.
  - id: "javascript_timing_rule-possible-timing-attacks"
    languages:
      - "javascript"
    patterns:
      - pattern-not: "if ($Z == null) { ... };"
      - pattern-not: "if ($Z === null) { ... };"
      - pattern-not: "if ($Z != null) { ... };"
      - pattern-not: "if ($Z !== null) { ... };"
      - pattern-not: "if ($Q != undefined) { ... };"
      - pattern-not: "if ($Q !== undefined) { ... };"
      - pattern-not: "if ($Q == undefined) { ... };"
      - pattern-not: "if ($Q === undefined) { ... };"
      - pattern-not: "return $Y == null;"
      - pattern-not: "return $Y === null;"
      - pattern-not: "return $Y != null;"
      - pattern-not: "return $Y !== null;"
      - pattern-not: "return $Y == undefined;"
      - pattern-not: "return $Y === undefined;"
      - pattern-not: "return $Y != undefined;"
      - pattern-not: "return $Y !== undefined;"
      - pattern-either:
          - pattern: |
              if (password == $X) {
                  ...
              }
          - pattern: |
              if ($X == password) {
                  ...
              }
          - pattern: |
              if (password === $X) {
                  ...
              }
          - pattern: |
              if ($X === password) {
                  ...
              }
          - pattern: |
              if (pass == $X) {
                  ...
              }
          - pattern: |
              if ($X == pass) {
                  ...
              }
          - pattern: |
              if (pass === $X) {
                  ...
              }
          - pattern: |
              if ($X === pass) {
                  ...
              }
          - pattern: |
              if (secret == $X) {
                  ...
              }
          - pattern: |
              if ($X == secret) {
                  ...
              }
          - pattern: |
              if (secret === $X) {
                  ...
              }
          - pattern: |
              if ($X === secret) {
                  ...
              }
          - pattern: |
              if (api == $X) {
                  ...
              }
          - pattern: |
              if ($X == api) {
                  ...
              }
          - pattern: |
              if (api === $X) {
                  ...
              }
          - pattern: |
              if ($X === api) {
                  ...
              }
          - pattern: |
              if (apiKey == $X) {
                  ...
              }
          - pattern: |
              if ($X == apiKey) {
                  ...
              }
          - pattern: |
              if (apiKey === $X) {
                  ...
              }
          - pattern: |
              if ($X === apiKey) {
                  ...
              }
          - pattern: |
              if (apiSecret == $X) {
                  ...
              }
          - pattern: |
              if ($X == apiSecret) {
                  ...
              }
          - pattern: |
              if (apiSecret === $X) {
                  ...
              }
          - pattern: |
              if ($X === apiSecret) {
                  ...
              }
          - pattern: |
              if (token == $X) {
                  ...
              }
          - pattern: |
              if ($X == token) {
                  ...
              }
          - pattern: |
              if (token === $X) {
                  ...
              }
          - pattern: |
              if ($X === token) {
                  ...
              }
          - pattern: |
              if (hash == $X) {
                  ...
              }
          - pattern: |
              if ($X == hash) {
                  ...
              }
          - pattern: |
              if (hash === $X) {
                  ...
              }
          - pattern: |
              if ($X === hash) {
                  ...
              }
          - pattern: |
              if (auth_token == $X) {
                  ...
              }
          - pattern: |
              if ($X == auth_token) {
                  ...
              }
          - pattern: |
              if (auth_token === $X) {
                  ...
              }
          - pattern: |
              if ($X === auth_token) {
                  ...
              }
          - pattern: |
              if (password != $X) {
                  ...
              }
          - pattern: |
              if ($X != password) {
                  ...
              }
          - pattern: |
              if (password !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== password) {
                  ...
              }
          - pattern: |
              if (pass != $X) {
                  ...
              }
          - pattern: |
              if ($X != pass) {
                  ...
              }
          - pattern: |
              if (pass !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== pass) {
                  ...
              }
          - pattern: |
              if (secret != $X) {
                  ...
              }
          - pattern: |
              if ($X != secret) {
                  ...
              }
          - pattern: |
              if (secret !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== secret) {
                  ...
              }
          - pattern: |
              if (api != $X) {
                  ...
              }
          - pattern: |
              if ($X != api) {
                  ...
              }
          - pattern: |
              if (api !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== api) {
                  ...
              }
          - pattern: |
              if (apiKey != $X) {
                  ...
              }
          - pattern: |
              if ($X != apiKey) {
                  ...
              }
          - pattern: |
              if (apiKey !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== apiKey) {
                  ...
              }
          - pattern: |
              if (apiSecret != $X) {
                  ...
              }
          - pattern: |
              if ($X != apiSecret) {
                  ...
              }
          - pattern: |
              if (apiSecret !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== apiSecret) {
                  ...
              }
          - pattern: |
              if (token != $X) {
                  ...
              }
          - pattern: |
              if ($X != token) {
                  ...
              }
          - pattern: |
              if (token !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== token) {
                  ...
              }
          - pattern: |
              if (hash != $X) {
                  ...
              }
          - pattern: |
              if ($X != hash) {
                  ...
              }
          - pattern: |
              if (hash !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== hash) {
                  ...
              }
          - pattern: |
              if (auth_token != $X) {
                  ...
              }
          - pattern: |
              if ($X != auth_token) {
                  ...
              }
          - pattern: |
              if (auth_token !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== auth_token) {
                  ...
              }
          - pattern: |
              return $X === auth_token;
          - pattern: |
              return auth_token === $X;
          - pattern: |
              return $X === token;
          - pattern: |
              return token === $X;
          - pattern: |
              return $X === hash;
          - pattern: |
              return hash === $X;
          - pattern: |
              return $X === password;
          - pattern: |
              return password === $X;
          - pattern: |
              return $X === pass;
          - pattern: |
              return pass === $X;
          - pattern: |
              return $X === apiKey;
          - pattern: |
              return apiKey === $X;
          - pattern: |
              return $X === apiSecret;
          - pattern: |
              return apiSecret === $X;
          - pattern: |
              return $X === api_key;
          - pattern: |
              return api_key === $X;
          - pattern: |
              return $X === api_secret;
          - pattern: |
              return api_secret === $X;
          - pattern: |
              return $X === secret;
          - pattern: |
              return secret === $X;
          - pattern: |
              return $X === api;
          - pattern: |
              return api === $X;
          - pattern: |
              return $X == auth_token;
          - pattern: |
              return auth_token == $X;
          - pattern: |
              return $X == token;
          - pattern: |
              return token == $X;
          - pattern: |
              return $X == hash;
          - pattern: |
              return hash == $X;
          - pattern: |
              return $X == password;
          - pattern: |
              return password == $X;
          - pattern: |
              return $X == pass;
          - pattern: |
              return pass == $X;
          - pattern: |
              return $X == apiKey;
          - pattern: |
              return apiKey == $X;
          - pattern: |
              return $X == apiSecret;
          - pattern: |
              return apiSecret == $X;
          - pattern: |
              return $X == api_key;
          - pattern: |
              return api_key == $X;
          - pattern: |
              return $X == api_secret;
          - pattern: |
              return api_secret == $X;
          - pattern: |
              return $X == secret;
          - pattern: |
              return secret == $X;
          - pattern: |
              return $X == api;
          - pattern: |
              return api == $X;
          - pattern: |
              return $X !== auth_token;
          - pattern: |
              return auth_token !== $X;
          - pattern: |
              return $X !== token;
          - pattern: |
              return token !== $X;
          - pattern: |
              return $X !== hash;
          - pattern: |
              return hash !== $X;
          - pattern: |
              return $X !== password;
          - pattern: |
              return password !== $X;
          - pattern: |
              return $X !== pass;
          - pattern: |
              return pass !== $X;
          - pattern: |
              return $X !== apiKey;
          - pattern: |
              return apiKey !== $X;
          - pattern: |
              return $X !== apiSecret;
          - pattern: |
              return apiSecret !== $X;
          - pattern: |
              return $X !== api_key;
          - pattern: |
              return api_key !== $X;
          - pattern: |
              return $X !== api_secret;
          - pattern: |
              return api_secret !== $X;
          - pattern: |
              return $X !== secret;
          - pattern: |
              return secret !== $X;
          - pattern: |
              return $X !== api;
          - pattern: |
              return api !== $X;
          - pattern: |
              return $X != auth_token;
          - pattern: |
              return auth_token != $X;
          - pattern: |
              return $X != token;
          - pattern: |
              return token != $X;
          - pattern: |
              return $X != hash;
          - pattern: |
              return hash != $X;
          - pattern: |
              return $X != password;
          - pattern: |
              return password != $X;
          - pattern: |
              return $X != pass;
          - pattern: |
              return pass != $X;
          - pattern: |
              return $X != apiKey;
          - pattern: |
              return apiKey != $X;
          - pattern: |
              return $X != apiSecret;
          - pattern: |
              return apiSecret != $X;
          - pattern: |
              return $X != api_key;
          - pattern: |
              return api_key != $X;
          - pattern: |
              return $X != api_secret;
          - pattern: |
              return api_secret != $X;
          - pattern: |
              return $X != secret;
          - pattern: |
              return secret != $X;
          - pattern: |
              return $X != api;
          - pattern: |
              return api != $X;
    severity: "WARNING"
    message: |
      The application was found executing string comparisons using one of `===`, `!==`, `==` or `!=`
      against security sensitive values. String comparisons like this are not constant time, meaning
      the
      first character found not to match in the two strings will immediately exit the conditional
      statement.
      This allows an adversary to calculate or observe small timing differences depending on the
      strings
      passed to this comparison. This potentially allows an adversary the ability to brute force a
      string
      that will match the expected value by monitoring different character values.

      To remediate this issue, use the `crypto.timingSafeEqual` method when comparing strings.

      Example using `crypto.timingSafeEqual` to safely compare strings:
      ```
      function constantTimeIsPasswordEqual(userInput) {
          // Retrieve the password from a secure data store such as a KMS or Hashicorp's vault.
          const password = getPasswordFromSecureDataStore();
          // Use crypto timingSafeEqual to ensure the comparison is done in constant time.
          return crypto.timingSafeEqual(Buffer.from(userInput, 'utf-8'), Buffer.from(password,
      'utf-8'));
      }
      ```

      For more information on constant time comparison see:
      - https://nodejs.org/api/crypto.html#crypto_crypto_timingsafeequal_a_b
    metadata:
      cwe: "CWE-208"
      shortDescription: "Observable timing discrepancy"
      category: "security"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      related-packages: ['pkg:npm/crypto']
      license: MIT (c) GitLab Inc.
  - id: "javascript_xss_rule-mustache-escape"
    languages:
      - "javascript"
      - "typescript"
    patterns:
      - pattern-inside: |
          $OBJ = require('mustache')
          ...
      - pattern-either:
          - pattern: |
              $OBJ.escape = function($TEXT) {
                ...
                return $TEXT;
              }
          - patterns:
              - metavariable-regex:
                  metavariable: "$X"
                  regex: "\"\\{\\{\\{(.+?)\\}\\}\\}\""
              - pattern: "$OBJ.render($X, ... )"
          - patterns:
              - metavariable-regex:
                  metavariable: "$Y"
                  regex: "\"\\{\\{\\&(.+?)\\}\\}\""
              - pattern: "$OBJ.render($Y, ... )"
    severity: "WARNING"
    message: |
      Markup escaping disabled. This can be used with some template engines to escape
      disabling of HTML entities, which can lead to XSS attacks.
    metadata:
      cwe: "CWE-79"
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      category: "security"
      shortDescription: "Improper neutralization of input during web page generation (XSS)"
    # yamllint enable
      security-severity: "Medium"
      related-packages: ['pkg:npm/mustache']
      license: MIT (c) GitLab Inc.
  - id: "kotlin_cookie_rule-CookieHTTPOnly"
    languages:
      - "kotlin"
    message: |
      A new cookie is created without the HttpOnly flag set. The HttpOnly flag is a directive to the
      browser to make sure that the cookie can not be red by malicious script. When a user is the
      target of a "Cross-Site Scripting", the attacker would benefit greatly from getting the session
      id for example.
    severity: "WARNING"
    metadata:
      shortDescription: "Sensitive cookie without 'HttpOnly' flag"
      category: "security"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-1004"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: GNU Lesser General Public License v3.0
    patterns:
      - pattern: |
          $C = $X.servlet.http.Cookie(..., ...)
          ...;
          ($RESP: $X.servlet.http.HttpServletResponse).addCookie($C)
      - pattern-not-inside: |
          $C = $X.servlet.http.Cookie(..., ...)
          ...
          $C.setHttpOnly(true)
          ...;
          ($RESP: $X.servlet.http.HttpServletResponse).addCookie($C)
  - id: "kotlin_cookie_rule-CookieInsecure"
    languages:
      - "kotlin"
    message: |
      "A new cookie is created without the Secure flag set. The Secure flag is a
       directive to the browser to make sure that the cookie is not sent for insecure communication
      (http://)"
    severity: "WARNING"
    metadata:
      shortDescription: "Sensitive cookie in HTTPS session without 'Secure' attribute"
      category: "security"
      cwe: "CWE-614"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"


      license: GNU Lesser General Public License v3.0
    patterns:
      - pattern: |
          $C = $X.servlet.http.Cookie(..., ...)
          ...;
          ($RESP: $X.servlet.http.HttpServletResponse).addCookie($C)
      - pattern-not-inside: |
          $C = $X.servlet.http.Cookie(..., ...)
          ...
          $C.setSecure(true)
          ...;
          ($RESP: $X.servlet.http.HttpServletResponse).addCookie($C)
  - id: "kotlin_cookie_rule-HttpResponseSplitting"
    languages:
      - "kotlin"
    message: |
      When an HTTP request contains unexpected CR and LF characters, the server may respond with an
      output stream that is interpreted as two different HTTP responses (instead of one). An attacker
      can control the second response and mount attacks such as cross-site scripting and cache
      poisoning attacks.
    severity: "WARNING"
    metadata:
      shortDescription: "Improper neutralization of CRLF sequences in HTTP headers ('HTTP Response Splitting')"
      category: "security"
      cwe: "CWE-113"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: GNU Lesser General Public License v3.0
    mode: "taint"
    pattern-sanitizers:
      - patterns:
          # a string that might be empty
          - metavariable-pattern:
              metavariable: "$S0"
              pattern-either:
                - pattern: "..."
                - pattern: '""'
          # replace pattern should include a character class with \r and \n
          - metavariable-pattern:
              metavariable: "$PATTERN"
              patterns:
                - pattern: "..."
                - pattern-regex: ".*\\[\\]?(?=[^]]*\\\\r)(?=[^]]*\\\\n)[^]]*\\]\\+"
          - pattern-inside: |
              $STR.replace($PATTERN, $S0)
              ...
      - pattern: "org.apache.commons.text.StringEscapeUtils.escapeJava(...)"
    pattern-sinks:
      - pattern: 'javax.servlet.http.Cookie("$KEY", ...)'
      - patterns:
          - pattern-inside: |
              $C = javax.servlet.http.Cookie("$KEY", ...)
              ...
          - pattern: "$C.setValue(...)"
    pattern-sources:
      - pattern: "($REQ: javax.servlet.http.HttpServletRequest).getParameter(...)"
      - pattern: "($REQ: javax.servlet.http.HttpServletRequest).getParameterNames()"
      - pattern: "($REQ: javax.servlet.http.HttpServletRequest).getParameterValues(...)"
      - pattern: "($REQ: javax.servlet.http.HttpServletRequest).getParameterMap()"
      - pattern: "($REQ: javax.servlet.http.HttpServletRequest).getHeader(...)"
      - pattern: "($REQ: javax.servlet.http.HttpServletRequest).getPathInfo()"
  - id: "kotlin_cookie_rule-RequestParamToHeader"
    languages:
      - "kotlin"
    message: |
      This code directly writes an HTTP parameter to an HTTP header, which allows for a HTTP
      response splitting vulnerability. See http://en.wikipedia.org/wiki/HTTP_response_splitting for
      more information.
    severity: "ERROR"
    metadata:
      shortDescription: "Improper neutralization of CRLF sequences in HTTP headers ('HTTP Response Splitting')"
      category: "security"
      cwe: "CWE-113"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "kotlin"
      security-severity: "CRITICAL"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: GNU Lesser General Public License v3.0
    mode: "taint"
    pattern-sanitizers:
      - patterns:
          # a string that might be empty
          - metavariable-pattern:
              metavariable: "$S0"
              pattern-either:
                - pattern: "..."
                - pattern: '""'
          # replace pattern should include a character class with \r and \n
          - metavariable-pattern:
              metavariable: "$PATTERN"
              patterns:
                - pattern: "..."
                - pattern-regex: ".*\\[\\]?(?=[^]]*\\\\r)(?=[^]]*\\\\n)[^]]*\\]\\+"
          - pattern-inside: |
              $STR.replace($PATTERN, $S0);
              ...
      - pattern: "org.apache.commons.text.StringEscapeUtils.unescapeJava(...);"
    pattern-sinks:
      - pattern: '($RES: $X.servlet.http.HttpServletResponse).setHeader("$KEY", ...);'
      - pattern: '($RES: $X.servlet.http.HttpServletResponse).addHeader("$KEY", ...);'
      - pattern: '($WRP: $X.servlet.http.HttpServletResponseWrapper).setHeader("$KEY", ...);'
      - pattern: '($WRP: $X.servlet.http.HttpServletResponseWrapper).addHeader("$KEY", ...);'
    pattern-sources:
      - pattern: "($REQ: $X.servlet.http.HttpServletRequest).getParameter(...);"
      - pattern: "($REQ: $X.servlet.http.HttpServletRequest).getParameterNames();"
      - pattern: "($REQ: $X.servlet.http.HttpServletRequest).getParameterValues(...);"
      - pattern: "($REQ: $X.servlet.http.HttpServletRequest).getParameterMap();"
      - pattern: "($REQ: $X.servlet.http.HttpServletRequest).getHeader(...);"
      - pattern: "($REQ: $X.servlet.http.HttpServletRequest).getPathInfo();"
  - id: "kotlin_cors_rule-PermissiveCORSInjection"
    languages:
      - "kotlin"
    message: |
      Prior to HTML5, Web browsers enforced the Same Origin Policy which ensures that in order for
      JavaScript to access the contents of a Web page, both the JavaScript and the Web page must
      originate from the same domain. Without the Same Origin Policy, a malicious website could serve
      up JavaScript that loads sensitive information from other websites using a client's
      credentials, cull through it, and communicate it back to the attacker. HTML5 makes it possible
      for JavaScript to access data across domains if a new HTTP header called
      Access-Control-Allow-Origin is defined. With this header, a Web server defines which other
      domains are allowed to access its domain using cross-origin requests. However, caution should
      be taken when defining the header because an overly permissive CORS policy will allow a
      malicious application to communicate with the victim application in an inappropriate way,
      leading to spoofing, data theft, relay and other attacks.
    severity: "ERROR"
    metadata:
      shortDescription: "Permissive cross-domain policy with untrusted domains"
      cwe: "CWE-942"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "kotlin"
      security-severity: "CRITICAL"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: GNU Lesser General Public License v3.0
    mode: "taint"
    pattern-sources:
      - pattern: "($REQ: HttpServletRequest).getParameter(...)"
      - pattern: "($REQ: HttpServletRequest).getHeader(...)"
      - pattern: "($REQ: HttpServletRequest).getPathInfo()"
      - pattern: "($REQ: HttpServletRequest).getQueryString()"
      - pattern: "($REQ: HttpServletRequest).getAttribute(...)"
      - pattern: "($REQ: HttpServletRequest).getSession().getAttribute(...)"
      - pattern: "($REQ: HttpServletRequest).getServletContext().getAttribute(...)"
      - pattern: "($REQ: HttpServletRequest).getParameterValues(...)"
      - pattern: "($REQ: HttpServletRequest).getParameterNames()"
      - pattern: "($REQ: HttpServletRequest).getParameterMap()"
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern: '($RES: HttpServletResponse).setHeader("$HEADER", ...)'
              - pattern: '($RES: HttpServletResponse).addHeader("$HEADER", ...)'
          - metavariable-regex:
              metavariable: "$HEADER"
              regex: "(?i)(Access-Control-Allow-Origin)"
  - id: "kotlin_crypto_rule-BlowfishKeySize"
    languages:
      - "kotlin"
    message: |
      A small key size makes the ciphertext vulnerable to brute force attacks. At least 128 bits of
      entropy should be used when generating the key if use of Blowfish is required.
    severity: "WARNING"
    metadata:
      category: "security"
      shortDescription: "Inadequate encryption strength"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-326"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"


      license: GNU Lesser General Public License v3.0
    patterns:
      - pattern-inside: |
          $KEYGEN = javax.crypto.KeyGenerator.getInstance("Blowfish", ...);
          ...
          $KEYGEN.init($KEY_SIZE);
      - metavariable-comparison:
          comparison: "$KEY_SIZE < 128"
          metavariable: "$KEY_SIZE"
  - id: "kotlin_crypto_rule-CipherDESInsecure"
    languages:
      - "kotlin"
    message: |
      DES is considered strong ciphers for modern applications. Currently, NIST recommends the usage
      of AES block ciphers instead of DES.
    severity: "WARNING"
    metadata:
      shortDescription: "Inadequate encryption strength"
      category: "security"
      cwe: "CWE-326"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"

      license: GNU Lesser General Public License v3.0
    patterns:
      - pattern-inside: |-
          javax.crypto.Cipher.getInstance($TRANSFORMATION, ...)
      - metavariable-regex:
          metavariable: "$TRANSFORMATION"
          regex: '^"DES(/|"$)'
  - id: "kotlin_crypto_rule-CipherDESedeInsecure"
    languages:
      - "kotlin"
    message: |
      Triple DES (also known as 3DES or DESede) is considered strong ciphers for modern
      applications. NIST recommends the usage of AES block ciphers instead of 3DES.
    severity: "WARNING"
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      category: "security"
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"

      license: GNU Lesser General Public License v3.0
    patterns:
      - pattern-inside: |-
          javax.crypto.Cipher.getInstance($ALG, ...)
      - metavariable-regex:
          metavariable: "$ALG"
          regex: '(?i)^"DESede(/|"$)'
  - id: "kotlin_crypto_rule-CipherECBMode"
    languages:
      - "kotlin"
    message: |
      An authentication cipher mode which provides better confidentiality of the encrypted data
      should be used instead of Electronic Code Book (ECB) mode, which does not provide good
      confidentiality. Specifically, ECB mode produces the same output for the same input each time.
      This allows an attacker to intercept and replay the data.
    metadata:
      category: "security"
      cwe: "CWE-326"
      shortDescription: "Inadequate Encryption Strength"
      technology:
        - "kotlin"
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"

    patterns:
      - pattern: javax.crypto.Cipher.getInstance($TRANSFORMATION, ...)
      - metavariable-regex:
          metavariable: "$TRANSFORMATION"
          regex: '^"[^/]*/ECB(/.*)?"$'
  - id: "kotlin_crypto_rule-CipherIntegrity"
    languages:
      - "kotlin"
    message: |
      The ciphertext produced is susceptible to alteration by an adversary. This mean that the
      cipher provides no way to detect that the data has been tampered with. If the ciphertext can be
      controlled by an attacker, it could be altered without detection.
    severity: "ERROR"
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      category: "security"
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      technology:
        - "kotlin"
      security-severity: "CRITICAL"

      license: GNU Lesser General Public License v3.0
    patterns:
      - pattern: |
          javax.crypto.Cipher.getInstance($TRANSFORMATION, ...)
      - metavariable-pattern:
          metavariable: "$TRANSFORMATION"
          patterns:
            # any algorithm with either no mode or CBC, OFB, CTR, or ECB and any padding
            - pattern-regex: '^"[^/]*(/(CBC|OFB|CTR|ECB)(/.*)?)?"$'
            # ECIES and RSA algorithms are ok
            - pattern-not-regex: '^"(ECIES|RSA)(/|"$)'
  - id: "kotlin_crypto_rule-CipherPaddingOracle"
    languages:
      - "kotlin"
    message: |
      This specific mode of CBC with PKCS5Padding is susceptible to padding oracle attacks. An
      adversary could potentially decrypt the message if the system exposed the difference between
      plaintext with invalid padding or valid padding. The distinction between valid and invalid
      padding is usually revealed through distinct error messages being returned for each condition.
    severity: "ERROR"
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      category: "security"
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      technology:
        - "java"
      security-severity: "CRITICAL"

      license: GNU Lesser General Public License v3.0
    patterns:
      - pattern: |-
          javax.crypto.Cipher.getInstance($TRANSFORMATION)
      - metavariable-regex:
          metavariable: "$TRANSFORMATION"
          regex: '^"[^/]*/CBC/PKCS5Padding'
      - metavariable-pattern:
          metavariable: "$TRANSFORMATION"
          patterns:
            - pattern-not-regex: '^"(RSA|ECIES)/'
  - id: "kotlin_crypto_rule-CustomMessageDigest"
    languages:
      - "kotlin"
    message: |
      Implementing a custom MessageDigest is error-prone. National Institute of Standards and
      Technology(NIST) recommends the use of SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, or
      SHA-512/256.
    severity: "WARNING"
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      category: "security"
      cwe: "CWE-327"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A04:2021-Insecure Design"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"

      license: GNU Lesser General Public License v3.0
    pattern: |
      class $CLAZZ : java.security.MessageDigest(...) {
        ...
      }
  - id: "kotlin_crypto_rule-HazelcastSymmetricEncryption"
    languages:
      - "kotlin"
    message: |
      The network communications for Hazelcast is configured to use a symmetric cipher (probably DES
      or Blowfish). Those ciphers alone do not provide integrity or secure authentication. The use of
      asymmetric encryption is preferred.
    severity: "WARNING"
    metadata:
      shortDescription: "Inadequate encryption strength"
      category: "security"
      cwe: "CWE-326"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"

      license: GNU Lesser General Public License v3.0
    pattern: "com.hazelcast.config.SymmetricEncryptionConfig()"
  - id: "kotlin_crypto_rule-InsufficientKeySizeRsa"
    languages:
      - "kotlin"
    message: |
      Detected an insufficient key size for DSA. NIST recommends a key size
      of 2048 or higher.
    metadata:
      shortDescription: "Inadequate encryption strength"
      category: "security"
      cwe: "CWE-326"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "MEDIUM"
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"

    patterns:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  $GEN = KeyPairGenerator.getInstance($ALG, ...);
                  ...
              - pattern-either:
                  - pattern: "$VAR.initialize($SIZE, ...);"
                  - pattern: "java.security.spec.RSAKeyGenParameterSpec($SIZE,...);"
              - metavariable-comparison:
                  comparison: "$SIZE < 2048"
                  metavariable: "$SIZE"
              - metavariable-regex:
                  metavariable: "$ALG"
                  regex: '"(RSA|DSA)"'
  - id: "kotlin_crypto_rule-NullCipher"
    languages:
      - "kotlin"
    message: |
      The NullCipher implements the Cipher interface by returning ciphertext identical to the
      supplied plaintext. In a few contexts, such as testing, a NullCipher may be appropriate. Avoid
      using the NullCipher. Its accidental use can introduce a significant confidentiality risk.
    severity: "WARNING"
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      category: "security"
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"

      license: GNU Lesser General Public License v3.0
    pattern: "javax.crypto.NullCipher()"
  - id: "kotlin_crypto_rule-RsaNoPadding"
    languages:
      - "kotlin"
    message: |
      The software uses the RSA algorithm but does not incorporate Optimal Asymmetric
      Encryption Padding (OAEP), which might weaken the encryption.
    metadata:
      shortDescription: "Use of RSA algorithm without OAEP"
      category: "security"
      cwe: "CWE-780"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "MEDIUM"
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"

    pattern-either:
      - patterns:
          - pattern: |
              $VAR = "$ALG";
              ...
              javax.crypto.Cipher.getInstance($VAR);
          - metavariable-regex:
              metavariable: "$ALG"
              regex: ".*RSA.*NoPadding.*"
      - patterns:
          - pattern: "javax.crypto.Cipher.getInstance($ALG,...);"
          - metavariable-regex:
              metavariable: "$ALG"
              regex: ".*RSA.*NoPadding.*"
  - id: "kotlin_crypto_rule-WeakMessageDigest"
    languages:
      - "kotlin"
    message: |
      DES is considered strong ciphers for modern applications. Currently, NIST recommends the usage
      of AES block ciphers instead of DES.
    severity: "WARNING"
    metadata:
      shortDescription: "Use of a broken or risky cryptographic algorithm (SHA1/MD5)"
      category: "security"
      cwe: "CWE-327"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A04:2021-Insecure Design"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"

      license: GNU Lesser General Public License v3.0
    patterns:
      - pattern-either:
          - pattern: "MessageDigest.getInstance($ALG, ...)"
          - pattern: "Signature.getInstance($ALG, ...)"
      - metavariable-regex:
          metavariable: "$ALG"
          regex: ".*(MD5|MD4|MD2|SHA1|SHA-1).*"
  - id: "kotlin_crypto_rule-WeakTLSProtocol"
    languages:
      - "kotlin"
    message: |
      A HostnameVerifier that accept any host are often use because of certificate
      reuse on many hosts. As a consequence, this is vulnerable to Man-in-the-middleattacks
      attacks since the client will trust any certificate.
    metadata:
      shortDescription: "Improper certificate validation"
      category: "security"
      cwe: "CWE-295"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "MEDIUM"
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"

    patterns:
      - pattern-either:
          - pattern: "org.apache.http.impl.client.DefaultHttpClient()"
          - pattern: "javax.net.ssl.SSLContext.getInstance(\"SSL\")"
  - id: "kotlin_crypto_rule-WeakTLSProtocolVersion"
    languages:
      - "kotlin"
    message: |
      The application was found enabling insecure TLS protocol versions. When enabling protocol
      versions for an `SSLContext`, only the following versions should be allowed:
      - TLSv1.2
      - TLSv1.3
      - DTLSv1.2
      - DTLSv1.3

      To mitigate potential security risks, it is strongly advised to enforce TLS 1.2 as the minimum
      protocol version and disallow older versions such as TLS 1.0. Do note that newer versions of
      Java do not even support TLS 1.0 and will throw `NoSuchAlgorithmException`. Versions of TLS
      prior to 1.2 could expose the connection to downgrade attacks, where an adversary intercepts
      the
      connection and alters the requested protocol version to be a less secure one.

      In many scenarios, relying on the default system configuration does not meet compliance
      standards. This is due to the application being deployed across diverse systems with varying
      configurations and Java versions. While the default value may be secure on modern and
      up-to-date systems, it may not hold true for older systems. Consequently, it is highly
      recommended to explicitly define a secure configuration in all cases.

      Example configuring an SSLContext with TLSv1.2:
      ```
      // Create an SSLContext with TLSv1.2 explicitly
      SSLContext tlsContext = SSLContext.getInstance("TLSv1.2"); // or TLSv1.3, DTLSv1.2, DTLSv1.3

      // Alternatively, set the enabled protocols
      SSLContext serverSslContext = SSLContext.getInstance("TLS");
      SSLEngine serverEngine = serverSslContext.createSSLEngine();
      // Calling setEnabledProtocols will override the original context's configured protocol version
      serverEngine.setEnabledProtocols(new String[]{ "TLSv1.2" });
      ```

      For more information on `SSLContext` see:
      - https://docs.oracle.com/en/java/javase/11/docs/api/java.base/javax/net/ssl/SSLContext.html

      For more information on MiTM attacks see:
      - https://owasp.org/www-community/attacks/Manipulator-in-the-middle_attack
    metadata:
      shortDescription: "Inadequate encryption strength"
      category: "security"
      cwe: "CWE-326"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "MEDIUM"
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"

    patterns:
      - pattern-either:
          - pattern-inside: |
              import javax.net.ssl.*;
              ...
          - pattern-inside: |
              import javax.net.ssl.SSLContext;
              ...
      - pattern-either:
          - pattern-inside: |
              SSLContext.getInstance("$UNSAFE_VERSION");
          - pattern-inside: |
              SSLContext.getInstance(...);
              ...
              $ENGINE.setEnabledProtocols(arrayOf(...,"$UNSAFE_VERSION",...));
      - pattern-not:
          patterns:
            - pattern-inside: |
                $C = SSLContext.getInstance(...);
                ...
                $ENGINE.setEnabledProtocols(arrayOf(...,"$DT_GOODNESS",...));
            - metavariable-regex:
                metavariable: "$DT_GOODNESS"
                regex: "^D?TLSv1\\.[23]$"
      - pattern-not:
          patterns:
            - pattern-inside: |
                $C = SSLContext.getInstance(...);
                ...
                $E = $C.createSSLEngine()
                ...
                $E.enabledProtocols = arrayOf(...,"$DT_GOODNESS",...)
            - metavariable-regex:
                metavariable: "$DT_GOODNESS"
                regex: "^DTLSv1\\.[23]$"
      - metavariable-regex:
          metavariable: "$UNSAFE_VERSION"
          regex: "^(TLS|(D)?TLSv1.(0|1))$"
  - id: "kotlin_csrf_rule-SpringCSRFDisabled"
    languages:
      - "kotlin"
    message: |
      The application fails to protect against Cross-Site Request Forgery (CSRF)
      due to disabling Spring's CSRF protection features.
    metadata:
      shortDescription: "Cross-Site Request Forgery (CSRF)"
      category: "security"
      cwe: "CWE-352"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/org.springframework.security/spring-security-web']
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"

    pattern-either:
      - pattern: "($H: org.springframework.security.config.annotation.web.builders.HttpSecurity). ... .csrf().disable()"
      # - pattern: "($C: org.springframework.security.config.annotation.web.configurers.CsrfConfigurer<HttpSecurity>).disable()"
      - pattern: "($C: CsrfConfigurer<HttpSecurity>).disable()"
  - id: "kotlin_endpoint_rule-UnvalidatedRedirect"
    languages:
      - "kotlin"
    message: |
      Unvalidated redirects occur when an application redirects a user to a
      destination URL specified by a user supplied parameter that is not validated.
      Such vulnerabilities can be used to facilitate phishing attacks.
    metadata:
      category: "security"
      cwe: "CWE-601"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      shortDescription: "URL redirection to untrusted site ('Open Redirect')"
      security-severity: "CRITICAL"
      related-packages: ['pkg:maven/javax.servlet/javax.servlet-api']
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"

    mode: "taint"
    pattern-sources:
      - patterns:
          - pattern: |
              $URL = ($REQ: $X.servlet.http.HttpServletRequest).$M(...);
          - metavariable-regex:
              metavariable: "$M"
              regex: "(getParameter|getCookies|getHeader|getHeaders|getHeaderNames|getPathInfo|getPathTranslated|getContextPath|getQueryString|getRemoteUser|getRequestedSessionId|getRequestURI|getRequestURL|getServletPath|getParts|getPart|getReader)"
    pattern-sinks:
      - pattern-either:
          - pattern: |
              ($RES: $X.servlet.http.HttpServletResponse).sendRedirect($URL)
          - pattern: |
              ($RES: $X.servlet.http.HttpServletResponse).addHeader("Location", $URL)
    pattern-sanitizers:
      - patterns:
          - pattern-inside: |
              if ($SAFE.contains($URL)){
                ...
              }
          - pattern-either:
              - pattern: |
                  ($RES: $X.servlet.http.HttpServletResponse).sendRedirect($URL)
              - pattern: |
                  ($RES: $X.servlet.http.HttpServletResponse).addHeader("Location", $URL)
  - id: "kotlin_endpoint_rule-WeakHostNameVerification"
    languages:
      - "kotlin"
    message: |
      A HostnameVerifier that accept any host are often use because of certificate
      reuse on many hosts. As a consequence, this is vulnerable to Man-in-the-middle
      attacks since the client will trust any certificate.
    metadata:
      category: "security"
      cwe: "CWE-295"
      shortDescription: "Improper Certificate Validation"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/javax.net.ssl/javax.net.ssl-api']
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"

    patterns:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  class $V : HostnameVerifier {
                    ...
                  }
              - pattern: |
                  fun verify(...): Boolean {
                    return true
                  }
          - patterns:
              - pattern-inside: |
                  class $V : X509TrustManager {
                    ...
                  }
              - pattern-either:
                  - pattern: "fun checkClientTrusted(...) {}"
                  - pattern: "fun checkServerTrusted(...) {}"
                  - pattern: "fun getAcceptedIssuers(): Array<X509Certificate>? {return null}"
  - id: "kotlin_file_rule-FileUploadFileName"
    languages:
      - "kotlin"
    message: |
      The filename provided by the FileUpload API can be tampered with by the client to reference
      unauthorized files. The provided filename should be properly validated to ensure it's properly
      structured, contains no unauthorized path characters (e.g., / \), and refers to an authorized
      file.
    metadata:
      category: "security"
      cwe: "CWE-22"
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "kotlin"
      security-severity: "CRITICAL"
      related-packages: ['pkg:maven/org.apache.commons/commons-fileupload']
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"

    pattern-either:
      - patterns:
          - pattern-inside: |
              $FILES = ($SFU: ServletFileUpload).parseRequest(($REQ: $X.servlet.http.HttpServletRequest?))
              ...
              for($ITEM in $FILES) {
                ...
              }
          - pattern: $ITEM.getName()
      - pattern: "($PART: $X.servlet.http.Part).getSubmittedFileName()"
  - id: "kotlin_file_rule-FilenameUtils"
    languages:
      - "kotlin"
    message: |
      A file is opened to read its content. The filename comes from an input
      parameter. If an unfiltered parameter is passed to this file API, files from an
      arbitrary filesystem location could be read.
    metadata:
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      cwe: "CWE-22"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "MEDIUM"
      technology:
        - "kotlin"
      category: "security"
      related-packages: ['pkg:maven/org.apache.commons/commons-io']
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"

    patterns:
      - pattern-inside: |
          import org.apache.commons.io.FilenameUtils
          ...
      - pattern-either:
          - pattern: "normalize(...)"
          - pattern: "getExtension(...)"
          - pattern: "isExtensions(...)"
          - pattern: "getName(...)"
          - pattern: "getBaseName(...)"
          - pattern: "org.apache.commons.io.FilenameUtils.normalize(...)"
          - pattern: "org.apache.commons.io.FilenameUtils.getExtension(...)"
          - pattern: "org.apache.commons.io.FilenameUtils.isExtensions(...)"
          - pattern: "org.apache.commons.io.FilenameUtils.getName(...)"
          - pattern: "org.apache.commons.io.FilenameUtils.getBaseName(...)"
  - id: "kotlin_inject_rule-CommandInjection"
    languages:
      - "kotlin"
    message: |
      The highlighted API is used to execute a system command. If unfiltered input is passed to this
      API, it can lead to arbitrary command execution.
    severity: "WARNING"
    metadata:
      shortDescription: "Improper neutralization of special elements used in an OS command ('OS Command Injection')"
      category: "security"
      cwe: "CWE-78"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/org.apache.commons/commons-exec']
      license: GNU Lesser General Public License v3.0
    pattern-either:
      - patterns:
          - pattern-inside: |
              fun $FUNC(..., $PARAM: String, ...) {
                ...
              }
          - pattern-inside: |
              $R = Runtime.getRuntime()
              ...
          - pattern-either:
              - pattern: "$R.exec(<...$PARAM...>,...)"
              - patterns:
                  - pattern-either:
                      - pattern: |
                          $CMDARR = arrayOf("$SHELL",...,<...$PARAM...>,...)
                          ...
                          $R.exec($CMDARR,...)
                      - pattern: '$R.exec(arrayOf("$SHELL",...,<...$PARAM...>,...), ...)'
                      - pattern: '$R.exec(java.util.String.format("...", ...,<...$PARAM...>,...))'
                      - pattern: "$R.exec(($A: String) + ($B: String))"
                  - metavariable-regex:
                      metavariable: "$SHELL"
                      regex: "(/.../)?(sh|bash|ksh|csh|tcsh|zsh)$"
          - pattern-not: '$R.exec("...","...","...",...)'
          - pattern-not: |
              $R.exec(arrayOf("...","...","...",...),...)
      - patterns:
          - pattern-inside: |
              fun $FUNC(..., $PARAM: String, ...) {
                ...
              }
          - pattern-inside: |
              $PB = ProcessBuilder()
              ...
          - pattern-either:
              - pattern: "$PB.command(<...$PARAM...>,...)"
              - patterns:
                  - pattern-inside: |-
                      $VAL = <...$PARAM...>; ...
                  - pattern: "$PB.command(<...$VAL...>,...)"
              - patterns:
                  - pattern-either:
                      - pattern: '$PB.command("$SHELL",...,<...$PARAM...>,...)'
                      - pattern: |
                          $CMDARR = java.util.Arrays.asList("$SHELL",...,<...$PARAM...>,...)
                          ...
                          $PB.command($CMDARR,...)
                      - pattern: '$PB.command(java.util.Arrays.asList("$SHELL",...,<...$PARAM...>,...),...)'
                      - pattern: '$PB.command(java.util.String.format("...", ...,<...$PARAM...>,...))'
                      - pattern: "$PB.command(($A: String) + ($B: String))"
                  - metavariable-regex:
                      metavariable: "$SHELL"
                      regex: "(/.../)?(sh|bash|ksh|csh|tcsh|zsh)$"
          - pattern-not: '$PB.command("...","...","...",...)'
          - pattern-not: |
              $PB.command(java.util.Arrays.asList("...","...","...",...))
  - id: "kotlin_inject_rule-ELInjection"
    languages:
      - "kotlin"
    message: |
      An expression is built with a dynamic value. The source of the value(s) should be verified to
      avoid that unfiltered values fall into this risky code evaluation.
    metadata:
      category: "security"
      cwe: "CWE-917"
      shortDescription: "Improper neutralization of special elements used in an expression language statement ('Expression
        Language Injection')"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/org.apache.el/el-api']
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"

    pattern-either:
      - patterns:
          - metavariable-regex:
              metavariable: "$METHOD"
              regex: "^create(Method|Value)Expression$"
          - metavariable-pattern:
              metavariable: "$EXPR"
              patterns:
                - pattern-not: '"..."'
          - pattern: |
              ($EXP: ExpressionFactory ).$METHOD(($CTX: $X.el.ELContext), $EXPR, ...)
      - patterns:
          - pattern: |
              ($P: $X.el.ELProcessor).$METHOD(...)
          - pattern-not: |
              ($P: $X.el.ELProcessor).$METHOD("...", ...)
          - metavariable-regex:
              metavariable: "$METHOD"
              regex: "^(eval|(get|set)Value)$"
  - id: "kotlin_inject_rule-FileDisclosure"
    languages:
      - "kotlin"
    message: |
      Constructing a server-side redirect path with user input could allow an
      attacker to download application binaries (including application classes or
      jar files) or view arbitrary files within protected directories.
    metadata:
      shortDescription: "Files or directories accessible to external parties"
      category: "security"
      cwe: "CWE-552"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      related-packages: ['pkg:maven/org.apache.commons/commons-fileupload']
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"

    mode: "taint"
    pattern-sinks:
      - patterns:
          - pattern: "org.springframework.web.servlet.ModelAndView($FST);"
          - pattern: "$FST"
      - patterns:
          - pattern: "org.springframework.web.servlet.ModelAndView($FST, $SND);"
          - pattern: "$FST"
      - patterns:
          - pattern: "org.springframework.web.servlet.ModelAndView($FST, $SND, $TRD);"
          - pattern: "$FST"
      - patterns:
          - pattern: "org.apache.struts.action.ActionForward($FST)"
          - pattern: "$FST"
      - patterns:
          - pattern: "org.apache.struts.action.ActionForward($FST, $SND)"
          - pattern: "$FST"
      - patterns:
          - pattern: "org.apache.struts.action.ActionForward($FST, $SND, $TRD)"
          - pattern: "$SND"
      - patterns:
          - pattern: "org.apache.struts.action.ActionForward($FST, $SND, $TRD)"
          - pattern: "$TRD"
      - patterns:
          - pattern-inside: |
              $ACTION = org.apache.struts.action.ActionForward();
              ...
          - pattern: "$ACTION.setPath(...)"
      - patterns:
          - pattern-inside: |
              $MVC = org.springframework.web.servlet.ModelAndView();
              ...
          - pattern: "$MVC.setViewName(...);"
      - patterns:
          - pattern-inside: |
              $REQ = $HTTP.getRequestDispatcher(...);
              ...
          - pattern-either:
              - pattern: "$REQ.include($FST, $SND)"
              - pattern: "$REQ.forward($FST, $SND)"
    pattern-sources:
      - pattern: "($VAR: javax.servlet.http.HttpServletRequest).getParameter(...)"
  - id: "kotlin_inject_rule-HttpParameterPollution"
    languages:
      - "kotlin"
    message: |
      Concatenating unvalidated user input into a URL can allow an attacker to override the value of
      a request parameter. Attacker may be able to override existing parameter values, inject a new
      parameter or exploit variables out of a direct reach. HTTP Parameter Pollution (HPP) attacks
      consist of injecting encoded query string delimiters into other existing parameters. If a web
      application does not properly sanitize the user input, a malicious user may compromise the
      logic of the application to perform either client-side or server-side attacks.
    severity: "ERROR"
    metadata:
      shortDescription: "Improper neutralization of argument delimiters in a command ('Argument Injection')"
      category: "security"
      cwe: "CWE-88"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "kotlin"
      security-severity: "CRITICAL"
      related-packages: ['pkg:maven/org.apache.httpcomponents.client5/httpclient5']
      license: GNU Lesser General Public License v3.0
    mode: "taint"
    pattern-sources:
      - pattern: "($REQ: HttpServletRequest).getParameter(...)"
    pattern-sanitizers:
      - pattern: "java.net.URLEncoder.encode(...)"
      - pattern: "com.google.common.net.UrlEscapers.urlPathSegmentEscaper().escape(...)"
    pattern-sinks:
      - pattern: "org.apache.http.client.methods.HttpGet(...)"
      - pattern: "org.apache.commons.httpclient.methods.GetMethod(...)"
      - pattern: "($GM: org.apache.commons.httpclient.methods.GetMethod).setQueryString(...)"
  - id: "kotlin_inject_rule-LDAPInjection"
    languages:
      - "kotlin"
    message: |
      Just like SQL, all inputs passed to an LDAP query need to be passed in safely. Unfortunately,
      LDAP doesn't have prepared statement interfaces like SQL. Therefore, the primary defense
      against LDAP injection is strong input validation of any untrusted data before including it in
      an LDAP query.
    severity: "WARNING"
    metadata:
      shortDescription: "Improper neutralization of special elements used in an LDAP query ('LDAP Injection')"
      category: "security"
      cwe: "CWE-90"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/com.unboundid/unboundid-ldapsdk']
      license: GNU Lesser General Public License v3.0

    mode: "taint"
    pattern-sinks:
      - pattern: "javax.naming.ldap.LdapName(...)"
      - pattern: "($C: javax.naming.directory.Context).lookup(...)"
      - pattern: "($C: javax.naming.Context).lookup(...)"
      - patterns:
          - pattern-inside: |-
              ($C: com.unboundid.ldap.sdk.LDAPConnection).search($QUERY, ...)
          - pattern: "$QUERY"
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern-either:
                      - pattern: "$CTX.lookup(...)"
                      - patterns:
                          - pattern-inside: |-
                              $CTX.search($QUERY, ...)
                          - pattern: "$QUERY"
                      - patterns:
                          - pattern-inside: |-
                              $CTX.search($NAME, $FILTER, ...)
                          - pattern: "$FILTER"
                  - metavariable-pattern:
                      metavariable: "$CTX"
                      pattern-either:
                        - pattern: "($C: DirContext)"
                        - pattern: "($IDC: InitialDirContext)"
                        - pattern: "($LC: LdapContext)"
                        - pattern: "($EDC: EventDirContext)"
                        - pattern: "($LC: LdapCtx)"
                        - pattern: "($C: javax.naming.directory.DirContext)"
                        - pattern: "($IDC: javax.naming.directory.InitialDirContext)"
                        - pattern: "($LC: javax.naming.ldap.LdapContext)"
                        - pattern: "($EDC: javax.naming.event.EventDirContext)"
                        - pattern: "($LC: com.sun.jndi.ldap.LdapCtx)"
              # repeat the previous rule with flow constraints on $CTX instead of type constraints
              - patterns:
                  - pattern-either:
                      - pattern: "$CTX.lookup(...)"
                      - patterns:
                          - pattern-inside: |-
                              $CTX.search($QUERY, ...)
                          - pattern: "$QUERY"
                      - patterns:
                          - pattern-inside: |-
                              $CTX.search($NAME, $FILTER, ...)
                          - pattern: "$FILTER"
                  - pattern-inside:
                      pattern-either:
                        - pattern: "$CTX = DirContext(...);..."
                        - pattern: "$CTX = InitialDirContext(...);..."
                        - pattern: "$CTX = LdapContext(...);..."
                        - pattern: "$CTX = EventDirContext(...);..."
                        - pattern: "$CTX = LdapCtx(...);..."
                        - pattern: "$CTX = javax.naming.directory.DirContext(...);..."
                        - pattern: "$CTX = javax.naming.directory.InitialDirContext(...);..."
                        - pattern: "$CTX = javax.naming.ldap.LdapContext(...);..."
                        - pattern: "$CTX = javax.naming.event.EventDirContext(...);..."
                        - pattern: "$CTX = com.sun.jndi.ldap.LdapCtx(...);..."
      - pattern-either:
          - patterns:
              - pattern-either:
                  - patterns:
                      - pattern-inside: |-
                          $CTX.list($QUERY, ...)
                      - pattern: "$QUERY"
                  - patterns:
                      - pattern-inside: |-
                          $CTX.lookup($QUERY, ...)
                      - pattern: "$QUERY"
                  - patterns:
                      - pattern-inside: |-
                          $CTX.search($QUERY, ...)
                      - pattern: "$QUERY"
                  - patterns:
                      - pattern-inside: |-
                          $CTX.search($NAME, $FILTER, ...)
                      - pattern: "$FILTER"
              - metavariable-pattern:
                  metavariable: "$CTX"
                  pattern-either:
                    - pattern: "($LT: LdapTemplate)"
                    - pattern: "($LO: LdapOperations)"
                    - pattern: "($LT: org.springframework.ldap.core.LdapTemplate)"
                    - pattern: "($LO: org.springframework.ldap.core.LdapOperations)"
          # again with flow constraints instead of type constraints on CTX
          - patterns:
              - pattern-either:
                  - patterns:
                      - pattern-inside: |-
                          $CTX.list($QUERY, ...)
                      - pattern: "$QUERY"
                  - patterns:
                      - pattern-inside: |-
                          $CTX.lookup($QUERY, ...)
                      - pattern: "$QUERY"
                  - patterns:
                      - pattern-inside: |-
                          $CTX.search($QUERY, ...)
                      - pattern: "$QUERY"
                  - patterns:
                      - pattern-inside: |-
                          $CTX.search($NAME, $FILTER, ...)
                      - pattern: "$FILTER"
              - pattern-inside:
                  pattern-either:
                    - pattern: "$CTX = LdapTemplate(...);..."
                    - pattern: "$CTX = LdapOperations(...);..."
                    - pattern: "$CTX = org.springframework.ldap.core.LdapTemplate(...);..."
                    - pattern: "$CTX = org.springframework.ldap.core.LdapOperations(...);..."
    pattern-sources:
      - patterns:
          - pattern-inside: |
              fun $FUNC(..., $VAR: String, ...) {
                ...
              }
          - pattern: "$VAR"
      - patterns:
          - pattern-inside: |
              fun $FUNC(..., $X: String, ...) {
                ...
                $VAR = ... + $X
                ...
              }
          - pattern: "$VAR"
  - id: "kotlin_inject_rule-OgnlInjection"
    message: |
      "A expression is built with a dynamic value. The source of the value(s) should be verified to
      avoid that unfiltered values fall into this risky code evaluation."
    languages:
      - "kotlin"
    severity: "WARNING"
    metadata:
      shortDescription: "Expression injection (OGNL)"
      category: "security"
      cwe: "CWE-917"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/org.apache.struts/struts2-core']
      license: GNU Lesser General Public License v3.0
    patterns:
      - pattern-either:
          - pattern-inside: |
              fun $FUNC(..., $VAR: String, ...) {
                ...
              }
          - pattern-inside: |
              fun $FUNC(..., $VAR: Map<$K,$V>, ...) {
                ...
              }
          - pattern-inside: |
              fun $FUNC(..., $VAR: Map<$K,*>, ...) {
                ...
              }
          - pattern-inside: |
              fun $FUNC(..., $VAR: java.util.HashMap<$K,$V>, ...) {
                ...
              }
      - pattern-either:
          - pattern: "com.opensymphony.xwork2.util.TextParseUtil.translateVariables(...,$VAR, ...)"
          - pattern: "com.opensymphony.xwork2.util.TextParseUtil.translateVariablesCollection(..., $VAR, ...)"
          - pattern: "com.opensymphony.xwork2.util.TextParseUtil.shallBeIncluded(..., $VAR, ...)"
          - pattern: "com.opensymphony.xwork2.util.TextParseUtil.commaDelimitedStringToSet(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.TextParser).evaluate(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.OgnlTextParser).evaluate(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).getGetMethod(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).getSetMethod(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).getField(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).setProperties(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).setProperty(...,$VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).getValue(...,$VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).setValue(...,$VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).getGetMethod(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).getSetMethod(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).getField(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).setProperties(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).setProperty(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).getValue(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).setValue(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).setProperties(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).setProperty(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).getValue(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).setValue(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).callMethod(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).compile(..., $VAR, ...)"
          - pattern: "($P:org.apache.struts2.util.VelocityStrutsUtil).evaluate(...)"
          - pattern: "org.apache.struts2.util.StrutsUtil.findString(...)"
          - pattern: "org.apache.struts2.util.StrutsUtil.findValue(..., $VAL)"
          - pattern: "org.apache.struts2.util.StrutsUtil.getText(...)"
          - pattern: "org.apache.struts2.util.StrutsUtil.translateVariables(...)"
          - pattern: "org.apache.struts2.util.StrutsUtil.makeSelectList(..., $VAR, ...)"
          - pattern: "($T:org.apache.struts2.views.jsp.ui.OgnlTool).findValue(..., $VAR, ...)"
          - pattern: "($V:com.opensymphony.xwork2.util.ValueStack).findString(...)"
          - pattern: "($V:com.opensymphony.xwork2.util.ValueStack).findValue(..., $VAR, ...)"
          - pattern: "($V:com.opensymphony.xwork2.util.ValueStack).setValue(..., $VAR, ...)"
          - pattern: "($V:com.opensymphony.xwork2.util.ValueStack).setParameter(..., $VAR, ...)"
  - id: "kotlin_inject_rule-SpotbugsPathTraversalAbsolute"
    languages:
      - "kotlin"
    message: |
      The software uses an HTTP request parameter to construct a pathname that should be within a
      restricted directory, but it does not properly neutralize absolute path sequences such as
      "/abs/path" that can resolve to a location that is outside of that directory. See
      http://cwe.mitre.org/data/definitions/36.html for more information.
    severity: "WARNING"
    metadata:
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      category: "security"
      cwe: "CWE-22"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/org.apache.commons/commons-io']
      license: GNU Lesser General Public License v3.0

    mode: "taint"
    pattern-sanitizers:
      - pattern: "org.apache.commons.io.FilenameUtils.getName(...)"
    pattern-sinks:
      - patterns:
          - pattern-inside: |
              $U = java.net.URI($VAR)
          - pattern-either:
              - pattern-inside: |-
                  java.io.File($U)
              - pattern-inside: |-
                  java.nio.file.Paths.get($U)
          - pattern: "$VAR"
      - patterns:
          - pattern-inside: |-
              java.io.RandomAccessFile($INPUT,...)
          - pattern: "$INPUT"
      - pattern: "java.io.FileReader(...)"
      - pattern: "javax.activation.FileDataSource(...)"
      - pattern: "java.io.FileInputStream(...)"
      - pattern: "java.io.File(...)"
      - pattern: "java.nio.file.Paths.get(...)"
      - pattern: "java.io.File.createTempFile(...)"
      - pattern: "java.io.File.createTempDirectory(...)"
      - pattern: "java.nio.file.Files.createTempFile(...)"
      - pattern: "java.nio.file.Files.createTempDirectory(...)"
      - patterns:
          - pattern: "$SRC.$METHOD(...)"
          - metavariable-pattern:
              metavariable: "$SRC"
              pattern-either:
                - pattern: "getClass()"
                - pattern: "getClass().getClassLoader()"
                - pattern: "($C: ClassLoader)"
                - pattern: "($C: Class)"
                - pattern: "$CLZ.getClassLoader()"
          - metavariable-pattern:
              metavariable: "$METHOD"
              pattern-either:
                - pattern: "getResourceAsStream"
                - pattern: "getResource"
      - patterns:
          - pattern-inside: |-
              java.io.FileWriter($PATH, ...)
          - pattern: "$PATH"
      - patterns:
          - pattern-inside: |-
              java.io.FileOutputStream($PATH, ...)
          - pattern: "$PATH"
    pattern-sources:
      - pattern: "($REQ: HttpServletRequest).getParameter(...)"
      - patterns:
          - pattern-inside: |-
              fun $FUNC(..., @RequestParam $REQ:$TYPE, ...) {...}
          - focus-metavariable: "$REQ"
  - id: "kotlin_inject_rule-SqlInjection"
    languages:
      - "kotlin"
    message: |
      The input values included in SQL queries need to be passed in safely. Bind
      variables in prepared statements can be used to easily mitigate the risk of
      SQL injection.
    options:
      taint_assume_safe_functions: true
    metadata:
      shortDescription: "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')"
      category: "security"
      cwe: "CWE-89"
      technology:
        - "java"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      related-packages: ['pkg:maven/org.springframework/spring-jdbc']
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"

    mode: taint
    pattern-sources:
      - patterns:
          - pattern-inside: "fun $FUNC(..., $SRC: String, ...) { ... }"
          - pattern: $SRC
    pattern-propagators:
      - pattern: $SB.append($SRC)
        from: $SRC
        to: $SB
      - patterns:
          - pattern: $F(..., $SRC, ...)
          - focus-metavariable: $F
          - pattern-either:
              - pattern: String.format
              - pattern: StringBuilder
        from: $SRC
        to: $F
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern: "($PM: javax.jdo.PersistenceManager).newQuery($ARG)"
              - pattern: "($PM: javax.jdo.PersistenceManager).newQuery(..., $ARG)"
              - pattern: "($Q: javax.jdo.Query).setFilter($ARG)"
              - pattern: "($Q: javax.jdo.Query).setGrouping($ARG)"
              - pattern: "org.hibernate.criterion.Restrictions.sqlRestriction($ARG, ...)"
              - pattern: "($S: org.hibernate.Session).createQuery($ARG, ...)"
              - pattern: "($S: org.hibernate.Session).createSQLQuery($ARG, ...)"
              - pattern: "($S: java.sql.Statement).executeQuery($ARG, ...)"
              - pattern: "($S: java.sql.Statement).execute($ARG, ...)"
              - pattern: "($S: java.sql.Statement).executeUpdate($ARG, ...)"
              - pattern: "($S: java.sql.Statement).executeLargeUpdate($ARG, ...)"
              - pattern: "($S: java.sql.Statement).addBatch($ARG, ...)"
              - pattern: "($S: java.sql.PreparedStatement).executeQuery($ARG, ...)"
              - pattern: "($S: java.sql.PreparedStatement).execute($ARG, ...)"
              - pattern: "($S: java.sql.PreparedStatement).executeUpdate($ARG, ...)"
              - pattern: "($S: java.sql.PreparedStatement).executeLargeUpdate($ARG, ...)"
              - pattern: "($S: java.sql.PreparedStatement).addBatch($ARG, ...)"
              - pattern: "($S: java.sql.Connection).prepareCall($ARG, ...)"
              - pattern: "($S: java.sql.Connection).prepareStatement($ARG, ...)"
              - pattern: "($S: java.sql.Connection).nativeSQL($ARG, ...)"
              - pattern: "org.springframework.jdbc.core.PreparedStatementCreatorFactory($ARG, ...)"
              - pattern: "($F: org.springframework.jdbc.core.PreparedStatementCreatorFactory).newPreparedStatementCreator($ARG,
                  ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).batchUpdate($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).execute($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).query($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForList($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForMap($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForObject($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForObject($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForRowSet($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForInt($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForLong($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).update($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).batchUpdate($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).execute($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).query($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForList($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForMap($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForObject($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForRowSet($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForInt($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForLong($ARG, ...)"
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).update($ARG, ...)"
              - pattern: "($O: io.vertx.sqlclient.SqlClient).query($ARG, ...)"
              - pattern: "($O: io.vertx.sqlclient.SqlClient).preparedQuery($ARG, ...)"
              - pattern: "($O: io.vertx.sqlclient.SqlConnection).prepare($ARG, ...)"
              - pattern: "($O: org.apache.turbine.om.peer.BasePeer).executeQuery($ARG, ...)"
              - pattern: "($O: org.apache.torque.util.BasePeer).executeQuery($ARG, ...)"
              - pattern: "($O: javax.persistence.EntityManager).createQuery($ARG, ...)"
              - pattern: "($O: javax.persistence.EntityManager).createNativeQuery($ARG, ...)"
              - pattern: "($H: org.jdbi.v3.core.Handle).createQuery($ARG, ...)"
              - pattern: "($H: org.jdbi.v3.core.Handle).createScript($ARG, ...)"
              - pattern: "($H: org.jdbi.v3.core.Handle).createUpdate($ARG, ...)"
              - pattern: "($H: org.jdbi.v3.core.Handle).execute($ARG, ...)"
              - pattern: "($H: org.jdbi.v3.core.Handle).prepareBatch($ARG, ...)"
              - pattern: "($H: org.jdbi.v3.core.Handle).select($ARG, ...)"
              - pattern: "org.jdbi.v3.core.statement.Script($H, $ARG)"
              - pattern: "org.jdbi.v3.core.statement.Update($H, $ARG)"
              - pattern: "org.jdbi.v3.core.statement.PreparedBatch($H, $ARG)"
          - focus-metavariable: $ARG
  - id: "kotlin_ldap_rule-AnonymousLDAP"
    languages:
      - "kotlin"
    message: |
      Without proper access control, executing an LDAP statement that contains a
      user-controlled value can allow an attacker to abuse poorly configured LDAP
      context
    metadata:
      shortDescription: "Missing authentication for critical function (LDAP)"
      category: "security"
      cwe: "CWE-306"
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/com.unboundid/unboundid-ldapsdk']
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"

    patterns:
      - pattern-inside: |
          import javax.naming.Context
          ...
      - pattern: '$ENV[Context.SECURITY_AUTHENTICATION] = "none"'
  - id: "kotlin_password_rule-ConstantDBPassword"
    pattern: 'java.sql.DriverManager.getConnection($URI, $USR, "...")'
    message: |
      A potential hard-coded password was identified in a database connection string.
      Passwords should not be stored directly in code
      but loaded from secure locations such as a Key Management System (KMS).

      The purpose of using a Key Management System is so access can be audited and keys easily
      rotated
      in the event of a breach. By hardcoding passwords, it will be extremely difficult to determine
      when or if, a key is compromised.

      The recommendation on which KMS to use depends on the environment the application is running
      in:

      - For Google Cloud Platform consider [Cloud Key Management](https://cloud.google.com/kms/docs)
      - For Amazon Web Services consider [AWS Key Management](https://aws.amazon.com/kms/)
      - For on premise or other alternatives to cloud providers, consider [Hashicorp's
      Vault](https://www.vaultproject.io/)
      - For other cloud providers, please see their documentation
    severity: "ERROR"
    metadata:
      shortDescription: "Use of hard-coded password"
      category: "security"
      cwe: "CWE-259"
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      technology:
        - "kotlin"
      security-severity: "Critical"
      related-packages: ['pkg:maven/org.springframework/spring-jdbc']
      license: GNU Lesser General Public License v3.0
    languages:
      - "kotlin"
  - id: "kotlin_password_rule-EmptyDBPassword"
    pattern: "java.sql.DriverManager.getConnection($URI, $USR, \"\");"
    message: |
      The application does not provide authentication when communicating a database
      server. It is strongly recommended that the database server be configured with
      authentication and restrict what queries users can execute.

      Please see your database server's documentation on how to configure a password.

      Additionally, passwords should not be stored directly in code
      but loaded from secure locations such as a Key Management System (KMS).

      The purpose of using a Key Management System is so access can be audited and keys easily
      rotated
      in the event of a breach. By hardcoding passwords, it will be extremely difficult to determine
      when or if, a key is compromised.

      The recommendation on which KMS to use depends on the environment the application is running
      in:

      - For Google Cloud Platform consider [Cloud Key Management](https://cloud.google.com/kms/docs)
      - For Amazon Web Services consider [AWS Key Management](https://aws.amazon.com/kms/)
      - For on premise or other alternatives to cloud providers, consider [Hashicorp's
      Vault](https://www.vaultproject.io/)
      - For other cloud providers, please see their documentation
    severity: "ERROR"
    metadata:
      shortDescription: "Missing authentication for critical function (database)"
      category: "security"
      cwe: "CWE-306"
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      technology:
        - "kotlin"
      security-severity: "Critical"
      related-packages: ['pkg:maven/org.springframework/spring-jdbc']
      license: GNU Lesser General Public License v3.0
    languages:
      - "kotlin"

  - id: "kotlin_password_rule-HardcodePassword"
    languages:
      - "kotlin"
    message: |
      A potential hard-coded password was identified in the source code.
      Passwords should not be stored directly in code
      but loaded from secure locations such as a Key Management System (KMS).

      The purpose of using a Key Management System is so access can be audited and keys easily
      rotated
      in the event of a breach. By hardcoding passwords, it will be extremely difficult to determine
      when or if, a key is compromised.

      The recommendation on which KMS to use depends on the environment the application is running
      in:

      - For Google Cloud Platform consider [Cloud Key Management](https://cloud.google.com/kms/docs)
      - For Amazon Web Services consider [AWS Key Management](https://aws.amazon.com/kms/)
      - For on premise or other alternatives to cloud providers, consider [Hashicorp's
      Vault](https://www.vaultproject.io/)
      - For other cloud providers, please see their documentation
    severity: "ERROR"
    metadata:
      shortDescription: "Use of hard-coded password"
      category: "security"
      cwe: "CWE-259"
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      technology:
        - "kotlin"
      security-severity: "High"
      related-packages: ['pkg:maven/org.springframework/spring-jdbc']
      license: GNU Lesser General Public License v3.0
    pattern-either:
      - pattern: "java.security.KeyStore.PasswordProtection(\"...\".toCharArray())"
      - pattern: "java.security.KeyStore.getInstance(...).load(..., \"...\".toCharArray())"
      - pattern: "($KS: java.security.KeyStore).load(..., \"...\".toCharArray())"
      - pattern: "KeyManagerFactory.getInstance(...).init(..., \"...\".toCharArray())"
      - pattern: "($KMF: KeyManagerFactory).init(..., \"...\".toCharArray())"
      - pattern: "PBEKeySpec(\"...\", ...)"
      - pattern: "PasswordAuthentication(\"...\", \"...\")"
      - pattern: "($CB: PasswordCallback).setPassword(\"...\")"
      - pattern: "KerberosKey(...,\"...\",...)"
      - pattern: "java.sql.DriverManager.getConnection(..., \"...\")"
      - pattern: "io.vertx.ext.web.handler.CSRFHandler.create(..., \"...\")"
      - pattern: "$S.setPassword(\"...\")"

  - id: "kotlin_perm_rule-DangerousPermissions"
    languages:
      - "kotlin"
    message: |
      Do not grant dangerous combinations of permissions.
    metadata:
      shortDescription: "Insecure inherited permissions"
      category: "security"
      cwe: "CWE-277"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      confidence: "HIGH"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"

    patterns:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  $PC = $X.getPermissions(...)
                  ...
              - pattern: $PC.add($PERMISSION)
          - pattern: |
              $REFVAR = $PERMISSION
              ...;
              ($PC: PermissionCollection).add($REFVAR)
          - pattern: '($PC: PermissionCollection).add($PERMISSION)'
      - metavariable-pattern:
          metavariable: "$PERMISSION"
          pattern-either:
            - pattern: 'ReflectPermission("suppressAccessChecks")'
            - pattern: 'RuntimePermission("createClassLoader")'
  - id: "kotlin_perm_rule-OverlyPermissiveFilePermissionInline"
    languages:
      - "kotlin"
    message: |
      Overly permissive file permission
    metadata:
      shortDescription: "Incorrect permission assignment for critical resource"
      cwe: "CWE-732"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      category: "security"
      confidence: "HIGH"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"

    patterns:
      - pattern-either:
          - pattern: "java.nio.file.Files.setPosixFilePermissions(..., java.nio.file.attribute.PosixFilePermissions.fromString(\"\
              $PERM_STRING\"));"
          - pattern: |
              $PERMISSIONS = java.nio.file.attribute.PosixFilePermissions.fromString("$PERM_STRING");
              ...
              java.nio.file.Files.setPosixFilePermissions(..., $PERMISSIONS);
      - metavariable-regex:
          metavariable: "$PERM_STRING"
          regex: "[rwx-]{6}[rwx]{1,}"
  - id: "kotlin_script_rule-ScriptInjection"
    languages:
      - "kotlin"
    message: |
      The software constructs all or part of a code segment using externally-influenced
      input from an upstream component, but it does not neutralize or incorrectly
      neutralizes special elements that could modify the syntax or behavior of the
      intended code segment.
    severity: "ERROR"
    metadata:
      shortDescription: "Improper control of generation of code ('Code Injection')"
      category: "security"
      cwe: "CWE-94"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      related-packages: ['pkg:maven/javax.script/javax.script-api']
      license: GNU Lesser General Public License v3.0
    mode: "taint"
    pattern-sinks:
      - patterns:
          - patterns:
              - pattern-inside: |
                  $ENGINE = $F.getEngineByExtension(...)
                  ...
              - pattern: "$ENGINE.eval($ARG, ...);"
              - pattern-not: '$ENGINE.eval("...");'
              - pattern-not: '$ENGINE.eval("...", ($BINDING: javax.script.Bindings));'
      - patterns:
          - pattern: "($ENGINE: javax.script.ScriptEngine).eval($ARG, ...);"
          - pattern-not: '($ENGINE: javax.script.ScriptEngine).eval("...");'
          - pattern-not: '($ENGINE: javax.script.ScriptEngine).eval("...", ($BINDING: javax.script.Bindings));'
      - pattern: "($INVC: javax.script.Invocable).invokeFunction(..., $ARG)"
      - pattern: "($INVC: javax.script.Invocable).invokeMethod(..., $ARG)"
    pattern-sources:
      - patterns:
          - pattern-inside: |-
              fun $FUNC(..., $VAR: String, ...) { ... }
          - pattern: "$VAR"
  - id: "kotlin_smtp_rule-InsecureSmtp"
    languages:
      - "kotlin"
    message: |
      Server identity verification is disabled when making SSL connections.
    metadata:
      shortDescription: "Improper validation of certificate with host mismatch"
      category: "security"
      cwe: "CWE-297"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "CRITICAL"
      related-packages: ['pkg:maven/org.apache.commons/commons-email']
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"

    patterns:
      - pattern-either:
          - pattern-inside: |
              $E = org.apache.commons.mail.SimpleEmail(...)
              ...
          - pattern-inside: |
              $E = org.apache.commons.mail.Email(...)
              ...
          - pattern-inside: |
              $E = org.apache.commons.mail.MultiPartEmail(...)
              ...
          - pattern-inside: |
              $E = org.apache.commons.mail.HtmlEmail(...)
              ...
          - pattern-inside: |
              $E = org.apache.commons.mail.ImageHtmlEmail(...)
              ...
      - pattern-not: |
          $E.setSSLOnConnect(true)
          ...
          $E.setSSLCheckServerIdentity(true)
  - id: "kotlin_smtp_rule-SmtpClient"
    languages:
      - "kotlin"
    message: |
      Simple Mail Transfer Protocol (SMTP) is a the text based protocol used for
      email delivery. Like with HTTP, headers are separate by new line separator. If
      kuser input is place in a header line, the application should remove or replace
      new line characters (CR / LF). You should use a safe wrapper such as Apache
      Common Email and Simple Java Mail which filter special characters that can lead
      to header injection.
    metadata:
      shortDescription: "Improper neutralization of special elements used in a command"
      category: "security"
      cwe: "CWE-77"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"

    patterns:
      - pattern-inside: |
          $M = MimeMessage(...);
          ...
      - pattern-either:
          - patterns:
              - pattern-either:
                  - pattern: "$M.setSubject($VAR)"
                  - pattern: "$M.addHeader($ARG, $VAR)"
                  - pattern: "$M.addHeader($VAR, $ARG)"
                  - pattern: "$M.setDescription($VAR)"
                  - pattern: "$M.setDisposition($VAR)"
              - metavariable-regex:
                  metavariable: $VAR
                  regex: "^[a-zA-Z_$][a-zA-Z0-9_$]*$"
          - patterns:
              - pattern-either:
                  - pattern: "$M.setSubject($OBJ.$GETTER(...))"
                  - pattern: "$M.setSubject($OBJ.$GETTER(...) + ...)"
                  - pattern: "$M.setSubject(... + $OBJ.$GETTER(...))"
                  - pattern: "$M.setSubject(... + $OBJ.$GETTER(...) + ...)"
                  - pattern: "$M.addHeader($ARG, $OBJ.$GETTER(...))"
                  - pattern: "$M.addHeader($ARG, $OBJ.$GETTER(...) + ...)"
                  - pattern: "$M.addHeader($ARG, ... + $OBJ.$GETTER(...))"
                  - pattern: "$M.addHeader($ARG, ... + $OBJ.$GETTER(...) + ...)"
                  - pattern: "$M.addHeader($OBJ.$GETTER(...), $ARG)"
                  - pattern: "$M.addHeader($OBJ.$GETTER(...) + ..., $ARG)"
                  - pattern: "$M.addHeader(... + $OBJ.$GETTER(...), $ARG)"
                  - pattern: "$M.addHeader(... + $OBJ.$GETTER(...) + ..., $ARG)"
                  - pattern: "$M.setDescription($OBJ.$GETTER(...))"
                  - pattern: "$M.setDisposition($OBJ.$GETTER(...) + ...)"
                  - pattern: "$M.setDisposition(... + $OBJ.$GETTER(...))"
                  - pattern: "$M.setDisposition(... + $OBJ.$GETTER(...) + ...)"
              - metavariable-regex:
                  metavariable: $GETTER
                  regex: ^get
  - id: "kotlin_ssrf_rule-SSRF"
    languages:
      - "kotlin"
    message: |
      Server-Side Request Forgery occur when a web server executes a request to a user supplied
      destination parameter that is not validated. Such vulnerabilities could allow an attacker to
      access internal services or to launch attacks from your web server.
    metadata:
      shortDescription: "Server-Side Request Forgery (SSRF)"
      category: "security"
      cwe: "CWE-918"
      owasp:
        - "A1:2017-Injection"
        - "A10:2021-Server-Side Request Forgery"
      security-severity: "CRITICAL"
      related-packages: ['pkg:maven/org.apache.httpcomponents.client5/httpclient5']
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"

    pattern-either:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  import java.net.*;
                  ...
              - pattern-inside: |
                  import java.net.URL;
                  ...
              - pattern-inside: |
                  import java.net.URI;
                  ...
          - pattern: "$TYPE(...). ... .$FUNC"
          - pattern-not: '$TYPE("..."). ... .$FUNC'
          - metavariable-pattern:
              metavariable: "$FUNC"
              pattern-either:
                - pattern: "connect"
                - pattern: "GetContent"
                - pattern: "openConnection"
                - pattern: "openStream"
                - pattern: "getContent"
                - pattern: "content"
          - metavariable-pattern:
              metavariable: "$TYPE"
              pattern-either:
                - pattern: "URL"
                - pattern: "java.net.URL"
                - pattern: "URI"
                - pattern: "java.net.URI"
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  import java.net.*;
                  ...
              - pattern-inside: |
                  import java.net.InetSocketAddress;
                  ...
          - pattern: |
              InetSocketAddress(..., $PORT)
          - pattern-not: |
              InetSocketAddress("...", $PORT)
  - id: "kotlin_strings_rule-BadHexConversion"
    languages:
      - "kotlin"
    message: |
      When converting a byte array containing a hash signature to a human readable string, a
      conversion mistake can be made if the array is read byte by byte.
    metadata:
      shortDescription: "Incorrect type conversion or cast"
      cwe: "CWE-704"
      category: "security"
      confidence: "HIGH"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      security-severity: "MEDIUM"
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"

    patterns:
      - pattern-inside: |
          $B_ARR = ($MD: java.security.MessageDigest).digest(...);
          ...
      - pattern-either:
          - pattern: |
              for($B in $B_ARR) {
                ...
                $B_TOSTR
              }
          - pattern: |
              while(...) {
                ...
                $B_TOSTR
              }
          - pattern: |
              do {
                ...
                $B_TOSTR
              } while(...)
      - metavariable-pattern:
          metavariable: $B_TOSTR
          patterns:
            - pattern-either:
                - pattern: java.lang.Integer.toHexString($B_TOINT)
                - pattern: Integer.toHexString($B_TOINT)
                - pattern: $B_TOINT.toHexString(...)
            - metavariable-pattern:
                metavariable: $B_TOINT
                pattern-either:
                  - pattern: $B_ARR[...].toInt()
                  - pattern: $B_ARR[...]
                  - pattern: $B.toInt()
                  - pattern: $B
  - id: "kotlin_strings_rule-FormatStringManipulation"
    languages:
      - "kotlin"
    message: |
      Allowing user input to control format parameters could enable an attacker to cause exceptions
      to be thrown or leak information.Attackers may be able  to modify the format string argument,
      such that an exception is thrown. If this exception is left uncaught, it may crash the
      application. Alternatively, if sensitive information is used within the unused arguments,
      attackers may change the format string to reveal this information.
    metadata:
      shortDescription: "Use of externally-controlled format string"
      cwe: "CWE-134"
      category: "security"
      confidence: "HIGH"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"

    patterns:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  $INPUT = ($REQ: HttpServletRequest).getParameter(...)
                  ...
              - pattern-inside: |
                  $FORMAT_STR = ... + $INPUT
                  ...
          - patterns:
              - pattern-inside: |
                  $INPUT = ($REQ: HttpServletRequest).getParameter(...)
                  ...
              - pattern-inside: |
                  $FORMAT_STR = ... + $INPUT + ...
                  ...
          - pattern-inside: |
              $FORMAT_STR = ... + ($REQ: HttpServletRequest).getParameter(...) + ...
              ...
          - pattern-inside: |
              $FORMAT_STR = ... + ($REQ: HttpServletRequest).getParameter(...)
              ...
      - pattern-either:
          - pattern: "String.format($FORMAT_STR, ...)"
          - pattern: "String.format(java.util.Locale.$LOCALE, $FORMAT_STR, ...)"
          - patterns:
              - pattern-inside: |
                  $F = java.util.Formatter(...)
                  ...
              - pattern-either:
                  - pattern: "$F.format($FORMAT_STR, ...)"
                  - pattern: "$F.format(java.util.Locale.$LOCALE, $FORMAT_STR, ...)"
          - pattern: "($F: java.io.PrintStream).printf($FORMAT_STR, ...)"
          - pattern: "($F: java.io.PrintStream).printf(java.util.Locale.$LOCALE, $FORMAT_STR, ...)"
          - pattern: "($F: java.io.PrintStream).format($FORMAT_STR, ...)"
          - pattern: "($F: java.io.PrintStream).format(java.util.Locale.$LOCALE, $FORMAT_STR, ...)"
          - pattern: "System.out.printf($FORMAT_STR, ...)"
          - pattern: "System.out.printf(java.util.Locale.$LOCALE, $FORMAT_STR, ...)"
          - pattern: "System.out.format($FORMAT_STR, ...)"
          - pattern: "System.out.format(java.util.Locale.$LOCALE, $FORMAT_STR, ...)"
  - id: "kotlin_strings_rule-ModifyAfterValidation"
    languages:
      - "kotlin"
    message: |
      CERT: IDS11-J. Perform any string modifications before validation
    metadata:
      shortDescription: "Collapse of data into unsafe value"
      category: "security"
      cwe: "CWE-182"
      confidence: "HIGH"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "MEDIUM"
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"

    patterns:
      - pattern-inside: |
          $PATTERN = Pattern.compile(...)
          ...
      - pattern-inside: |
          $PATTERN.matcher($VAR)
          ...
      - pattern-either:
          - pattern: |
              $VAR + $OTHER
          - patterns:
              - pattern: |
                  $VAR.$METHOD(...)
              - metavariable-regex:
                  metavariable: "$METHOD"
                  regex: "(replace|replaceAll|replaceFirst|concat)"
  - id: "kotlin_strings_rule-NormalizeAfterValidation"
    languages:
      - "kotlin"
    message: |
      IDS01-J. Normalize strings before validating them
    metadata:
      shortDescription: "Incorrect behavior order: validate before canonicalize"
      category: "security"
      cwe: "CWE-180"
      confidence: "HIGH"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "MEDIUM"
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"

    patterns:
      - pattern: |-
          $Y = java.util.regex.Pattern.compile("[<>]");
          ...
          $Y.matcher($VAR);
          ...
          java.text.Normalizer.normalize($VAR, ...);
  - id: "kotlin_templateinjection_rule-TemplateInjection"
    languages:
      - "kotlin"
    message: |
      A malicious user in control of a template can run malicious code on the
      server-side. Velocity templates should be seen as scripts.
    metadata:
      shortDescription: "Improper control of generation of code ('Code Injection')"
      category: "security"
      cwe: "CWE-94"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      related-packages: ['pkg:maven/org.apache.velocity/velocity']
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"

    pattern-either:
      - patterns:
          - pattern: "org.apache.velocity.app.Velocity.evaluate(..., $VAR)"
          - pattern-not: 'org.apache.velocity.app.Velocity.evaluate(..., "...")'
      - patterns:
          - pattern-not-inside: |
              $C = ($CFG: freemarker.template.Configuration).getTemplate("...");
              ...
          - pattern-inside: |
              $C = ($CFG: freemarker.template.Configuration).getTemplate($IN);
              ...
          - pattern: "$C.process(...)"
      - patterns:
          - pattern-inside: |
              import com.mitchellbosecke.pebble.PebbleEngine;
              ...
          - pattern-inside: |
              $C = $T.getTemplate($IN);
              ...
          - pattern-not-inside: |
              $C = $T.getTemplate("...");
              ...
          - pattern: "$C.evaluate(...)"
  - id: "kotlin_unsafe_rule-ExternalConfigControl"
    languages:
      - "kotlin"
    message: |
      Allowing external control of system settings can disrupt service or cause an application to
      behave in unexpected, and potentially malicious ways. An attacker could cause an error by
      providing a nonexistent catalog name or connect to an unauthorized portion of the database.
    severity: "WARNING"
    metadata:
      shortDescription: "External control of system or configuration setting"
      category: "security"
      cwe: "CWE-15"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/org.springframework/spring-jdbc']
      license: GNU Lesser General Public License v3.0
    patterns:
      - metavariable-pattern:
          metavariable: "$GET_PARAMETER"
          pattern-either:
            - pattern: '($REQ: HttpServletRequest).getParameter'
            - pattern: '($REQ: HttpServletRequest?)?.getParameter'
      - metavariable-pattern:
          metavariable: "$SET_CATALOG"
          pattern-either:
            - pattern: '($CONN: java.sql.Connection).setCatalog'
            - pattern: '($CONN: java.sql.Connection?)?.setCatalog'
      - pattern: |-
          $TAINTED = $GET_PARAMETER(...)
          ...
          $SET_CATALOG($TAINTED)
  - id: "kotlin_xml_rule-SAMLIgnoreComments"
    languages:
      - "kotlin"
    message: |
      Ignoring XML comments in SAML may lead to authentication bypass
    metadata:
      shortDescription: "Weak authentication"
      cwe: "CWE-1390"
      category: "security"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "MEDIUM"
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"
    related-packages: ['pkg:maven/org.opensaml/opensaml']
    pattern: "($POOL: org.opensaml.xml.parse.BasicParserPool).setIgnoreComments(false);"
  - id: "kotlin_xml_rule-XmlDecoder"
    languages:
      - "kotlin"
    message: |
      Avoid using XMLDecoder to parse content from an untrusted source.
    metadata:
      shortDescription: "Deserialization of untrusted data"
      category: "security"
      cwe: "CWE-502"
      owasp:
        - "A8:2017-Insecure Deserialization"
        - "A08:2021-Software and Data Integrity Failures"
      security-severity: "MEDIUM"
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"
    related-packages: ['pkg:maven/java/beans']
    patterns:
      - pattern-either:
          - pattern: "($D: java.beans.XMLDecoder).readObject()"
          - patterns:
              - pattern: "$D.readObject()"
              - pattern-inside: |
                  $D = XMLDecoder(...)
                  ...
      - pattern-not:
          pattern-either:
            - patterns:
                - pattern-inside: |
                    $DEC = java.beans.XMLDecoder(..., $CL)
                    ...
                - pattern: $DEC.readObject()
                - metavariable-pattern:
                    metavariable: $CL
                    patterns:
                      - pattern: |
                          object : ClassLoader() {
                            ...
                            fun loadClass(name: String, resolve: Boolean): $RET {
                              if($X){
                                throw ...
                              }
                              ...
                            }
                            ...
                          }
                      - metavariable-pattern:
                          metavariable: $X
                          pattern-either:
                            - pattern: |
                                name != ...
                            - pattern: |
                                !$LIST.contains(name)
            - patterns:
                - pattern-inside: |
                    $CLASS_LOADER  = $CL
                    ...
                - pattern-inside: |
                    $DEC = java.beans.XMLDecoder(..., $CLASS_LOADER)
                    ...
                - pattern: $DEC.readObject()
                - metavariable-pattern:
                    metavariable: $CL
                    patterns:
                      - pattern: |
                          object : ClassLoader(){
                            ...
                            fun loadClass(name: String, resolve: Boolean): $RET{
                              if($X){
                                throw ...
                              }
                              ...
                            }
                            ...
                          }
                      - metavariable-pattern:
                          metavariable: $X
                          pattern-either:
                            - pattern: |
                                name != ...
                            - pattern: |
                                !$LIST.contains(name)
  - id: "kotlin_xml_rule-XsltTransform"
    languages:
      - "kotlin"
    message: |
      It is possible to attach malicious behavior to those style sheets. Therefore, if an attacker
      can control the content or the source of the style sheet, he might be able to trigger remote
      code execution.
    metadata:
      shortDescription: "XML injection (aka Blind XPath injection)"
      category: "security"
      cwe: "CWE-91"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "MEDIUM"
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"
    related-packages: ['pkg:maven/javax.xml.transform/javax.xml.transform-api']
    mode: "taint"
    pattern-sources:
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern-inside: |
                      fun $FUNC(...,$VAR: String, ...) {
                        ...
                      }
                  - pattern-either:
                      - pattern: "FileInputStream(<... $VAR ...>);"
                      - pattern: "javaClass.getResourceAsStream(<... $VAR ...>)"
              - patterns:
                  - pattern-inside: |
                      class $CLZ {
                        var $X = "...";
                        ...
                      }
                  - pattern-inside: |
                      fun $FUNC(...,$Y: String, ...) {
                        ...
                      }
                  - pattern-either:
                      - pattern: "FileInputStream($X + $Y);"
                      - pattern: "javaClass.getResourceAsStream($X + $Y)"
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern-inside: |-
                  ($T: javax.xml.transform.TransformerFactory).newTransformer($SRC, ...)
              - pattern-inside: |-
                  ($T: javax.xml.transform.Transformer).transform($SRC, ...)
              # flow instead of type assertion
              - patterns:
                  - pattern-inside: |-
                      $FACTORY = javax.xml.transform.TransformerFactory.newInstance(...)
                      ...
                  - pattern-inside: $FACTORY.newTransformer($SRC, ...)
              - patterns:
                  - pattern-inside: |-
                      $FACTORY = javax.xml.transform.TransformerFactory(...)
                      ...
                  - pattern-inside: |-
                      $T = $FACTORY.newTransformer(...)
                      ...
                  - pattern-inside: |-
                      $T.transform($SRC, ...)
          - pattern: "$SRC"
  - id: "kotlin_xpathi_rule-XpathInjection"
    languages:
      - "kotlin"
    message: |
      The input values included in SQL queries need to be passed in safely. Bind
      variables in prepared statements can be used to easily mitigate the risk of
      SQL injection.
    metadata:
      shortDescription: "Improper neutralization of data within XPath expressions ('XPath Injection')"
      category: "security"
      cwe: "CWE-643"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"
    related-packages: ['pkg:maven/javax.xml.xpath/javax.xml.xpath-api']
    mode: "taint"
    pattern-sources:
      - patterns:
          - pattern-inside: |
              fun $FUNC(..., $VAR: $T, ...) {
                ...
              }
          - pattern: "$VAR"
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern-inside: "import javax.xml.xpath.*; \n...\n"
              - pattern-inside: "import javax.xml.xpath.XPath; \n...\n"
          - patterns:
              - pattern-either:
                  - patterns:
                      - pattern: "$X.compile($VAR)"
                      - pattern-not: '$X.compile("...")'
                  - patterns:
                      - pattern: "$X.evaluate($VAR, ...)"
                      - pattern-not: '$X.evaluate("...", ...)'
    pattern-sanitizers:
      - pattern-either:
          - pattern-inside: |
              $X.xPathVariableResolver = ...;
              ...;
              $X.compile("...");
          - pattern-inside: |
              $X.setXPathVariableResolver(...);
              ...;
              $X.compile("...");
  - id: "kotlin_xss_rule-WicketXSS"
    languages:
      - "kotlin"
    message: |
      Disabling HTML escaping put the application at risk for Cross-Site Scripting (XSS).
    metadata:
      shortDescription: "Improper neutralization of input during web page generation ('Cross-site Scripting')"
      category: "security"
      cwe: "CWE-79"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "MEDIUM"
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"
    related-packages: ['pkg:maven/org.apache.wicket/wicket']
    patterns:
      - pattern-inside: |
          import org.apache.wicket.$A
          ...
      - pattern: |
          $OBJ.setEscapeModelStrings(false);
  - id: "kotlin_xss_rule-XSSReqParamToServletWriter"
    languages:
      - "kotlin"
    message: |
      Servlet reflected cross site scripting vulnerability
    severity: "WARNING"
    metadata:
      shortDescription: "Improper neutralization of input during web page generation ('Cross-site Scripting')"
      category: "security"
      cwe: "CWE-79"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      technology:
        - "kotlin"
      security-severity: "MEDIUM"
      related-packages: ['pkg:maven/org.owasp.encoder/encoder']
      license: GNU Lesser General Public License v3.0
    mode: "taint"
    pattern-sanitizers:
      - patterns:
          - pattern-inside: |-
              org.owasp.encoder.Encode.forHtml($TAINTED);
          - pattern: "$TAINTED"
    pattern-sinks:
      - patterns:
          - pattern-inside: |-
              fun $FUNC(..., $RES: HttpServletResponse , ...) {...}
          - pattern-inside: |
              $WRITER = $RES.getWriter();
              ...
          - pattern: "$WRITER.write($DATA,...);"
          - pattern: "$DATA"
      - patterns:
          - pattern-inside: |-
              fun $FUNC(..., $RES: HttpServletResponse , ...) {...}
          - pattern: "$RES.getWriter().write($DATA,...);"
          - pattern: "$DATA"
    pattern-sources:
      - patterns:
          - pattern-inside: |-
              fun $FUNC(..., $REQ: HttpServletRequest , ...) {...}
          - pattern: "$REQ.getParameter(...);"
  - id: "kotlin_xxe_rule-SaxParserXXE"
    languages:
      - "kotlin"
    message: |
      XML External Entity (XXE) attacks can occur when an XML parser supports XML
      entities while processing XML received from an untrusted source.
    metadata:
      shortDescription: "Improper restriction of XML external entity reference ('XXE')"
      category: "security"
      cwe: "CWE-611"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"
    related-packages: ['pkg:maven/org.xml.sax/xml-apis']
    patterns:
      - pattern-inside: |
          $SF = SAXParserFactory.newInstance()
          ...
      - pattern-not-inside: |
          $SF.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
          ...
      - pattern-not-inside: |
          $SF.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
          ...
      - pattern-inside: |
          $P = $SFP.newSAXParser();
          ...
      - pattern: "$P.parse(...);"
  - id: "kotlin_xxe_rule-XMLRdr"
    languages:
      - "kotlin"
    message: |
      XML External Entity (XXE) attacks can occur when an XML parser supports XML
      entities while processing XML received from an untrusted source.
    metadata:
      shortDescription: "Improper restriction of XML external entity reference ('XXE')"
      category: "security"
      cwe: "CWE-611"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      related-packages: ['pkg:maven/org.xml.sax/xml-apis']
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"

    patterns:
      - pattern-inside: |
          $R = XMLReaderFactory.createXMLReader()
          ...
      - pattern-not-inside: |
          $R.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
          ...
      - pattern: "$R.parse(...)"
  - id: "kotlin_xxe_rule-XMLStreamRdr"
    languages:
      - "kotlin"
    message: |
      XML External Entity (XXE) attacks can occur when an XML parser supports XML
      entities while processing XML received from an untrusted source.
    metadata:
      shortDescription: "Improper restriction of XML external entity reference ('XXE')"
      category: "security"
      cwe: "CWE-611"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"
    related-packages: ['pkg:maven/javax.xml.stream/stax-api']
    patterns:
      - pattern-inside: |
          $SF = XMLInputFactory.newFactory();
          ...
      - pattern-not-inside: |
          $SF.setProperty(XMLInputFactory.SUPPORT_DTD, false);
          ...
      - pattern-not-inside: |
          $SF.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
          ...
      - pattern-not-inside: |
          $SF.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
          ...
      - pattern-not-inside: |
          $SF.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
          ...
      - pattern: "$SF.createXMLStreamReader(...)"
  - id: pyramid_jinja2_reflected_xss
    severity: ERROR
    message: |
      Untrusted user input is passed to Pyramid's template rendering functions without proper sanitization, creating a reflected XSS vulnerability. Even though templates typically auto-escape variables, this protection can be bypassed.
      Never include untrusted user data directly in templates or with auto-escaping disabled. Always pass user data through context dictionaries.
      If necessary, sanitize user input before passing it to templates using Pyramid's escape_html, MarkupSafe's escape, Jinja2's escape, html.escape(), or bleach.clean().
      Be especially careful with template features that disable auto-escaping (like safe filters or raw blocks) when working with user-controlled data.
    metadata:
      cwe: CWE-79
      owasp:
        - A7:2017-Cross-Site Scripting (XSS)
        - A03:2021-Injection
      category: security
      shortDescription: Improper neutralization of user input rendered in HTML ('XSS')
      security-severity: High
      related-packages:
        - pkg:pypi/pyramid
      license: License Not Found
    languages:
      - python
    mode: taint

    pattern-sources:
      - patterns:
          - pattern-inside: |
              from pyramid.view import $VIEW
              ...
              @$VIEW(...)
              def $FUNC($REQ, ...):
                  ...
          - pattern-either:
              - pattern: $UNTRUSTED = $REQ.params.get(...)
              - pattern: $UNTRUSTED = $REQ.GET.get(...)
              - pattern: $UNTRUSTED = $REQ.POST.get(...)
              - pattern: $UNTRUSTED = $REQ.json_body.get(...)
              - pattern: $UNTRUSTED = $REQ.matchdict.get(...)
              - pattern: $UNTRUSTED = $REQ.params[...]
              - pattern: $UNTRUSTED = $REQ.GET[...]
              - pattern: $UNTRUSTED = $REQ.POST[...]

    pattern-sinks:
      - pattern-either:
          - pattern: render($TEMPLATE, $DICT, ...)
          - pattern: Response(render($TEMPLATE, $DICT, ...))
          - pattern: |
              $HTML = render($TEMPLATE, $DICT, ...)
              ...
              Response($HTML, ...)
          - pattern: return Response(render($TEMPLATE, $DICT, ...))
          - pattern: |
              $DICT = {}
              ...
              $DICT[$KEY] = $UNTRUSTED
              ...
              render($TEMPLATE, $DICT, ...)
          - pattern: |
              $DICT = {}
              ...
              $DICT[$KEY] = $UNTRUSTED
              ...
              Response(render($TEMPLATE, $DICT, ...))

    pattern-sanitizers:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  import bleach
                  ...
              - pattern: bleach.clean($UNTRUSTED)
          - patterns:
              - pattern-inside: |
                  from markupsafe import escape
                  ...
              - pattern: escape($UNTRUSTED)
          - patterns:
              - pattern-inside: |
                  import html
                  ...
              - pattern: html.escape($UNTRUSTED)
          - patterns:
              - pattern-inside: |
                  from pyramid.escape import escape_html
                  ...
              - pattern: escape_html($UNTRUSTED)
          - patterns:
              - pattern-inside: |
                  from jinja2 import escape
                  ...
              - pattern: escape($UNTRUSTED)
  - id: pyramid_unsafe_user_input
    severity: ERROR
    message: |
      Untrusted user input from request objects (params, GET, POST, json_body, matchdict) is directly embedded in a Pyramid Response without proper sanitization, creating a reflected XSS vulnerability.
      Always sanitize user input before including it in HTML responses using Pyramid's escape_html, MarkupSafe's escape, html.escape(), or bleach.clean().
      Consider using Pyramid's template engines which automatically escape variables by default instead of manually constructing HTML.
      Remember that HTML escaping is not sufficient for JavaScript contexts, CSS contexts, or URL attributes - use context-specific sanitization for these cases.
    metadata:
      cwe: CWE-79
      owasp:
        - A7:2017-Cross-Site Scripting (XSS)
        - A03:2021-Injection
      category: security
      shortDescription: Improper neutralization of user input rendered in HTML ('XSS')
      security-severity: High
      related-packages:
        - pkg:pypi/pyramid
      license: License Not Found
    languages:
      - python
    mode: taint

    pattern-sources:
      - patterns:
          - pattern-inside: |
              from pyramid.view import $VIEW
              ...
              @$VIEW(...)
              def $FUNC($REQ, ...):
                  ...
          - pattern-either:
              - pattern: $UNTRUSTED = $REQ.params.get(...)
              - pattern: $UNTRUSTED = $REQ.GET.get(...)
              - pattern: $UNTRUSTED = $REQ.POST.get(...)
              - pattern: $UNTRUSTED = $REQ.json_body.get(...)
              - pattern: $UNTRUSTED = $REQ.matchdict.get(...)
              - pattern: $UNTRUSTED = $REQ.params[...]
              - pattern: $UNTRUSTED = $REQ.GET[...]
              - pattern: $UNTRUSTED = $REQ.POST[...]

    pattern-sinks:
      - pattern-either:
          - pattern: Response($UNTRUSTED, ...)
          - pattern: Response(f"<... { $UNTRUSTED } ...>", ...)
          - pattern: Response("<... " + $UNTRUSTED + " ...", ...)
          - pattern: Response("...".format(..., $UNTRUSTED, ...), ...)
          - pattern: Response("..." % $UNTRUSTED, ...)
          - pattern: |
              $HTML = f"<... { $UNTRUSTED } ...>"
              ...
              Response($HTML, ...)
          - pattern: |
              $HTML = "<... " + $UNTRUSTED + " ...>"
              ...
              Response($HTML, ...)
          - pattern: |
              $HTML = "...".format(..., $UNTRUSTED, ...)
              ...
              Response($HTML, ...)
          - pattern: |
              $HTML = "..." % $UNTRUSTED
              ...
              Response($HTML, ...)
          - pattern: return Response($UNTRUSTED, ...)
          - pattern: return Response(f"<... { $UNTRUSTED } ...>", ...)
          - pattern: return Response("<... " + $UNTRUSTED + " ...", ...)
          - pattern: return Response("...".format(..., $UNTRUSTED, ...), ...)
          - pattern: return Response("..." % $UNTRUSTED, ...)

    pattern-sanitizers:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  import bleach
                  ...
              - pattern: bleach.clean($UNTRUSTED)
          - patterns:
              - pattern-inside: |
                  from markupsafe import escape
                  ...
              - pattern: escape($UNTRUSTED)
          - patterns:
              - pattern-inside: |
                  import html
                  ...
              - pattern: html.escape($UNTRUSTED)
          - patterns:
              - pattern-inside: |
                  from pyramid.escape import escape_html
                  ...
              - pattern: escape_html($UNTRUSTED)
  - id: "python_assert_rule-assert-used"
    languages:
      - "python"
    message: |
      The application was found using `assert` in non-test code. Usually reserved for debug and test
      code, the `assert`
      function is commonly used to test conditions before continuing execution. However, enclosed
      code will be removed
      when compiling Python code to optimized byte code. Depending on the assertion and subsequent
      logic, this could
      lead to undefined behavior of the application or application crashes.

      To remediate this issue, remove the `assert` calls. If necessary, replace them with either `if`
      conditions or
      `try/except` blocks.

      Example using `try/except` instead of `assert`:
      ```
      # Below try/except is equal to the assert statement of:
      # assert user.is_authenticated(), "user must be authenticated"
      try:
          if not user.is_authenticated():
              raise AuthError("user must be authenticated")
      except AuthError as e:
          # Handle error
          # ...
          # Return, do not continue processing
          return
      ```
    metadata:
      cwe: "CWE-754"
      category: "security"
      shortDescription: "Improper check for unusual or exceptional conditions"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: "assert(...)"
      - pattern-not-inside: |
          import pytest
          ...
      - pattern-not-inside: |
          import unittest
          ...
    severity: "INFO"
  - id: "python_bind-all-interfaces_rule-general-bindall-interfaces"
    languages:
      - "python"
    message: |
      Binding to all network interfaces can potentially open up a service to
      traffic on unintended interfaces, that may not be properly documented or
      secured. By passing "0.0.0.0", "::" or an empty string as the address to the `socket.bind`
      function,
      the application will bind to all interfaces.

      Consider passing in the interface ip address through an environment variable,
      configuration file, or by determining the primary interface(s) IP address.

      Example getting the IP address from an environment variable `IP_ADDRESS`:
      ```
      # Get the IP_ADDRESS env variable, or bind to
      # 127.0.0.1 if it is not set
      address = os.getenv("IP_ADDRESS", "127.0.0.1")
      # Create an internet socket
      sock = socket.socket(socket.AF_INET)
      # Set the port to listen on
      port = 9777
      # Bind to the address and port combination
      sock.bind((address, port))
      # Listen for connections
      sock.listen()
      # Handle the connection
      ```
    patterns:
      - pattern-either:
          - pattern: |
              $S = socket.socket(...)
              ...
              $S.bind(("0.0.0.0", ...))
          - pattern: |
              $S = socket.socket(...)
              ...
              $S.bind(("::", ...))
          - pattern: |
              $S = socket.socket(...)
              ...
              $S.bind(("", ...))
    metadata:
      category: "security"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      shortDescription: "Binding to an unrestricted IP address"
      cwe: "CWE-1327"
      security-severity: "Low"
      license: MIT (c) GitLab Inc.
    severity: "INFO"
  - id: "python_crypto_rule-cipher-modes"
    languages:
      - "python"
    message: |
      Cryptographic algorithms provide many different modes of operation, only some of which provide
      message integrity. Without message integrity it could be possible for an adversary to attempt
      to tamper with the ciphertext which could lead to compromising the encryption key. Newer
      algorithms
      apply message integrity to validate ciphertext has not been tampered with.

      Instead of using an algorithm that requires configuring a cipher mode, an algorithm
      that has built-in message integrity should be used. Consider using `ChaCha20Poly1305` or
      `AES-256-GCM` instead.

      For older applications that don't have support for `ChaCha20Poly1305`, `AES-256-GCM` is
      recommended, however it has many drawbacks:
        - Slower than `ChaCha20Poly1305`.
        - Catastrophic failure if nonce values are reused.

      Example using `ChaCha20Poly1305`:
      ```
      import os
      # Import ChaCha20Poly1305 from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = ChaCha20Poly1305.generate_key()
      # Create a new ChaCha20Poly1305 instance with our secure key
      chacha = ChaCha20Poly1305(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = chacha.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      chacha.decrypt(nonce, cipher_text, aad)
      ```

      Example using `AESGCM`:
      ```
      import os
      # Import AESGCM from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import AESGCM
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = AESGCM.generate_key(bit_length=128)
      # Create a new AESGCM instance with our secure key
      aesgcm = AESGCM(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = aesgcm.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      aesgcm.decrypt(nonce, cipher_text, aad)
      ```

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/cryptography']
      license: MIT (c) GitLab Inc.
    pattern: "cryptography.hazmat.primitives.ciphers.modes.ECB(...)"
    severity: "WARNING"
  - id: "python_crypto_rule-crypto-cipher-blowfish"
    languages:
      - "python"
    message: |
      The Blowfish encryption algorithm was meant as a drop-in replacement for DES and was created in
      1993. Smaller key sizes may make the ciphertext vulnerable to [birthday
      attacks](https://en.wikipedia.org/wiki/Birthday_attack). While no known attacks against
      Blowfish
      exist, it should never be used to encrypt files over 4GB in size. If possible consider
      using ChaCha20Poly1305 or AES-GCM instead of Blowfish.

      For older applications that don't have support for `ChaCha20Poly1305`, `AES-256-GCM` is
      recommended, however it has many drawbacks:
        - Slower than `ChaCha20Poly1305`.
        - Catastrophic failure if nonce values are reused.

      Note that the `Crypto` and `Cryptodome` Python packages are no longer recommended for
      new applications, instead consider using the [cryptography](https://cryptography.io/) package.

      Example using `ChaCha20Poly1305`:
      ```
      import os
      # Import ChaCha20Poly1305 from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = ChaCha20Poly1305.generate_key()
      # Create a new ChaCha20Poly1305 instance with our secure key
      chacha = ChaCha20Poly1305(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = chacha.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      chacha.decrypt(nonce, cipher_text, aad)
      ```

      Example using `AESGCM`:
      ```
      import os
      # Import AESGCM from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import AESGCM
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = AESGCM.generate_key(bit_length=128)
      # Create a new AESGCM instance with our secure key
      aesgcm = AESGCM(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = aesgcm.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      aesgcm.decrypt(nonce, cipher_text, aad)
      ```

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/pycryptodome', 'pkg:pypi/pycrypto']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: "Cryptodome.Cipher.Blowfish.new(...)"
          - pattern: "Crypto.Cipher.Blowfish.new(...)"
    severity: "WARNING"
  - id: "python_crypto_rule-crypto-cipher-des"
    languages:
      - "python"
    message: |
      DES, TripleDES, RC2 and RC4 are all considered broken or insecure cryptographic algorithms.
      Newer algorithms apply message integrity to validate ciphertext has not been tampered
      with. Consider using `ChaCha20Poly1305` instead as it is easier and faster than the
      alternatives such as `AES-256-GCM`.

      For older applications that don't have support for `ChaCha20Poly1305`,
      `AES-256-GCM` is recommended, however it has many drawbacks:
      - Slower than `ChaCha20Poly1305`.
      - Catastrophic failure if nonce values are reused.

      Note that the `Crypto` and `Cryptodome` Python packages are no longer recommended for
      new applications, instead consider using the [cryptography](https://cryptography.io/) package.

      Example using `ChaCha20Poly1305`:
      ```
      import os
      # Import ChaCha20Poly1305 from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = ChaCha20Poly1305.generate_key()
      # Create a new ChaCha20Poly1305 instance with our secure key
      chacha = ChaCha20Poly1305(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = chacha.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      chacha.decrypt(nonce, cipher_text, aad)
      ```

      Example using `AESGCM`:
      ```
      import os
      # Import AESGCM from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import AESGCM
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = AESGCM.generate_key(bit_length=128)
      # Create a new AESGCM instance with our secure key
      aesgcm = AESGCM(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = aesgcm.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      aesgcm.decrypt(nonce, cipher_text, aad)
      ```

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/pycryptodome', 'pkg:pypi/pycrypto']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: "Cryptodome.Cipher.DES.new(...)"
          - pattern: "Crypto.Cipher.DES.new(...)"
    severity: "WARNING"
  - id: "python_crypto_rule-crypto-cipher-rc2"
    languages:
      - "python"
    message: |
      DES, TripleDES, RC2 and RC4 are all considered broken or insecure cryptographic algorithms.
      Newer algorithms apply message integrity to validate ciphertext has not been tampered
      with. Consider using `ChaCha20Poly1305` instead as it is easier and faster than the
      alternatives such as `AES-256-GCM`.

      For older applications that don't have support for `ChaCha20Poly1305`,
      `AES-256-GCM` is recommended, however it has many drawbacks:
      - Slower than `ChaCha20Poly1305`.
      - Catastrophic failure if nonce values are reused.

      Note that the `Crypto` and `Cryptodome` Python packages are no longer recommended for
      new applications, instead consider using the [cryptography](https://cryptography.io/) package.

      Example using `ChaCha20Poly1305`:
      ```
      import os
      # Import ChaCha20Poly1305 from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = ChaCha20Poly1305.generate_key()
      # Create a new ChaCha20Poly1305 instance with our secure key
      chacha = ChaCha20Poly1305(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = chacha.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      chacha.decrypt(nonce, cipher_text, aad)
      ```

      Example using `AESGCM`:
      ```
      import os
      # Import AESGCM from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import AESGCM
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = AESGCM.generate_key(bit_length=128)
      # Create a new AESGCM instance with our secure key
      aesgcm = AESGCM(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = aesgcm.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      aesgcm.decrypt(nonce, cipher_text, aad)
      ```

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/pycryptodome', 'pkg:pypi/pycrypto']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: "Cryptodome.Cipher.ARC2.new(...)"
          - pattern: "Crypto.Cipher.ARC2.new"
    severity: "WARNING"
  - id: "python_crypto_rule-crypto-cipher-rc4"
    languages:
      - "python"
    message: |
      DES, TripleDES, RC2 and RC4 are all considered broken or insecure cryptographic algorithms.
      Newer algorithms apply message integrity to validate ciphertext has not been tampered
      with. Consider using `ChaCha20Poly1305` instead as it is easier and faster than the
      alternatives such as `AES-256-GCM`.

      For older applications that don't have support for `ChaCha20Poly1305`,
      `AES-256-GCM` is recommended, however it has many drawbacks:
      - Slower than `ChaCha20Poly1305`.
      - Catastrophic failure if nonce values are reused.

      Note that the `Crypto` and `Cryptodome` Python packages are no longer recommended for
      new applications, instead consider using the [cryptography](https://cryptography.io/) package.

      Example using `ChaCha20Poly1305`:
      ```
      import os
      # Import ChaCha20Poly1305 from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = ChaCha20Poly1305.generate_key()
      # Create a new ChaCha20Poly1305 instance with our secure key
      chacha = ChaCha20Poly1305(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = chacha.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      chacha.decrypt(nonce, cipher_text, aad)
      ```

      Example using `AESGCM`:
      ```
      import os
      # Import AESGCM from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import AESGCM
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = AESGCM.generate_key(bit_length=128)
      # Create a new AESGCM instance with our secure key
      aesgcm = AESGCM(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = aesgcm.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      aesgcm.decrypt(nonce, cipher_text, aad)
      ```

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/pycryptodome', 'pkg:pypi/pycrypto']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: "Cryptodome.Cipher.ARC4.new(...)"
          - pattern: "Crypto.Cipher.ARC4.new(...)"
    severity: "WARNING"
  - id: "python_crypto_rule-crypto-cipher-xor"
    languages:
      - "python"
    message: |
      The application was found using the `xor` algorithm, which can be trivially decoded.
      Newer algorithms apply message integrity to validate ciphertext has not been tampered
      with. Consider using `ChaCha20Poly1305` instead as it is easier and faster than the
      alternatives such as `AES-256-GCM`.

      For older applications that don't have support for `ChaCha20Poly1305`,
      `AES-256-GCM` is recommended, however it has many drawbacks:
      - Slower than `ChaCha20Poly1305`.
      - Catastrophic failure if nonce values are reused.

      Note that the `Crypto` and `Cryptodome` Python packages are no longer recommended for
      new applications, instead consider using the [cryptography](https://cryptography.io/) package.

      Example using `ChaCha20Poly1305`:
      ```
      import os
      # Import ChaCha20Poly1305 from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = ChaCha20Poly1305.generate_key()
      # Create a new ChaCha20Poly1305 instance with our secure key
      chacha = ChaCha20Poly1305(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = chacha.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      chacha.decrypt(nonce, cipher_text, aad)
      ```

      Example using `AESGCM`:
      ```
      import os
      # Import AESGCM from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import AESGCM
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = AESGCM.generate_key(bit_length=128)
      # Create a new AESGCM instance with our secure key
      aesgcm = AESGCM(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = aesgcm.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      aesgcm.decrypt(nonce, cipher_text, aad)
      ```

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/pycryptodome', 'pkg:pypi/pycrypto']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: "Cryptodome.Cipher.XOR.new(...)"
          - pattern: "Crypto.Cipher.XOR.new(...)"
    severity: "WARNING"
  - id: "python_crypto_rule-crypto-encrypt-dsa-rsa"
    languages:
      - "python"
    message: |
      The application is generating an RSA key that is less than the recommended 2048 bits.
      The National Institute of Standards and Technology (NIST) deprecated signing Digital
      Certificates that contained RSA Public Keys of 1024 bits in December 2010. While
      1024-bit RSA keys have not been factored yet, advances in compute may make it possible
      in the near future.

      Consider upgrading to the newer asymmetric algorithm such as `X25519` which handles
      the complexities of generating key pairs and choosing correct key sizes for you:
      ```
      from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey

      # Generate a private key for use in the exchange.
      private_key = X25519PrivateKey.generate()
      # Work with private key/exchange with a peer's
      # public key to created a shared and derived key
      # ...
      ```

      Otherwise use a key size greater than 2048 when generating RSA keys:
      ```
      from cryptography.hazmat.primitives.asymmetric import rsa
      # Generate a private key of 4096 bits
      private_key = rsa.generate_private_key(
          # do not change the exponent value from 65537
          public_exponent=65537,
          key_size=4096,
      )
      # Work with the private key to sign/encrypt data
      # ...
      ```

      For more information on using the cryptography module see:
      - https://cryptography.io/en/latest
    metadata:
      cwe: "CWE-326"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Inadequate encryption strength"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/pycryptodome', 'pkg:pypi/pycrypto', 'pkg:pypi/cryptography']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: |
              cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key(...,key_size=$SIZE,...)
          - pattern: |
              cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key($EXP, $SIZE,...)
          - pattern: |
              cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key($SIZE, ...)
          - pattern: |
              cryptography.hazmat.primitives.asymmetric.dsa.generate_private_key(...,key_size=$SIZE,...)
          - pattern: |
              cryptography.hazmat.primitives.asymmetric.dsa.generate_private_key($EXP, $SIZE, ...)
          - pattern: "cryptography.hazmat.primitives.asymmetric.dsa.generate_private_key($SIZE,...)"
          - pattern: "Crypto.PublicKey.RSA.generate($SIZE, ...)"
          - pattern: "Crypto.PublicKey.DSA.generate($SIZE, ...)"
          - pattern: "Cryptodome.PublicKey.DSA.generate($SIZE, ...)"
          - pattern: "Cryptodome.PublicKey.RSA.generate($SIZE, ...)"
          - pattern: "Crypto.PublicKey.DSA.generate(bits=$SIZE, ...)"
          - pattern: "Cryptodome.PublicKey.DSA.generate(bits=$SIZE, ...)"
          - pattern: "pycrypto_rsa.generate(bits=$SIZE, ...)"
          - pattern: "pycrypto_dsa.generate(bits=$SIZE, ...)"
          - pattern: "pycryptodomex_rsa.generate(bits=$SIZE, ...)"
          - pattern: "pycryptodomex_rsa.generate($SIZE, ...)"
          - pattern: "pycryptodomex_dsa.generate(bits=$SIZE, ...)"
          - pattern: "pycryptodomex_dsa.generate($SIZE, ...)"
      - metavariable-comparison:
          comparison: "$SIZE < 2048"
          metavariable: "$SIZE"
    severity: "ERROR"
  - id: "python_crypto_rule-crypto-encrypt-ec"
    languages:
      - "python"
    message: |
      The application was found using an insufficient curve size for the Elliptical
      Cryptography (EC) asymmetric algorithm. NIST recommends using a key size of
      224 or greater.

      To remediate this issue, replace the current key size with `ec.SECP384R1`,

      Example using `ec.SECP384R1`:
      ```
      from cryptography.hazmat.primitives.asymmetric import ec
      # Generate an EC private key using SECP384R1
      private_key = ec.generate_private_key(
          ec.SECP384R1()
      )
      # Work with/sign data using the key
      # ...
      ```

      For more information on the cryptography module's EC section see:
      - https://cryptography.io/en/latest/hazmat/primitives/asymmetric/ec/
    metadata:
      cwe: "CWE-326"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Inadequate encryption strength"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/cryptography']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |-
          cryptography.hazmat.primitives.asymmetric.ec.generate_private_key(...)
      - pattern: "cryptography.hazmat.primitives.asymmetric.ec.$SIZE"
      - metavariable-pattern:
          metavariable: "$SIZE"
          pattern-either:
            - pattern: "SECP192R1"
            - pattern: "SECT163K1"
            - pattern: "SECT163R2"
      - focus-metavariable: "$SIZE"
    severity: "ERROR"
  - id: "python_crypto_rule-crypto-hash-md5"
    languages:
      - "python"
    message: |
      The application was found using an insecure or risky digest or signature algorithm. MD5
      and SHA1 hash algorithms have been found to be vulnerable to producing collisions.

      This means
      that two different values, when hashed, can lead to the same hash value. If the application is
      trying
      to use these hash methods for storing passwords, then it is recommended to switch to a
      password hashing
      algorithm such as Argon2id or PBKDF2.
      It is strongly recommended that a standard digest algorithm be chosen instead as implementing
      a custom algorithm is prone to errors.

      Note that the `Crypto` and `Cryptodome` Python packages are no longer recommended for
      new applications, instead consider using the [cryptography](https://cryptography.io/) package.

      Example of creating a SHA-384 hash using the `cryptography` package:
      ```
      from cryptography.hazmat.primitives import hashes
      # Create a SHA384 digest
      digest = hashes.Hash(hashes.SHA384())
      # Update the digest with some initial data
      digest.update(b"some data to hash")
      # Add more data to the digest
      digest.update(b"some more data")
      # Finalize the digest as bytes
      result = digest.finalize()
      ```

      For more information on secure password storage see OWASP:
      - https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/pycryptodome', 'pkg:pypi/pycrypto']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: "Crypto.Hash.MD5.new(...)"
          - pattern: "Cryptodome.Hash.MD5.new (...)"
    severity: "WARNING"
  - id: "python_crypto_rule-crypto-hash-sha1"
    languages:
      - "python"
    message: |
      The application was found using an insecure or risky digest or signature algorithm. MD5
      and SHA1 hash algorithms have been found to be vulnerable to producing collisions.

      This means
      that two different values, when hashed, can lead to the same hash value. If the application is
      trying
      to use these hash methods for storing passwords, then it is recommended to switch to a
      password hashing
      algorithm such as Argon2id or PBKDF2.
      It is strongly recommended that a standard digest algorithm be chosen instead as implementing
      a custom algorithm is prone to errors.

      Note that the `Crypto` and `Cryptodome` Python packages are no longer recommended for
      new applications, instead consider using the [cryptography](https://cryptography.io/) package.

      Example of creating a SHA-384 hash using the `cryptography` package:
      ```
      from cryptography.hazmat.primitives import hashes
      # Create a SHA384 digest
      digest = hashes.Hash(hashes.SHA384())
      # Update the digest with some initial data
      digest.update(b"some data to hash")
      # Add more data to the digest
      digest.update(b"some more data")
      # Finalize the digest as bytes
      result = digest.finalize()
      ```

      For more information on secure password storage see OWASP:
      - https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/pycryptodome', 'pkg:pypi/pycrypto']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: "Crypto.Hash.SHA.new(...)"
          - pattern: "Cryptodome.Hash.SHA.new (...)"
    severity: "WARNING"
  - id: "python_crypto_rule-crypto-hazmat-cipher-arc4"
    languages:
      - "python"
    message: |
      DES, TripleDES, RC2 and RC4 are all considered broken or insecure cryptographic algorithms.
      Newer algorithms apply message integrity to validate ciphertext has not been tampered
      with. Consider using `ChaCha20Poly1305` instead as it is easier and faster than the
      alternatives such as `AES-256-GCM`.

      For older applications that don't have support for `ChaCha20Poly1305`,
      `AES-256-GCM` is recommended, however it has many drawbacks:
      - Slower than `ChaCha20Poly1305`.
      - Catastrophic failure if nonce values are reused.

      Example using `ChaCha20Poly1305`:
      ```
      import os
      # Import ChaCha20Poly1305 from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = ChaCha20Poly1305.generate_key()
      # Create a new ChaCha20Poly1305 instance with our secure key
      chacha = ChaCha20Poly1305(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = chacha.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      chacha.decrypt(nonce, cipher_text, aad)
      ```

      Example using `AESGCM`:
      ```
      import os
      # Import AESGCM from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import AESGCM
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = AESGCM.generate_key(bit_length=128)
      # Create a new AESGCM instance with our secure key
      aesgcm = AESGCM(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = aesgcm.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      aesgcm.decrypt(nonce, cipher_text, aad)
      ```

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/cryptography']
      license: MIT (c) GitLab Inc.
    pattern: "cryptography.hazmat.primitives.ciphers.algorithms.ARC4(...)"
    severity: "WARNING"
  - id: "python_crypto_rule-crypto-hazmat-cipher-blowfish"
    languages:
      - "python"
    message: |
      The Blowfish encryption algorithm was meant as a drop-in replacement for DES and was created in
      1993. Smaller key sizes may make the ciphertext vulnerable to [birthday
      attacks](https://en.wikipedia.org/wiki/Birthday_attack). While no known attacks against
      Blowfish
      exist, it should never be used to encrypt files over 4GB in size. If possible consider
      using ChaCha20Poly1305 or AES-GCM instead of Blowfish.

      For older applications that don't have support for `ChaCha20Poly1305`, `AES-256-GCM` is
      recommended, however it has many drawbacks:
        - Slower than `ChaCha20Poly1305`.
        - Catastrophic failure if nonce values are reused.

      Example using `ChaCha20Poly1305`:
      ```
      import os
      # Import ChaCha20Poly1305 from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = ChaCha20Poly1305.generate_key()
      # Create a new ChaCha20Poly1305 instance with our secure key
      chacha = ChaCha20Poly1305(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = chacha.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      chacha.decrypt(nonce, cipher_text, aad)
      ```

      Example using `AESGCM`:
      ```
      import os
      # Import AESGCM from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import AESGCM
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = AESGCM.generate_key(bit_length=128)
      # Create a new AESGCM instance with our secure key
      aesgcm = AESGCM(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = aesgcm.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      aesgcm.decrypt(nonce, cipher_text, aad)
      ```

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/cryptography']
      license: MIT (c) GitLab Inc.
    pattern: "cryptography.hazmat.primitives.ciphers.algorithms.Blowfish(...)"
    severity: "WARNING"
  - id: "python_crypto_rule-crypto-hazmat-cipher-idea"
    languages:
      - "python"
    message: |
      The IDEA encryption algorithm was meant as a drop-in replacement for DES and was created in
      1991. A number of [vulnerabilities and
      exploits](https://en.wikipedia.org/wiki/International_Data_Encryption_Algorithm#Security) have
      been identified to work against IDEA and
      it is no longer recommended. If possible consider
      using ChaCha20Poly1305 or AES-GCM instead of Blowfish.

      For older applications that don't have support for `ChaCha20Poly1305`, `AES-256-GCM` is
      recommended, however it has many drawbacks:
        - Slower than `ChaCha20Poly1305`.
        - Catastrophic failure if nonce values are reused.

      Example using `ChaCha20Poly1305`:
      ```
      import os
      # Import ChaCha20Poly1305 from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = ChaCha20Poly1305.generate_key()
      # Create a new ChaCha20Poly1305 instance with our secure key
      chacha = ChaCha20Poly1305(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = chacha.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      chacha.decrypt(nonce, cipher_text, aad)
      ```

      Example using `AESGCM`:
      ```
      import os
      # Import AESGCM from cryptography
      from cryptography.hazmat.primitives.ciphers.aead import AESGCM
      # Our plaintext to encrypt
      plain_text = b"Secret text to encrypt"
      # We do not require authenticated but unencrypted data, so set to None
      aad = None
      # Generate a secure key
      key = AESGCM.generate_key(bit_length=128)
      # Create a new AESGCM instance with our secure key
      aesgcm = AESGCM(key)
      # Note: nonce values _must_ be regenerated every time they are used.
      nonce = os.urandom(12)
      # Encrypt our plaintext
      cipher_text = aesgcm.encrypt(nonce, plain_text, aad)
      # Decrypt the plain text using the nonce and cipher_text
      aesgcm.decrypt(nonce, cipher_text, aad)
      ```

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      shortDescription: "Use of a Broken or Risky Cryptographic Algorithm"
      security-severity: "Medium"
      category: "security"
      related-packages: ['pkg:pypi/cryptography']
      license: MIT (c) GitLab Inc.
    pattern: "cryptography.hazmat.primitives.ciphers.algorithms.IDEA(...)"
    severity: "WARNING"
  - id: "python_crypto_rule-crypto.hazmat-hash-md5"
    languages:
      - "python"
    message: |
      The application was found using an insecure or risky digest or signature algorithm. MD2, MD5
      and SHA1 hash algorithms have been found to be vulnerable to producing collisions.

      This means
      that two different values, when hashed, can lead to the same hash value. If the application is
      trying
      to use these hash methods for storing passwords, then it is recommended to switch to a
      password hashing
      algorithm such as Argon2id or PBKDF2.
      It is strongly recommended that a standard digest algorithm be chosen instead as implementing
      a custom algorithm is prone to errors.

      Example of creating a SHA-384 hash using the `cryptography` package:
      ```
      from cryptography.hazmat.primitives import hashes
      # Create a SHA384 digest
      digest = hashes.Hash(hashes.SHA384())
      # Update the digest with some initial data
      digest.update(b"some data to hash")
      # Add more data to the digest
      digest.update(b"some more data")
      # Finalize the digest as bytes
      result = digest.finalize()
      ```

      For more information on secure password storage see OWASP:
      - https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      category: "security"
      related-packages: ['pkg:pypi/cryptography']
      license: MIT (c) GitLab Inc.
    pattern: "cryptography.hazmat.primitives.hashes.MD5(...)"
    severity: "WARNING"
  - id: "python_crypto_rule-crypto.hazmat-hash-sha1"
    languages:
      - "python"
    message: |
      The application was found using an insecure or risky digest or signature algorithm. MD2, MD5
      and SHA1 hash algorithms have been found to be vulnerable to producing collisions.

      This means
      that two different values, when hashed, can lead to the same hash value. If the application is
      trying
      to use these hash methods for storing passwords, then it is recommended to switch to a
      password hashing
      algorithm such as Argon2id or PBKDF2.
      It is strongly recommended that a standard digest algorithm be chosen instead as implementing
      a custom algorithm is prone to error.

      Example of creating a SHA-384 hash using the `cryptography` package:
      ```
      from cryptography.hazmat.primitives import hashes
      # Create a SHA384 digest
      digest = hashes.Hash(hashes.SHA384())
      # Update the digest with some initial data
      digest.update(b"some data to hash")
      # Add more data to the digest
      digest.update(b"some more data")
      # Finalize the digest as bytes
      result = digest.finalize()
      ```

      For more information on secure password storage see OWASP:
      - https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/cryptography']
      license: MIT (c) GitLab Inc.
    pattern: "cryptography.hazmat.primitives.hashes.SHA1(...)"
    severity: "WARNING"
  - id: "python_crypto_rule-hash-md2"
    languages:
      - "python"
    message: |
      The application was found using an insecure or risky digest or signature algorithm. MD2, MD5
      and SHA1 hash algorithms have been found to be vulnerable to producing collisions.

      This means
      that two different values, when hashed, can lead to the same hash value. If the application is
      trying
      to use these hash methods for storing passwords, then it is recommended to switch to a
      password hashing
      algorithm such as Argon2id or PBKDF2.

      Note that the `Crypto` and `Cryptodome` Python packages are no longer recommended for
      new applications, instead consider using the [cryptography](https://cryptography.io/) package.

      Example of creating a SHA-384 hash using the `cryptography` package:
      ```
      from cryptography.hazmat.primitives import hashes
      # Create a SHA384 digest
      digest = hashes.Hash(hashes.SHA384())
      # Update the digest with some initial data
      digest.update(b"some data to hash")
      # Add more data to the digest
      digest.update(b"some more data")
      # Finalize the digest as bytes
      result = digest.finalize()
      ```

      For more information on secure password storage see OWASP:
      - https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/pycryptodome', 'pkg:pypi/pycrypto']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: "Crypto.Hash.MD2.new(...)"
          - pattern: "Cryptodome.Hash.MD2.new (...)"
    severity: "WARNING"
  - id: "python_crypto_rule-hash-md4"
    languages:
      - "python"
    message: |
      The application was found using an insecure or risky digest or signature algorithm. MD2, MD4,
       MD5  and SHA1 hash algorithms have been found to be vulnerable to producing collisions.

      This means
      that two different values, when hashed, can lead to the same hash value. If the application is
      trying
      to use these hash methods for storing passwords, then it is recommended to switch to a
      password hashing
      algorithm such as Argon2id or PBKDF2.

      Note that the `Crypto` and `Cryptodome` Python packages are no longer recommended for
      new applications, instead consider using the [cryptography](https://cryptography.io/) package.

      Example of creating a SHA-384 hash using the `cryptography` package:
      ```
      from cryptography.hazmat.primitives import hashes
      # Create a SHA384 digest
      digest = hashes.Hash(hashes.SHA384())
      # Update the digest with some initial data
      digest.update(b"some data to hash")
      # Add more data to the digest
      digest.update(b"some more data")
      # Finalize the digest as bytes
      result = digest.finalize()
      ```

      For more information on secure password storage see OWASP:
      - https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/pycryptodome', 'pkg:pypi/pycrypto']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: "Crypto.Hash.MD4.new(...)"
          - pattern: "Cryptodome.Hash.MD4.new (...)"
    severity: "WARNING"
  - id: "python_crypto_rule-hash-md5"
    languages:
      - "python"
    message: |
      The application was found using an insecure or risky digest or signature algorithm. MD2, MD4,
       MD5  and SHA1 hash algorithms have been found to be vulnerable to producing collisions.

      This means
      that two different values, when hashed, can lead to the same hash value. If the application is
      trying
      to use these hash methods for storing passwords, then it is recommended to switch to a
      password hashing
      algorithm such as Argon2id or PBKDF2.

      Note that the `Crypto` and `Cryptodome` Python packages are no longer recommended for
      new applications, instead consider using the [cryptography](https://cryptography.io/) package.

      Example of creating a SHA-384 hash using the `cryptography` package:
      ```
      from cryptography.hazmat.primitives import hashes
      # Create a SHA384 digest
      digest = hashes.Hash(hashes.SHA384())
      # Update the digest with some initial data
      digest.update(b"some data to hash")
      # Add more data to the digest
      digest.update(b"some more data")
      # Finalize the digest as bytes
      result = digest.finalize()
      ```

      For more information on secure password storage see OWASP:
      - https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/hashlib']
      license: MIT (c) GitLab Inc.
    pattern: "hashlib.md5(...)"
    severity: "WARNING"
  - id: "python_crypto_rule-hash-sha1"
    languages:
      - "python"
    message: |
      The application was found using an insecure or risky digest or signature algorithm. MD2, MD4,
       MD5  and SHA1 hash algorithms have been found to be vulnerable to producing collisions.

      This means
      that two different values, when hashed, can lead to the same hash value. If the application is
      trying
      to use these hash methods for storing passwords, then it is recommended to switch to a
      password hashing
      algorithm such as Argon2id or PBKDF2.

      Note that the `Crypto` and `Cryptodome` Python packages are no longer recommended for
      new applications, instead consider using the [cryptography](https://cryptography.io/) package.

      Example of creating a SHA-384 hash using the `cryptography` package:
      ```
      from cryptography.hazmat.primitives import hashes
      # Create a SHA384 digest
      digest = hashes.Hash(hashes.SHA384())
      # Update the digest with some initial data
      digest.update(b"some data to hash")
      # Add more data to the digest
      digest.update(b"some more data")
      # Finalize the digest as bytes
      result = digest.finalize()
      ```

      For more information on secure password storage see OWASP:
      - https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html

      For more information on the cryptography module see:
      - https://cryptography.io/en/latest/
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/hashlib']
      license: MIT (c) GitLab Inc.
    pattern: "hashlib.sha1(...)"
    severity: "WARNING"
  - id: "python_crypto_rule-hashlib-new-insecure-functions"
    languages:
      - "python"
    message: |
      The application was found using an insecure or risky digest or signature algorithm. MD2, MD4,
      MD5  and SHA1 hash algorithms have been found to be vulnerable to producing collisions.

      This means
      that two different values, when hashed, can lead to the same hash value. If the application is
      trying
      to use these hash methods for storing passwords, then it is recommended to switch to a
      password hashing
      algorithm such as Argon2id or PBKDF2.
      It is strongly recommended that a standard digest algorithm be chosen instead as implementing
      a custom algorithm is prone to errors.

      Example using `hashlib.sha384()` to create a SHA384 hash:
      ```
      import hashlib
      # Create a SHA384 digest
      digest = hashlib.sha384()
      # Update the digest with some initial data
      digest.update(b"some data to hash")
      # Add more data to the digest
      digest.update(b"some more data")
      # Finalize the digest as bytes
      digest.digest()
      ```
    metadata:
      cwe: "CWE-327"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/hashlib']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: "hashlib.new(\"=~/[M|m][D|d][4|5]/\", ...)"
          - pattern: "hashlib.new(..., name=\"=~/[M|m][D|d][4|5]/\", ...)"
          - pattern: "hashlib.new('sha1')"
          - pattern: "hashlib.new(..., name='SHA1')"
          - pattern: "hashlib.new('sha', string='test')"
          - pattern: "hashlib.new(name='SHA', string='test')"
    severity: "WARNING"
  - id: "python_crypto_rule-import-pycrypto"
    languages:
      - "python"
    message: |
      The application was detected importing `pycrypto`. This package has been deprecated as it
      contains
      security vulnerabilities.

      To remediate this issue, consider using the [cryptography](https://cryptography.io/)
      package instead.
    metadata:
      cwe: "CWE-1104"
      owasp:
        - "A9:2017-Using Components with Known Vulnerabilities"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of unmaintained third party components"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/pycrypto']
      license: MIT (c) GitLab Inc.
    pattern-either:
      - pattern: "import pycryto"
      - pattern: "import Crypto.Cipher"
      - pattern: "import Crypto.Hash"
      - pattern: "import Crypto.IO"
      - pattern: "import Crypto.Protocol"
      - pattern: "import Crypto.PublicKey"
      - pattern: "import Crypto.Random"
      - pattern: "import Crypto.Signature"
      - pattern: "import Crypto.Util"
    severity: "ERROR"
  - id: "python_deserialization_rule-cpickle"
    languages:
      - "python"
    message: |
      The application was found using `cPickle` which is vulnerable to deserialization attacks.
      Deserialization attacks exploit the process of reading serialized data and turning it back
      into an object. By constructing malicious objects and serializing them, an adversary may
      attempt to:

      - Inject code that is executed upon object construction, which occurs during the
      deserialization process.
      - Exploit mass assignment by including fields that are not normally a part of the serialized
      data but are read in during deserialization.

      Consider safer alternatives such as serializing data in the JSON format. Ensure any format
      chosen allows
      the application to specify exactly which object types are allowed to be deserialized.

      To protect against mass assignment, only allow deserialization of the specific fields that are
      required. If this is not easily done, consider creating an intermediary type that
      can be serialized with only the necessary fields exposed.

      Example JSON deserializer using an intermediary type that is validated against a schema to
      ensure
      it is safe from mass assignment:
      ```
      import jsonschema

      # Create a schema to validate our user-supplied input against
      # an intermediary object
      intermediary_schema = {
          "type" : "object",
          "properties" :  {
              "name": {"type" : "string"}
          },
          "required": ["name"],
          # Protect against random properties being added to the object
          "additionalProperties": False,
      }
      # If a user attempted to add "'is_admin': True" it would cause a validation error
      intermediary_object = {'name': 'test user'}

      try:
          # Validate the user supplied intermediary object against our schema
          jsonschema.validate(instance=intermediary_object, schema=intermediary_schema)
          user_object = {'user':
              {
                  # Assign the deserialized data from intermediary object
                  'name': intermediary_object['name'],
                  # Add in protected data in object definition (or set it from a class constructor)
                  'is_admin': False,
              }
          }
          # Work with the user_object
      except jsonschema.exceptions.ValidationError as ex:
          # Gracefully handle validation errors
          # ...
      ```

      For more details on deserialization attacks in general, see OWASP's guide:
      - https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
    metadata:
      cwe: "CWE-502"
      owasp:
        - "A8:2017-Insecure Deserialization"
        - "A08:2021-Software and Data Integrity Failures"
      category: "security"
      shortDescription: "Deserialization of untrusted data"
      security-severity: "High"
      license: MIT (c) GitLab Inc.
    pattern: "cPickle.$FUNC(...)"
    severity: "WARNING"
  - id: "python_deserialization_rule-dill"
    languages:
      - "python"
    message: |
      The application was found using `dill` which is vulnerable to deserialization attacks.
      Deserialization attacks exploit the process of reading serialized data and turning it back
      into an object. By constructing malicious objects and serializing them, an adversary may
      attempt to:

      - Inject code that is executed upon object construction, which occurs during the
      deserialization process.
      - Exploit mass assignment by including fields that are not normally a part of the serialized
      data but are read in during deserialization.

      Consider safer alternatives such as serializing data in the JSON format. Ensure any format
      chosen allows
      the application to specify exactly which object types are allowed to be deserialized.

      To protect against mass assignment, only allow deserialization of the specific fields that are
      required. If this is not easily done, consider creating an intermediary type that
      can be serialized with only the necessary fields exposed.

      Example JSON deserializer using an intermediary type that is validated against a schema to
      ensure
      it is safe from mass assignment:
      ```
      import jsonschema

      # Create a schema to validate our user-supplied input against
      # an intermediary object
      intermediary_schema = {
          "type" : "object",
          "properties" :  {
              "name": {"type" : "string"}
          },
          "required": ["name"],
          # Protect against random properties being added to the object
          "additionalProperties": False,
      }
      # If a user attempted to add "'is_admin': True" it would cause a validation error
      intermediary_object = {'name': 'test user'}

      try:
          # Validate the user supplied intermediary object against our schema
          jsonschema.validate(instance=intermediary_object, schema=intermediary_schema)
          user_object = {'user':
              {
                  # Assign the deserialized data from intermediary object
                  'name': intermediary_object['name'],
                  # Add in protected data in object definition (or set it from a class constructor)
                  'is_admin': False,
              }
          }
          # Work with the user_object
      except jsonschema.exceptions.ValidationError as ex:
          # Gracefully handle validation errors
          # ...
      ```

      For more details on deserialization attacks in general, see OWASP's guide:
      - https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
    metadata:
      cwe: "CWE-502"
      owasp:
        - "A8:2017-Insecure Deserialization"
        - "A08:2021-Software and Data Integrity Failures"
      category: "security"
      shortDescription: "Deserialization of untrusted data"
      security-severity: "High"
      related-packages: ['pkg:pypi/dill']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: dill.$FUNC(...)
      - pattern-not: dill.$FUNC("...")
    severity: "WARNING"
  - id: "python_deserialization_rule-marshal"
    languages:
      - "python"
    message: |
      The application was found using `dill` which is vulnerable to deserialization attacks.
      Deserialization attacks exploit the process of reading serialized data and turning it back
      into an object. By constructing malicious objects and serializing them, an adversary may
      attempt to:

      - Inject code that is executed upon object construction, which occurs during the
      deserialization process.
      - Exploit mass assignment by including fields that are not normally a part of the serialized
      data but are read in during deserialization.

      Consider safer alternatives such as serializing data in the JSON format. Ensure any format
      chosen allows
      the application to specify exactly which object types are allowed to be deserialized.

      To protect against mass assignment, only allow deserialization of the specific fields that are
      required. If this is not easily done, consider creating an intermediary type that
      can be serialized with only the necessary fields exposed.

      Example JSON deserializer using an intermediary type that is validated against a schema to
      ensure
      it is safe from mass assignment:
      ```
      import jsonschema

      # Create a schema to validate our user-supplied input against
      # an intermediary object
      intermediary_schema = {
          "type" : "object",
          "properties" :  {
              "name": {"type" : "string"}
          },
          "required": ["name"],
          # Protect against random properties being added to the object
          "additionalProperties": False,
      }
      # If a user attempted to add "'is_admin': True" it would cause a validation error
      intermediary_object = {'name': 'test user'}

      try:
          # Validate the user supplied intermediary object against our schema
          jsonschema.validate(instance=intermediary_object, schema=intermediary_schema)
          user_object = {'user':
              {
                  # Assign the deserialized data from intermediary object
                  'name': intermediary_object['name'],
                  # Add in protected data in object definition (or set it from a class constructor)
                  'is_admin': False,
              }
          }
          # Work with the user_object
      except jsonschema.exceptions.ValidationError as ex:
          # Gracefully handle validation errors
          # ...
      ```

      For more details on deserialization attacks in general, see OWASP's guide:
      - https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
    metadata:
      cwe: "CWE-502"
      owasp:
        - "A8:2017-Insecure Deserialization"
        - "A08:2021-Software and Data Integrity Failures"
      category: "security"
      shortDescription: "Deserialization of untrusted data"
      security-severity: "High"
      license: MIT (c) GitLab Inc.
    pattern-either:
      - pattern: "marshal.dump(...)"
      - pattern: "marshal.dumps(...)"
      - pattern: "marshal.load(...)"
      - pattern: "marshal.loads(...)"
    severity: "WARNING"
  - id: "python_deserialization_rule-pickle"
    languages:
      - "python"
    message: |
      The application was found using `pickle` which is vulnerable to deserialization attacks.
      Deserialization attacks exploit the process of reading serialized data and turning it back
      into an object. By constructing malicious objects and serializing them, an adversary may
      attempt to:

      - Inject code that is executed upon object construction, which occurs during the
      deserialization process.
      - Exploit mass assignment by including fields that are not normally a part of the serialized
      data but are read in during deserialization.

      Consider safer alternatives such as serializing data in the JSON format. Ensure any format
      chosen allows the application to specify exactly which object types are allowed to be deserialized.

      To protect against mass assignment, only allow deserialization of the specific fields that are
      required. If this is not easily done, consider creating an intermediary type that
      can be serialized with only the necessary fields exposed.

      Example JSON deserializer using an intermediary type that is validated against a schema to ensure
      it is safe from mass assignment:
      ```
      import jsonschema

      # Create a schema to validate our user-supplied input against
      # an intermediary object
      intermediary_schema = {
          "type" : "object",
          "properties" :  {
              "name": {"type" : "string"}
          },
          "required": ["name"],
          # Protect against random properties being added to the object
          "additionalProperties": False,
      }
      # If a user attempted to add "'is_admin': True" it would cause a validation error
      intermediary_object = {'name': 'test user'}

      try:
          # Validate the user supplied intermediary object against our schema
          jsonschema.validate(instance=intermediary_object, schema=intermediary_schema)
          user_object = {'user':
              {
                  # Assign the deserialized data from intermediary object
                  'name': intermediary_object['name'],
                  # Add in protected data in object definition (or set it from a class constructor)
                  'is_admin': False,
              }
          }
          # Work with the user_object
      except jsonschema.exceptions.ValidationError as ex:
          # Gracefully handle validation errors
          # ...
      ```

      For more details on deserialization attacks in general, see OWASP's guide:
      - https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
    metadata:
      cwe: "CWE-502"
      owasp:
        - "A8:2017-Insecure Deserialization"
        - "A08:2021-Software and Data Integrity Failures"
      category: "security"
      shortDescription: "Deserialization of untrusted data"
      security-severity: "High"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - patterns:
              - pattern: "pickle.$METHOD(...)"
              - pattern-not: "pickle.$METHOD(\"...\")"
          - patterns:
              - pattern: "_pickle.$METHOD(...)"
              - pattern-not: "_pickle.$METHOD(\"...\")"
      - metavariable-regex:
          metavariable: "$METHOD"
          regex: "(load|loads|Unpickler)"
    severity: "WARNING"
  - id: "python_deserialization_rule-shelve"
    languages:
      - "python"
    message: |
      The application was found using `shelve` which is vulnerable to deserialization attacks as
      it calls `pickle` internally.
      Deserialization attacks exploit the process of reading serialized data and turning it back
      into an object. By constructing malicious objects and serializing them, an adversary may
      attempt to:

      - Inject code that is executed upon object construction, which occurs during the
      deserialization process.
      - Exploit mass assignment by including fields that are not normally a part of the serialized
      data but are read in during deserialization.

      Consider safer alternatives such as serializing data in the JSON format. Ensure any format
      chosen allows
      the application to specify exactly which object types are allowed to be deserialized.

      To protect against mass assignment, only allow deserialization of the specific fields that are
      required. If this is not easily done, consider creating an intermediary type that
      can be serialized with only the necessary fields exposed.

      Example JSON deserializer using an intermediary type that is validated against a schema to
      ensure
      it is safe from mass assignment:
      ```
      import jsonschema

      # Create a schema to validate our user-supplied input against
      # an intermediary object
      intermediary_schema = {
          "type" : "object",
          "properties" :  {
              "name": {"type" : "string"}
          },
          "required": ["name"],
          # Protect against random properties being added to the object
          "additionalProperties": False,
      }
      # If a user attempted to add "'is_admin': True" it would cause a validation error
      intermediary_object = {'name': 'test user'}

      try:
          # Validate the user supplied intermediary object against our schema
          jsonschema.validate(instance=intermediary_object, schema=intermediary_schema)
          user_object = {'user':
              {
                  # Assign the deserialized data from intermediary object
                  'name': intermediary_object['name'],
                  # Add in protected data in object definition (or set it from a class constructor)
                  'is_admin': False,
              }
          }
          # Work with the user_object
      except jsonschema.exceptions.ValidationError as ex:
          # Gracefully handle validation errors
          # ...
      ```

      For more details on deserialization attacks in general, see OWASP's guide:
      - https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
    metadata:
      cwe: "CWE-502"
      owasp:
        - "A8:2017-Insecure Deserialization"
        - "A08:2021-Software and Data Integrity Failures"
      category: "security"
      shortDescription: "Deserialization of untrusted data"
      security-severity: "High"
      license: MIT (c) GitLab Inc.
    pattern-either:
      - pattern: "shelve.$FUNC(...)"
    severity: "WARNING"
  - id: "python_deserialization_rule-yaml-load"
    languages:
      - "python"
    message: |
      The application was found using an unsafe version of `yaml` load which is vulnerable to
      deserialization attacks. Deserialization attacks exploit the process of reading serialized
      data and turning it back
      into an object. By constructing malicious objects and serializing them, an adversary may
      attempt to:

      - Inject code that is executed upon object construction, which occurs during the
      deserialization process.
      - Exploit mass assignment by including fields that are not normally a part of the serialized
      data but are read in during deserialization.

      To remediate this issue, use `safe_load()` or call `yaml.load()` with the `Loader` argument
      set to
      `yaml.SafeLoader`.

      Example loading YAML using `safe_load`:
      ```
      import yaml

      # Use safe_load to load data into an intermediary object
      intermediary_object = yaml.safe_load("""user:
          name: 'test user'"""
      )
      # Create our real object, copying over only the necessary fields
      user_object = {'user': {
              # Assign the deserialized data from intermediary object
              'name': intermediary_object['user']['name'],
              # Add in protected data in object definition (or set it from a class constructor)
              'is_admin': False,
          }
      }
      # Work with user_object
      # ...
      ```

      For more details on deserialization attacks in general, see OWASP's guide:
      - https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
    patterns:
      - pattern-inside: |
          import yaml
          ...
      - pattern-not-inside: |
          from ruamel.yaml import YAML
          ...
      - pattern-either:
          - pattern: "yaml.unsafe_load(...)"
          - pattern: "yaml.$LD(..., Loader=yaml.$LOADER, ...)"
          - pattern: "yaml.$LD($DATA)"
      - metavariable-regex:
          metavariable: "$LOADER"
          regex: "(Loader|UnsafeLoader|CLoader|FullLoader)"
      - metavariable-regex:
          metavariable: "$LD"
          regex: "(load|load_all)"
    metadata:
      cwe: "CWE-502"
      owasp:
        - "A8:2017-Insecure Deserialization"
        - "A08:2021-Software and Data Integrity Failures"
      category: "security"
      shortDescription: "Deserialization of untrusted data"
      security-severity: "High"
      license: MIT (c) GitLab Inc.
    severity: "ERROR"
  - id: "python_django_rule-django-extra-used"
    languages:
      - "python"
    message: |
      SQL Injection is a critical vulnerability that can lead to data or system compromise. By
      dynamically generating SQL query strings, user input may be able to influence the logic of
      the SQL statement. This could lead to an adversary accessing information they should
      not have access to, or in some circumstances, being able to execute OS functionality or code.

      Replace all dynamically generated SQL queries with parameterized queries. In situations where
      dynamic queries must be created, never use direct user input, but instead use a map or
      dictionary of valid values and resolve them using a user supplied key.

      For example, some database drivers do not allow parameterized queries for `>` or `<` comparison
      operators. In these cases, do not use a user supplied `>` or `<` value, but rather have the
      user
      supply a `gt` or `lt` value. The alphabetical values are then used to look up the `>` and `<`
      values to be used in the construction of the dynamic query. The same goes for other queries
      where
      column or table names are required but cannot be parameterized.

      The `QuerySet.extra` API method will be deprecated as it a source of SQL Injection
      vulnerabilities and other problems. This method is especially risky as callers
      will need to do their own escaping of any parameters that come from user-supplied
      information.

      To remediate this issue, do not use `extra` but use other `QuerySet` methods to achieve
      the same goals. If for some reason this is not feasible, consider using the `RawSQL` method
      and making sure that all arguments, including user-supplied ones, are only used in
      `params`


      While not recommended due to [potential SQL
      Injection](https://docs.djangoproject.com/en/4.2/ref/models/expressions/#raw-sql-expressions),
      below is an example using `RawSQL`,
      passing in user-supplied data as a `param` which will escape the input:
      ```
      # If dealing with integer based user input, restrict the values to integers only using the
      # path configuration: path('<int:user_supplied_id>/someview/', views.some_view,
      name='someview'),

      # views.py
      def some_view(request, user_supplied_id):
        # Never use string interpolation in the `sql` parameter.
        # Never quote the `%s` string format such as `... where id='%s'` as this could lead to SQL
      Injection.
        # Pass the user supplied data only in the `params` parameter.
        for obj in DBObject.objects.all().annotate(
            val=RawSQL(sql="select id from some_secondary_table where id=%s",
      params=[user_supplied_id])):
          # Work with the results from the query
          # ...
      ```

      For more information on QuerySet see:
      - https://docs.djangoproject.com/en/4.2/ref/models/querysets/#queryset-api

      For more information on SQL Injection see OWASP:
      - https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
    patterns:
      - pattern-inside: |
          import django
          ...
      - pattern-either:
          - pattern: "$X.objects. ... .extra(..., $K = $V, ...)"
          - patterns:
              - pattern-inside: |
                  $Q = $X.objects. ...
                  ...
              - pattern: $Q.extra(..., $K = $V, ...)

      - pattern-not-inside: |
          $V = ['...']
          ...


      - metavariable-pattern:
          metavariable: "$V"
          pattern-either:
            - patterns:
                - pattern-not: "[..., '...', ...]"
                - pattern-not: "{..., '...': '...', ...}"
                - pattern-not: "\"...\""
                - pattern-not: "[..., \"...\" % \"...\", ...]"
                - pattern-not: "{..., $L: \"...\" % \"...\", ...}"
                - pattern-not: "{..., $L: \"...\".format(\"...\"), ...}"
                - pattern-not: "[..., \"...\".format(\"...\"), ...]"
            - pattern-regex: \.*['"]%[a-z]['"].*

      - metavariable-pattern:
          metavariable: "$K"
          patterns:
            - pattern-not: params




    metadata:
      cwe: "CWE-89"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      related-packages: ['pkg:pypi/django']
      category: "security"
      shortDescription: "Improper neutralization of special elements used in an SQL Command ('SQL Injection')"
      security-severity: "High"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: python_django_rule_reflected_xss_csp
    languages:
      - python
    severity: ERROR
    message: |
      Unsecure Content Security Policy detected in Django. Avoid usage of 'unsafe-inline', 'unsafe-eval' or 'unsafe-hashes' in the CSP. 
      Avoid overly permissive CSP (containing '*'). Use 'self' or allow lists instead. 
    metadata:
      cwe: CWE-79
      owasp:
        - A7:2017-Cross-Site Scripting (XSS)
        - A03:2021-Injection
      related-packages:
        - pkg:pypi/django
        - pkg:pypi/django-csp
      category: security
      shortDescription: Improper neutralization of user input rendered in HTML ('XSS')
      security-severity: High
      confidence: Medium
      license: License Not Found
    pattern-either:
      - pattern: $CSP_PARAM = (<... "'unsafe-inline'" ...>)
      - pattern: $CSP_PARAM = (<... "'unsafe-eval'" ...>)
      - pattern: $CSP_PARAM = (<... "'unsafe-hashes'" ...>)
      - pattern: $CSP_PARAM = (<... "*" ...>)


  - id: python_django_rule_reflected_xss_global
    languages:
      - python
    severity: ERROR
    message: |
      A reflected XSS vulnerability was detected in Django due to an insecure global parameter: either the security headers SECURE_BROWSER_XSS_FILTER or SECURE_CONTENT_TYPE_NOSNIFF were set to False (they should be True), ALLOWED_HOSTS was overly permissive (e.g., '*' instead of 'self' or an allowlist), auto-escaping was disabled globally (autoescape: False), or a class was incorrectly decorated with @html_safe (which bypasses escaping for all its string output, risking XSS if the class processes untrusted data).
    metadata:
      cwe: CWE-79
      owasp:
        - A7:2017-Cross-Site Scripting (XSS)
        - A03:2021-Injection
      related-packages:
        - pkg:pypi/django
      category: security
      shortDescription: Improper neutralization of user input rendered in HTML ('XSS') in Django
      security-severity: High
      confidence: High
      license: License Not Found
    pattern-either:
      - patterns:
          - pattern-inside: |
              from django.utils.safestring import html_safe
              ...
          - pattern: |
              @html_safe
              class $C(...):
          - focus-metavariable: $C
      - pattern-regex: '.*\"autoescape\"\: False'
      - pattern: <... SECURE_BROWSER_XSS_FILTER ...> = <... False ...>
      - pattern: <... SECURE_CONTENT_TYPE_NOSNIFF ...> = <... False ...>
      - pattern: <... 'SECURE_BROWSER_XSS_FILTER' ...> = <... False ...>
      - pattern: <... 'SECURE_CONTENT_TYPE_NOSNIFF' ...> = <... False ...>
      - pattern: <... ALLOWED_HOSTS ...> = <... '*' ...>
  - id: python_django_rule_reflected_xss_httpresponse
    languages:
      - python
    severity: ERROR
    message: |
      A reflected Cross-Site Scripting (XSS) vulnerability was identified in Django. By default, Django’s HttpResponse bypasses Django's automatic escaping, making it unsafe to use with unescaped user-controlled input. 
      To mitigate this, use Django’s auto-escaping render() method instead of HttpResponse or manually sanitize inputs with django.utils.html.escape/format_html before passing them to HttpResponse. 
      Be careful when using format_html not to use f-string with the user controlled input nested in it, the escaping would be cancelled. instead, use it this way: format_html("<b>{}</b>", user_input). 
      Ensure to be aware of the context in which the user controlled data will be rendered: if it is embedded in CSS or <script> tags, you need to use a dedicated escaping method as django.utils.html.escapejs. Django's escape methods is meant to be use for HTML sanitizing!
    metadata:
      cwe: CWE-79
      owasp:
        - A7:2017-Cross-Site Scripting (XSS)
        - A03:2021-Injection
      related-packages:
        - pkg:pypi/django
      category: security
      shortDescription: Improper neutralization of user input rendered in HTML ('XSS') in Django
      security-severity: High
      confidence: High
      license: License Not Found
    pattern-sources:
      - patterns:
          - patterns:
              - pattern-inside: |
                  from django import $HTTPRESPONSE
                  ...
                  def $FOO(..., $REQ, ...): 
                    ...
              - metavariable-regex:
                  metavariable: $HTTPRESPONSE
                  regex: ^(HttpResponse)
          - pattern-either:
              - pattern: $USER_INPUT = <... $REQ.$METHOD.get(...) ...>
              - patterns:
                  - pattern: $USER_INPUT = $REQ.$METHOD
          - metavariable-regex:
              metavariable: $METHOD
              regex: ^(POST|GET)$
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern: $HTTPRESPONSE($USER_INPUT, ...)
          - metavariable-regex:
              metavariable: $HTTPRESPONSE
              regex: ^(HttpResponse)

    pattern-sanitizers:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  from django.utils.html import format_html
                  ...
              - pattern: format_html($USER_INPUT)
          - patterns:
              - pattern-inside: |
                  from django.utils.html import escape
                  ...
              - pattern: escape($USER_INPUT)
          - patterns:
              - pattern-inside: |
                  import bleach
                  ...
              - pattern: bleach.clean($USER_INPUT)
          - patterns:
              - pattern-inside: |
                  import nh3
                  ...
              - pattern: nh3.clean($USER_INPUT)

    mode: taint
  - id: python_django_rule_reflected_xss_render
    languages:
      - python
    severity: ERROR
    message: |
      A user controlled input is used in Django's render() function but the auto escaping has been disabled with django.utils.safestring.mark_safe|SafeString. Hence the user controlled input is directly embedded in the HTML template, creating an XSS vulnerability. 
      Avoid using mark_safe|SafeString on user controlled variables. Escape the user controlled variables with django.utils.html.escape/format_html. 
      Be careful when using format_html not to use f-string with the user controlled input nested in it, the escaping would be cancelled. instead, use it this way: format_html("<b>{}</b>", user_input). 
      Ensure to be aware of the context in which the user controlled data will be rendered: if it is embedded in CSS or <script> tags, you need to use a dedicated escaping method as django.utils.html.escapejs in addition to Django's autoescaping. Django's escape methods is meant to be use for HTML sanitizing!
    metadata:
      cwe: CWE-79
      owasp:
        - A7:2017-Cross-Site Scripting (XSS)
        - A03:2021-Injection
      related-packages:
        - pkg:pypi/django
      category: security
      shortDescription: Improper neutralization of user input rendered in HTML ('XSS')
      security-severity: High
      confidence: High
      license: License Not Found
    pattern-sources:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  from django import render
                  ...
              - pattern-inside: |
                  from django import render_to_string
                  ...
                  from django.http import $HTTPRESPONSE
                  ...
          # usiung a mark_safe function
          - pattern-either:
              - patterns:
                  - pattern-inside: |
                      from django.utils.safestring import $SAFE_VAR 
                      ...
                      def $FOO(..., $REQ, ...): 
                        ...     
                  - pattern-either:
                      - patterns:
                          - pattern-inside: |
                              $QUERY = <... $REQ.$METHOD.get(...) ...>
                              ...
                          - pattern: |
                              $VULN_HTML = <... $SAFE_VAR(<... $QUERY ...>) ...>
                      - pattern: $VULN_HTML = <... $SAFE_VAR(<... $REQ.$METHOD.get(...) ...>) ...>
                  - metavariable-regex:
                      metavariable: $SAFE_VAR
                      regex: mark_safe|SafeString

              - patterns:
                  - pattern-inside: |
                      from django.template import loader
                      ...
                      $TEMPLATE = loader.get_template(...)
                      ...          
                        
          - metavariable-regex:
              metavariable: $METHOD
              regex: POST|GET



    pattern-sinks:
      - pattern-either:
          - pattern: render(..., $VULN_HTML, ...)
          - patterns:
              - pattern-inside: |
                  $T = render_to_string(..., $VULN_HTML, ...)
                  ...
              - pattern: $HTTPRESPONSE($T, ...)
          - pattern: |
              $HTTPRESPONSE($TEMPLATE.render(..., $VULN_HTML, ...))
    pattern-sanitizers:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  from django.utils.html import format_html
                  ...
              - pattern-either:
                  - pattern: format_html(...)
          - patterns:
              - pattern-inside: |
                  from html_sanitizer import Sanitizer
                  ...
                  $SAN = Sanitizer(...)
                  ...
              - pattern-either:
                  - pattern: $SAN.sanitize(...)
    mode: taint
  - id: "python_escaping_rule-jinja2-autoescape-false"
    languages:
      - "python"
    message: |
      The application was found using Jinja2 `Environment` without autoescaping enabled. If using in
      the context of HTML this could lead to Cross-Site Scripting (XSS) attacks when rendering with
      user-supplied input.

      Unfortunately, Jinja2 does not support context-aware escaping, meaning it is insufficient to
      protect against
      XSS for the various web contexts. It is important to encode the data depending on the specific
      context
      it
      is used in. There are at least six context types:

      - Inside HTML tags `<div>context 1</div>`
      - Inside attributes: `<div class="context 2"></div>`
      - Inside event attributes `<button onclick="context 3">button</button>`
      - Inside script blocks: `<script>var x = "context 4"</script>`
      - Unsafe element HTML assignment: `element.innerHTML = "context 5"`
      - Inside URLs: `<iframe src="context 6"></iframe><a href="context 6">link</a>`

      Script blocks alone have multiple ways they need to be encoded. Extra care must be taken if
      user input
      is ever output inside of script tags.

      User input that is displayed within the application must be encoded, sanitized or validated
      to ensure it cannot be treated as HTML or executed as Javascript code. Care must also be
      taken
      to not mix server-side templating with client-side templating, as the server-side templating
      will
      not encode things like {{ 7*7 }} which may execute client-side templating features.

      It is _NOT_ advised to encode user input prior to inserting into a data store. The data will
      need to be
      encoded depending on context of where it is output. It is much safer to force the displaying
      system to
      handle the encoding and not attempt to guess how it should be encoded.

      To handle different contexts, one approach would be to write custom Jinja2 filters. Below is
      an example
      that escapes or encodes links and potentially malicious script, note this does not include
      other contexts
      such as CSS or attributes:
      ```
      from jinja2 import Environment, select_autoescape, FileSystemLoader
      from jinja2 import pass_eval_context
      from markupsafe import Markup, escape

      @pass_eval_context
      def escape_link(eval_ctx, value):
          bad_link = "#JinjatmplZ"
          # Block any values that start with // as that could be used to inject
          # links to third party pages see:
      https://en.wikipedia.org/wiki/Wikipedia:Protocol-relative_URL
          if value.startswith('//'):
              return bad_link

          # Only allow relative links
          # if you want to allow links that start with http or ws replace with below:
          # if not value.startswith('/'): and not value.startswith('http') and not
      value.startswith('ws')
          if not value.startswith('/'):
              return bad_link

          # Alternatively, you could only call escape if autoescape is true
          # if eval_ctx.autoescape:
          #    return escape(value)
          # else
          #    return value

          return escape(value)

      # Create a replacement table
      js_replacement = str.maketrans({
              '"':  "\\u0022",
              '`':  "\\u0060",
              '&':  "\\u0026",
              '\'': "\\u0027",
              '+':  "\\u002b",
              '/':  "\\/",
              '<':  "\\u003c",
              '>':  "\\u003e",
              '\\': "\\\\",
              '(': "\\u0028",
              ')': "\\u0029",
          })

      @pass_eval_context
      def escape_js(eval_ctx, value):
          """
          Escape the input for use in <script> context, USE WITH CAUTION
          It is strongly recommended to _never_ pass user-supplied input to
          the JavaScript context. This may still be unsafe depending where
          used, it does not consider characters used  in regular expressions
          for example.
          """

          #if eval_ctx.autoescape:
          #    value = escape(value)
          # Escape by default
          value = escape(value)
          # Translate any potential characters using our translation table
          return value.translate(js_replacement)

      # Create our environment, setting autoescape to use the default
      # select_autoescape function
      env = Environment(
          loader=FileSystemLoader(os.getcwd()+"/template"),
          autoescape=select_autoescape,
      )
      # Add an escape link filter to be used in our template
      env.filters["escape_link"] = escape_link
      env.filters["escape_js"] = escape_js
      # Load our template file
      template = env.get_template("mytemplate.html")
      # Render with different variables which call our filters
      print(template.render(
          html_context="<img src=x onerror=alert(1)>",
          link_context="/# onclick=alert(1)<script>alert(1)</script>",
          script_context="alert(1);alert`1`",)
      )

      # Sample template:
      """
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <title>My Webpage</title>
      </head>
      <body>
          <h1>My Webpage</h1>
          {{ html_context }}
          <a href="{{ link_context | escape_link }}">link</a>
          <script>{{ script_context | escape_js }}</script>
      </body>
      </html>
      """
      ```

      For more information on autoescape see:
      - https://jinja.palletsprojects.com/en/3.1.x/api/#autoescaping

      For more information on XSS see OWASP:
      - https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
    metadata:
      cwe: "CWE-116"
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      category: "security"
      shortDescription: "Improper encoding or escaping of output"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/jinja2']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          import jinja2
          ...
      - pattern-not: "jinja2.Environment(..., autoescape=True, ...)"
      - pattern-not: "jinja2.Environment(..., autoescape=jinja2.select_autoescape(...),...)"
      - pattern-not: "jinja2.Environment(..., autoescape=select_autoescape(...), ...)"
      - pattern: "jinja2.Environment(...)"
    severity: "WARNING"
  - id: "python_escaping_rule-use-of-mako-templates"
    languages:
      - "python"
    message: |
      The application was found using mako templates without `default_filters`
      being passed to the `Template` or `TemplateLookup` constructors. If using 
      in the context of HTML, this could lead to Cross-Site Scripting (XSS) attacks 
      when rendering with user-supplied input.

      Unfortunately, Jinja2 does not support context-aware escaping, meaning it
      is insufficient to protect against XSS for the various web contexts. It is 
      important to encode the data depending on the specific context it is used in. 
      There are at least six context types:

      - Inside HTML tags `<div>context 1</div>`
      - Inside attributes: `<div class="context 2"></div>`
      - Inside event attributes `<button onclick="context 3">button</button>`
      - Inside script blocks: `<script>var x = "context 4"</script>`
      - Unsafe element HTML assignment: `element.innerHTML = "context 5"`
      - Inside URLs: 
      `<iframe src="context 6"></iframe><a href="context 6">link</a>`

      Script blocks alone have multiple ways they need to be encoded. Extra care
      must be taken if user input is ever output inside of script tags.

      User input that is displayed within the application must be encoded,
      sanitized or validated to ensure it cannot be treated as HTML or executed 
      as Javascript code. Care must also be taken to not mix server-side templating 
      with client-side templating, as the server-side templating will not encode things 
      like {{ 7*7 }} which may execute client-side templating features.

      It is _NOT_ advised to encode user input prior to inserting into a data
      store. The data will need to be encoded depending on context of where it is output. 
      It is much safer to force the displaying system to handle the encoding and 
      not attempt to guess how it should be encoded.

      To handle different contexts, one approach would be to write custom mako
      filters. Below is an example that escapes or encodes links and 
      potentially malicious script, note this does not include other contexts 
      such as CSS or attributes:
      ```
      # filters.py module:

      def escape_link(value):
          bad_link = "#JinjatmplZ"
          # Block any values that start with // as that could be used to inject
          # links to third party pages see:
      https://en.wikipedia.org/wiki/Wikipedia:Protocol-relative_URL
          if value.startswith('//'):
              return bad_link

          # Only allow relative links
          # if you want to allow links that start with http or ws replace with below:
          # if not value.startswith('/'): and not value.startswith('http') and not
      value.startswith('ws')
          if not value.startswith('/'):
              return bad_link

          return value

      # Create a replacement table
      js_replacement = str.maketrans({
          '\0': "\\u0000",
          '\t': "\\t",
          '\n': "\\n",
          '\v': "\\u000b",
          '\f': "\\f`",
          '\r': "\\r",
          '"':  "\\u0022",
          '`':  "\\u0060",
          '&':  "\\u0026",
          '\'': "\\u0027",
          '+':  "\\u002b",
          '/':  "\\/",
          '<':  "\\u003c",
          '>':  "\\u003e",
          '\\': "\\\\",
          '(': "\\u0028",
          ')': "\\u0029",
      })

      def escape_js(value):
          # Escape the input for use in <script> context, USE WITH CAUTION
          # It is strongly recommended to never pass user-supplied input to
          # the JavaScript context.

          # Translate any potential characters using our translation table
          return value.translate(js_replacement)

      #####################################################################
      # main module:                                                      #
      #####################################################################
      from mako.template import Template

      # Define our template, note the calls to our custom filters depending
      # on context
      template_text = """

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <title>My Webpage</title>
      </head>
      <body>
          <h1>My Webpage</h1>
          ${html_context}
          <a href="${link_context | escape_link}">link</a>
          <script>${script_context | escape_js}</script>
      </body>
      </html>
      """

      # Load our template with default filters and our imported filters for
      # usage in template files
      t = Template(template_text,
                  # By default enable the html filter with 'h'
                  default_filters=['h'],
                  # Import our custom filters
                  imports=["from filters import escape_link, escape_js"])

      # Render our template
      print(t.render(html_context="<img src=x onerror=alert(1)>",
          link_context="/# onclick=alert(1)<script>alert(1)</script>",
          script_context="alert(1)<img src=x onerror=alert(1)>",)
      )
      ```
    metadata:
      cwe: "CWE-79"
      category: "security"
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      shortDescription: "Improper neutralization of input during web page generation ('Cross-site Scripting')"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/mako']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: "mako.template.Template(...)"
          - pattern: "mako.lookup.TemplateLookup(...)"
      - pattern-not: "mako.lookup.TemplateLookup(..., default_filters=[\"...\"])"
      - pattern-not: "mako.template.Template(..., default_filters=[\"...\"])"
    severity: "WARNING"
  - id: "python_eval_rule-eval"
    languages:
      - "python"
    message: |
      The application was found calling the `eval` function with non-literal data. If the variable contains 
      user-controlled data, either partially or fully, an adversary could compromise the entire system by 
      executing arbitrary Python code.

      To remediate this issue, remove all calls to `eval` and consider alternative methods for executing 
      the necessary business logic. There is almost no safe method of calling `eval` with user-supplied input.

      If the application only needs to convert strings into objects, consider using `json.loads`. In 
      some cases `ast.literal_eval` is recommended, but this should be avoided as it can still suffer 
      from other issues such as the ability for malicious code to crash the python interpreter or application.

      Example using `json.loads`` to load in arbitrary data to create data structures:
      ```
      # User supplied data as a blob of JSON
      user_supplied_data = """{"user": "test", "metadata": [1,2,3]}"""
      # Load the JSON
      user_object = json.loads(user_supplied_data)
      # Manually add protected properties _after_ loading, never before
      user_object["is_admin"] = False
      # Work with the object
      ```
    patterns:
      - pattern: "eval($X,...)"
      - pattern-not: |
          eval("...")
      - pattern-not: |
          eval("..." % <... "..." ...>)
      - pattern-not: |
          eval(<... "...".format( "..." ) ...>)
      - pattern-not-inside: |
          def eval(...):
            ...
          ...
    metadata:
      cwe: "CWE-95"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      category: "security"
      shortDescription: "Improper neutralization of directives in dynamically evaluated code ('Eval Injection')"
      security-severity: "High"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "python_exec_rule-exec-used"
    languages:
      - "python"
    message: |
      The application was found calling the `exec` function with a non-literal variable. If the
      variable comes from user-supplied input, an adversary could compromise the entire system by
      executing arbitrary python code.

      To remediate this issue, remove all calls to `exec` and consider alternative methods for
      executing the necessary business logic. There is almost no safe method of calling `eval` 
      with user-supplied input.

      If the application only needs to convert strings into objects, consider using `json.loads`.
      In some cases `ast.literal_eval` is recommended, but this should be avoided as it can still
      suffer from other issues such as the ability for malicious code to crash the python
      interpreter or application.

      Example using `json.loads`` to load in arbitrary data to create data structures:
      ```
      # User supplied data as a blob of JSON
      user_supplied_data = """{"user": "test", "metadata": [1,2,3]}"""
      # Load the JSON
      user_object = json.loads(user_supplied_data)
      # Manually add protected properties _after_ loading, never before
      user_object["is_admin"] = False
      # Work with the object
      ```
    metadata:
      cwe: "CWE-78"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      category: "security"
      shortDescription: "Improper neutralization of special elements used in an OS command ('OS Command Injection')"
      security-severity: "High"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: "exec(...)"
      - pattern-not: "exec(\"...\")"
    severity: "WARNING"
  - id: "python_exec_rule-linux-command-wildcard-injection"
    languages:
      - "python"
    message: |
      Detected use of the wildcard character in a system call that spawns a shell. This subjects the
      wildcard to normal shell expansion, which can have unintended consequences if there exist any
      non-standard file names. For instance, a file named `-e sh script.sh` could cause issues when 
      expanded by the shell and executed as a command. Consider using a different method to achieve 
      the same result, such as using the `glob` module to expand the wildcard before passing it to the
      system call. Or if the command is static, consider hardcoding the command instead of using a
      wildcard.

      For example, the below code uses the glob module to expand the wildcard and get a list of all 
      CSV files in the current directory. This list is then used in the subprocess.run call, instead of a 
      wildcard. This avoids the potential issues that can arise from using a wildcard in a system call.
      ```
      import glob
      import subprocess

      # Secure way to process all CSV files
      files = glob.glob('*.csv')
      for file in files:
        subprocess.run(['process_data', file])
      ```

    metadata:
      category: "security"
      cwe: "CWE-155"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      shortDescription: "Improper neutralization of wildcards or matching symbols"
      security-severity: "High"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: |-
              os.$X("$CMD", ...)
          - pattern: |-
              subprocess.Popen("$CMD", shell=True, ...)
      - metavariable-regex:
          metavariable: $CMD
          regex: '(.*?)(\*|\?)'
      - pattern-not-inside: |-
          os.spawnvp(...)
    metavariable-regex:
      X: "(system|popen|popen2|popen3|popen4)"
    severity: "WARNING"
  - id: "python_exec_rule-os-path"
    languages:
      - "python"
    message: |
      Starting a process with a shell; seems safe, but may be changed in the future, consider
      rewriting without shell
    pattern-either:
      - pattern: "os.system(\"...\", ...)"
      - pattern: "$OS.popen(\"...\", ...)"
      - pattern: "$OS.popen2(\"...\", ...)"
      - pattern: "$OS.popen3(\"...\", ...)"
      - pattern: "$OS.popen4(\"...\", ...)"
      - pattern: "commands.getoutput(\"...\", ...)"
      - pattern: "commands.getstatusoutput(\"...\", ...)"
    metadata:
      cwe: "CWE-78"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      shortDescription: "Improper neutralization of special elements used in an OS Command ('OS Command Injection')"
      security-severity: "High"
      category: "security"
      license: MIT (c) GitLab Inc.
    severity: "INFO"
  - id: "python_exec_rule-os-popen2"
    languages:
      - "python"
    message: |
      Starting a process with a shell; seems safe, but may be changed in the future, consider
      rewriting without shell
    patterns:
      - pattern-either:
          - pattern: "os.system(...)"
          - pattern: "os.popen(...)"
          - pattern: "os.popen2(...)"
          - pattern: "os.popen3(...)"
          - pattern: "os.popen4(...)"
          - pattern: "popen2.popen2(...)"
          - pattern: "popen2.popen3(...)"
          - pattern: "popen2.popen4(...)"
          - pattern: "popen2.Popen3(...)"
          - pattern: "popen2.Popen4(...)"
          - pattern: "commands.getoutput(...)"
          - pattern: "commands.getstatusoutput(\"\")"
    metadata:
      cwe: "CWE-78"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      shortDescription: "Improper neutralization of special elements used in an OS Command ('OS Command Injection')"
      security-severity: "High"
      category: "security"
      license: MIT (c) GitLab Inc.
    severity: "INFO"
  - id: "python_exec_rule-start-process-with-no-shell"
    languages:
      - "python"
    message: |
      Found dynamic content when spawning a process. This is dangerous if externaldata can reach this
      function call because it allows a malicious actor to execute commands. Ensure no external data
      reaches here.
    patterns:
      - pattern-either:
          - patterns:
              - pattern-not: "os.$W(\"...\", ...)"
              - pattern-either:
                  - pattern: "os.execl(...)"
                  - pattern: "os.execle(...)"
                  - pattern: "os.execlp(...)"
                  - pattern: "os.execlpe(...)"
                  - pattern: "os.execv(...)"
                  - pattern: "os.execve(...)"
                  - pattern: "os.execvp(...)"
                  - pattern: "os.execvpe(...)"
                  - pattern: "os.startfile(...)"
          - patterns:
              - pattern-either:
                  - pattern: "os.spawnl(...)"
                  - pattern: "os.spawnle(...)"
                  - pattern: "os.spawnlp(...)"
                  - pattern: "os.spawnlpe(...)"
                  - pattern: "os.spawnv(...)"
                  - pattern: "os.spawnve(...)"
                  - pattern: "os.spawnvp(...)"
                  - pattern: "os.spawnvpe(...)"
    metadata:
      cwe: "CWE-78"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      shortDescription: "Improper neutralization of special elements used in an OS Command ('OS Command Injection')"
      security-severity: "High"
      category: "security"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "python_exec_rule-subprocess-call"
    languages:
      - "python"
    message: |
      Python possesses many mechanisms to invoke an external executable. However,
      doing so may present a security issue if appropriate care is not taken to
      sanitize any user provided or variable input. This plugin test is part of a
      family of tests built to check for process spawning and warn appropriately.
      Specifically, this test looks for the spawning of a subprocess without the
      use of a command shell. This type of subprocess invocation is not
      vulnerable to shell injection attacks, but care should still be taken to
      ensure validity of input.
    patterns:
      - pattern-not: "subprocess.$FUNC($ARG, shell=<... True ...>)"
      - pattern-not: "subprocess.$FUNC($ARG, shell=<... 'True' ...>)"
      - pattern-not: "subprocess.$FUNC($ARG, shell=<... \"True\" ...>)"
      - pattern-either:
          - pattern: |
              subprocess.$FUNC($ARG, shell=False)
          - pattern: |
              subprocess.$FUNC($ARG, shell=0)
          - pattern: |
              subprocess.$FUNC($ARG, shell={...})
          - pattern: |
              subprocess.$FUNC($ARG, shell=[...])
          - pattern: |
              subprocess.$FUNC($ARG)
    metadata:
      cwe: "CWE-78"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      shortDescription: "Improper neutralization of special elements used in an OS Command ('OS Command Injection')"
      security-severity: "High"
      category: "security"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "python_exec_rule-subprocess-popen-shell-true"
    languages:
      - "python"
    patterns:
      - patterns:
          - pattern-not-inside: |
              ...
              $ARG = '...'.format('...')
              ...
          - pattern: "subprocess.$FUNC($ARG, ...)"
          - pattern-not: "subprocess.$FUNC('...', ...)"
          - pattern-not: "subprocess.$FUNC('...' % '...', ...)"
          - pattern-not: "subprocess.$FUNC('...'.format('...'), ...)"
      - pattern-either:
          - pattern: "subprocess.$FUNC(..., shell=True, ...)"
          - pattern: "subprocess.$FUNC(..., shell=[$V, ...], ...)"
          - pattern: "subprocess.$FUNC(..., shell={$K: $V, ...}, ...)"
          - patterns:
              - pattern: "subprocess.$FUNC(..., shell=$INTVAL, ...)"
              - pattern-not: "subprocess.$FUNC(..., shell=0, ...)"
              - metavariable-regex:
                  metavariable: "$INTVAL"
                  regex: "^[0-9]+$"
          - patterns:
              - pattern: "subprocess.$FUNC(..., shell='$STRVAL', ...)"
              - pattern-not: "subprocess.$FUNC(..., shell='', ...)"
    message: |
      Found `subprocess` function `$FUNC` with `shell=True`. This is dangerous because this call will
      spawn the command using a shell process. Doing so propagates current shell settings and
      variables,
      which makes it much easier for a malicious actor to execute commands. Use `shell=False`
      instead.
    metadata:
      cwe: "CWE-78"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      shortDescription: "Improper neutralization of special elements used in an OS Command ('OS Command Injection')"
      security-severity: "High"
      category: "security"
      license: MIT (c) GitLab Inc.
    severity: "ERROR"
  - id: "python_exec_rule-subprocess-shell-TRUE"
    languages:
      - "python"
    message: |
      subprocess call - check for execution of untrusted input
    patterns:
      - pattern-not: "subprocess.$FUNC(..., shell=True, ...)"
      - pattern: "$FOO(..., shell=True, ...)"
    metadata:
      cwe: "CWE-78"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      shortDescription: "Improper neutralization of special elements used in an OS Command ('OS Command Injection')"
      security-severity: "High"
      category: "security"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "python_file-permissions_rule-general-bad-permission"
    languages:
      - "python"
    message: |
      The application was found setting file permissions to overly permissive values. Consider
      using the following values if the application user is the only process to access
      the file:

      - 0400 - read only access to the file
      - 0200 - write only access to the file
      - 0600 - read/write access to the file

      Example creating a file with read/write permissions for the application user:
      ```
      # Use octal values to set 0o600 (read/write access to the file) for the current
      # user
      os.chmod('somefile.txt', 0o600)
      ```

      For all other values please see:
      https://en.wikipedia.org/wiki/File-system_permissions#Numeric_notation
    metadata:
      shortDescription: "Incorrect permission assignment for critical resource"
      cwe: "CWE-732"
      category: "security"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: "os.chmod(...,$MASK)"
      - metavariable-regex:
          metavariable: "$MASK"
          regex: "(0x..f|0o..[2,3,7]|stat.S_IXGRP|stat.S_IWOTH)"
    severity: "WARNING"
  - id: "python_files_rule-tarfile-unsafe-members"
    languages:
      - "python"
    message: |
      The application may be vulnerable to a path traversal if it extracts untrusted archive files.
      This vulnerability is colloquially known as 'Zip Slip'. Archive files may contain folders
      which,
      when extracted, may write outside of the intended directory. This is exploited by including
      path traversal characters such as `../../other/directory` to overwrite or place files in system
      or application directories.

      Extra care must be taken when extracting archive files as there are numerous concerns:

      - If possible, generate unique filenames instead of using the archives file names, as it may be
      possible for users to overwrite files if the filenames are the same.
      - Validate file paths are written with a prefixed, known trusted directory.
      - Only process regular files and not symbolic links, as some applications may attempt to
      read/follow
      the symbolic link, leading to arbitrary file read / write vulnerabilities.

      Example of securely processing an archive file:
      ```
      import tarfile
      import uuid
      # import os

      tar = tarfile.open('some.tar')

      # Max number of allowed files in our archive
      max_files = 10
      # Max size for all files in archive
      max_size = 1024 * 1024 * 10 # 10MB
      # Max size per file in archive
      max_file_size = 1024 * 1024 # 1MB

      # Validate number of files in archive
      if len(tar.getmembers()) > max_files:
          raise Exception("Too many files in archive")

      total_size = 0
      # Loop over all files to see if we exceed max size
      # if so, do not process any of them.
      for f in tar.getmembers():
          total_size += f.size
          if total_size >= max_size:
              raise Exception("Archive files exceeded max file size")

      # Iterate over files now that we know the total size is within limits
      for f in tar.getmembers():
          # Internally this calls TarInfo.isreg() which ensures
          # the file is a regular file and not a sym link or directory
          if not f.isfile():
              continue

          # Optional, set a limit on each file size
          if f.size > max_file_size:
              raise Exception(f"File {f.name} too large: {f.size}")

          # If original names are required, ensure that only the
          # filename is used:
          # filename = os.path.basename(f.name)

          # More secure, generate a UUID4 value instead
          filename = uuid.uuid4().hex

          # Reset the archive filename to the basename
          # Newer versions of python (3.11.4+) should use:
          # new_tar = old_tar.replace(name=...new name...)
          f.name = filename

          # Extract the file into a restricted directory, with our
          # own user's attributes, not the file from the archive
          tar.extract(f, '/opt/app/restricted/', set_attrs=False)
      ```

      For more information on tarfile see:
      - https://docs.python.org/3/library/tarfile.html
    metadata:
      cwe: "CWE-22"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      security-severity: "Medium"
      category: "security"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          import tarfile
          ...
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  $TAR = tarfile.open(...)
                  ...
              - pattern-either:
                  - pattern: "$TAR.extractall(...)"
                  - pattern: "tarfile.extractall(..., members=$TAR)"
                  - pattern: "$TAR.extractall(..., members=[])"
          - patterns:
              - pattern: "tarfile.extractall(...)"
              - pattern: "tarfile.extractall(..., members=[])"
    severity: "WARNING"
  - id: "python_flask_rule-app-debug"
    languages:
      - "python"
    message: |
      The Flask application is running with `debug=True` configured. By enabling this option, certain
      exceptions or errors could cause sensitive information to be leaked in HTTP responses.

      Additionally, it is not recommended to run a Flask application using `Flask.run(...)` in
      production. Instead, a WSGI server such as
      [gunicorn](https://flask.palletsprojects.com/en/2.3.x/deploying/gunicorn/)
      or [waitress](https://flask.palletsprojects.com/en/2.3.x/deploying/waitress/) be used instead.

      For more information on deployment options for Flask applications see:
      - https://flask.palletsprojects.com/en/2.3.x/deploying/
    metadata:
      cwe: "CWE-489"
      category: "security"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      shortDescription: "Active debug code"
      security-severity: "Medium"
      related-packages: ["pkg:pypi/flask"]
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          import flask
          ...
      - pattern: "$APP.run(..., debug=True, ...)"
    severity: "WARNING"
  - id: python_flask_rule_reflected_xss_make_response
    languages:
      - python
    severity: ERROR
    message: |
      Untrusted user input from request objects is directly inserted into an HTML response created with make_response() without proper sanitization, creating a reflected XSS vulnerability.
      Always sanitize user input before including it in HTML responses using Flask's escape function, MarkupSafe's escape, html.escape(), or bleach.clean().
      Use Flask's render_template() with Jinja2 templates instead of manually constructing HTML with make_response(), as templates automatically escape variables.
      Remember that HTML escaping is not sufficient for JavaScript contexts, CSS contexts, or URL attributes - use context-specific sanitization for these cases.
    metadata:
      cwe: "CWE-79"
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      category: "security"
      shortDescription: "Improper neutralization of user input rendered in HTML (XSS)"
      security-severity: "High"
      related-packages:
        - "pkg:pypi/flask"

      license: License Not Found
    pattern-sources:
      - patterns:
          - pattern-inside: |
              from flask import $IMPORTS
              ...
              def $FUNC(...):
                  ...
          - pattern-either:
              - pattern: $USER_INPUT = request.args.get(...)
              - pattern: $USER_INPUT = request.form.get(...)
              - pattern: $USER_INPUT = request.json.get(...)
              - pattern: $USER_INPUT = request.values.get(...)
              - pattern: $USER_INPUT = request.$METHOD.get(...)
          - metavariable-regex:
              metavariable: $IMPORTS
              regex: .*request.*|.*make_response.*|.*

    pattern-sinks:
      - pattern-either:
          - pattern: make_response($HTML, ...)
          - pattern: make_response(f"<... { $USER_INPUT } ...>", ...)
          - pattern: make_response("<... " + $USER_INPUT + " ...", ...)
          - pattern: make_response("...".format(..., $USER_INPUT, ...), ...)
          - pattern: make_response("..." % $USER_INPUT, ...)
          - pattern: |
              $HTML = f"<... { $USER_INPUT } ...>"
              ...
              make_response($HTML, ...)
          - pattern: |
              $HTML = "<... " + $USER_INPUT + " ...>"
              ...
              make_response($HTML, ...)
          - pattern: |
              $HTML = "...".format(..., $USER_INPUT, ...)
              ...
              make_response($HTML, ...)
          - pattern: |
              $HTML = "..." % $USER_INPUT
              ...
              make_response($HTML, ...)

    pattern-sanitizers:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  import bleach
                  ...
              - pattern: bleach.clean($USER_INPUT)
          - patterns:
              - pattern-inside: |
                  from markupsafe import escape
                  ...
              - pattern: escape($USER_INPUT)
          - patterns:
              - pattern-inside: |
                  import html
                  ...
              - pattern: html.escape($USER_INPUT)
          - patterns:
              - pattern-inside: |
                  from flask import escape
                  ...
              - pattern: escape($USER_INPUT)

    mode: taint
  - id: python_flask_rule_reflected_xss_render
    languages:
      - python
    severity: ERROR
    message: |
      Untrusted user input is directly inserted into an HTML template string passed to render_template_string(), creating a reflected XSS vulnerability. While Jinja2 templates automatically escape variables, this protection is bypassed when user input is embedded in the template string itself or when the |safe filter is used.
      Never construct template strings dynamically with user input - always pass user data as context variables to the template instead.
      If you must include user data in dynamic templates, sanitize it first with Flask's escape function, MarkupSafe's escape, or html.escape().
      Never use the |safe filter with untrusted data, and consider using render_template() with static template files instead of render_template_string().
    metadata:
      cwe: "CWE-79"
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      category: "security"
      shortDescription: "Improper neutralization of user input rendered in HTML (XSS)"
      security-severity: "High"
      related-packages:
        - "pkg:pypi/flask"

      license: License Not Found
    mode: taint

    pattern-sources:
      - patterns:
          - pattern-inside: |
              from flask import $IMPORTS
              ...
              def $FUNC(...):
                  ...
          - pattern-either:
              - pattern: $USER_INPUT = request.args.get(...)
              - pattern: $USER_INPUT = request.form.get(...)
              - pattern: $USER_INPUT = request.json.get(...)
              - pattern: $USER_INPUT = request.values.get(...)
              - pattern: $USER_INPUT = request.$METHOD.get(...)
          - metavariable-regex:
              metavariable: "$IMPORTS"
              regex: ".*request.*|.*render_template_string.*|.*"

    pattern-sinks:
      - pattern-either:
          - pattern: render_template_string(<... $USER_INPUT ...>, ...)
          - pattern: render_template_string(f"<... { $USER_INPUT } ...>", ...)
          - pattern: render_template_string("<... " + $USER_INPUT + " ...", ...)
          - pattern: render_template_string("...".format(..., $USER_INPUT, ...), ...)
          - pattern: render_template_string("..." % $USER_INPUT, ...)
          - pattern: |
              $TEMPLATE = f"<... { $USER_INPUT } ...>"
              ...
              render_template_string($TEMPLATE, ...)
          - pattern: |
              $TEMPLATE = "<... " + $USER_INPUT + " ...>"
              ...
              render_template_string($TEMPLATE, ...)
          - pattern: |
              $TEMPLATE = "...".format(..., $USER_INPUT, ...)
              ...
              render_template_string($TEMPLATE, ...)
          - pattern: |
              $TEMPLATE = "..." % $USER_INPUT
              ...
              render_template_string($TEMPLATE, ...)

    pattern-sanitizers:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  import bleach
                  ...
              - pattern: bleach.clean($USER_INPUT)
          - patterns:
              - pattern-inside: |
                  from markupsafe import escape
                  ...
              - pattern: escape($USER_INPUT)
          - patterns:
              - pattern-inside: |
                  import html
                  ...
              - pattern: html.escape($USER_INPUT)
          - patterns:
              - pattern-inside: |
                  from flask import escape
                  ...
              - pattern: escape($USER_INPUT)
  - id: python_flask_rule_reflected_xss_user_input
    languages:
      - python
    severity: ERROR
    message: |
      User input from request objects (args, form, values, json) is directly used in HTML output without proper sanitization, creating a reflected XSS vulnerability.
      Always sanitize user input before including it in HTML responses using Flask's escape function, MarkupSafe's escape, html.escape(), or bleach.clean().
      Use Flask templates (Jinja2) which automatically escape variables by default instead of manually constructing HTML.
      For JavaScript contexts or URL attributes, use additional context-specific sanitization as HTML escaping alone is insufficient.
    metadata:
      cwe: CWE-79
      owasp:
        - A7:2017-Cross-Site Scripting (XSS)
        - A03:2021-Injection
      category: security
      shortDescription: Improper neutralization of user input rendered in HTML (XSS)
      security-severity: High
      related-packages:
        - pkg:pypi/flask

      license: License Not Found
    pattern-sources:
      - patterns:
          - pattern-inside: |
              from flask import $IMPORTS
              ...
              def $FUNC(...):
                  ...
          - pattern-either:
              - pattern: $USER_INPUT = request.args.get(...)
              - pattern: $USER_INPUT = request.form.get(...)
              - pattern: $USER_INPUT = request.json.get(...)
              - pattern: $USER_INPUT = request.values.get(...)
              - pattern: $USER_INPUT = request.$METHOD.get(...)
          - metavariable-regex:
              metavariable: $IMPORTS
              regex: .*request.*|.*

    pattern-sinks:
      - pattern-either:
          - pattern: return f"<... { $USER_INPUT } ...>"
          - pattern: return "<... " + $USER_INPUT + " ...>"
          - pattern: return "...".format(..., $USER_INPUT, ...)
          - pattern: return "..." % $USER_INPUT
          - pattern: |
              $HTML = f"<... { $USER_INPUT } ...>"
              ...
              return $HTML
          - pattern: |
              $HTML = "<... " + $USER_INPUT + " ...>"
              ...
              return $HTML
          - pattern: |
              $HTML = "...".format(..., $USER_INPUT, ...)
              ...
              return $HTML
          - pattern: |
              $HTML = "..." % $USER_INPUT
              ...
              return $HTML

    pattern-sanitizers:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  import bleach
                  ...
              - pattern: bleach.clean($USER_INPUT)
          - patterns:
              - pattern-inside: |
                  from markupsafe import escape
                  ...
              - pattern: escape($USER_INPUT)
          - patterns:
              - pattern-inside: |
                  import html
                  ...
              - pattern: html.escape($USER_INPUT)
          - patterns:
              - pattern-inside: |
                  from flask import escape
                  ...
              - pattern: escape($USER_INPUT)

    mode: taint
  - id: "python_ftp_rule-ftplib"
    languages:
      - "python"
    message: |
      The application was found using an FTP library. As FTP does not provide encryption, it is
      strongly recommended that any file transfers be done over a more secure transport such as
      SSH.

      The [paramiko](https://www.paramiko.org/) library can be used with an SCP module to allow
      secure file transfers.

      Example using `paramiko` SSH client and the `scp` module:
      ```
      import paramiko
      import scp

      # Create an SSH client
      with paramiko.SSHClient() as ssh:
          # Load the system host keys so we can confirm the
          # host we are connecting to is legitimate
          ssh.load_system_host_keys('/home/appuser/.ssh/known_hosts')

          # Connect to the remote host using our SSH private key
          ssh.connect(hostname='example.org',
                      port=22,
                      username='appuser',
                      key_filename='/home/appuser/.ssh/private_key')

          # Create an SCP client with the ssh transport and copy files
          with scp.SCPClient(ssh.get_transport()) as secure_copy:
              secure_copy.get('remote/test.file', 'local/test.file')
              secure_copy.put('local/some.file', 'remote/some.file')
      ```

      For more information on the paramiko module see:
      - https://www.paramiko.org/

      For more information on the scp module see:
      - https://github.com/jbardin/scp.py
    metadata:
      cwe: "CWE-319"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Cleartext transmission of sensitive information"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    pattern: "ftplib.$ANYTHING(...)"
    severity: "WARNING"
  - id: "python_log_rule-logging-config-insecure-listen"
    languages:
      - "python"
    message: |
      The application was found calling the `logging.config.listen`` function, which provides the
      ability to listen for
      external configuration files over a socket server. This listen socket parses part of the
      configuration and calls
      `eval` on the supplied configuration file. A local user, or an adversary who is able to
      exploit
      a Server Side Request Forgery (SSRF) attack to communicate over localhost, would be able to
      execute arbitrary
      code by passing in a logging config that contains python code.

      To remediate the issue, remove the call to `logging.config.listen` method.

      For more information on the listen functionality see:
      - https://docs.python.org/3/library/logging.config.html#logging.config.listen
    metadata:
      cwe: "CWE-94"
      shortDescription: "Improper control of generation of code ('Code Injection')"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "High"
      related-packages: ['pkg:pypi/logging']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: "logging.config.listen(...)"
    severity: "WARNING"
  - id: python_prompt_injection
    languages:
      - python
    severity: ERROR
    message: |
      User-controlled input is being passed directly to an LLM API (OpenAI, Gemini, Azure AI) without proper validation or sanitization.
      Prompt injection attacks can manipulate AI models to ignore their instructions, leak sensitive information, or perform unintended actions.

      To mitigate this risk:
      1. Validate and sanitize all user inputs before sending to LLM APIs
      2. Use input filtering to remove or escape potentially malicious prompts
      3. Implement prompt templates that clearly separate user data from system instructions
      4. Consider using structured inputs rather than free-form text when possible
      5. Apply the principle of least privilege to LLM API calls
      6. Monitor and log LLM interactions for suspicious patterns
    metadata:
      category: "AI"
      shortDescription: "Prompt injection in LLM API calls"
      security-severity: "Low"
      confidence: "High"
      related-packages:
        - "pkg:pypi/openai"
        - "pkg:pypi/google-generativeai"
        - "pkg:pypi/azure-ai-generative"
        - "pkg:pypi/django"
        - "pkg:pypi/flask"
        - "pkg:pypi/fastapi"

      license: License Not Found
    mode: taint

    pattern-sources:
      - patterns:
          - pattern-inside: |
              from django import $IMPORTS
              ...
              def $FUNC(..., $REQ, ...):
                  ...
          - pattern-either:
              - pattern: $USER_INPUT = <... $REQ.$METHOD.get(...) ...>
              - pattern: $USER_INPUT = $REQ.$METHOD
          - metavariable-regex:
              metavariable: $METHOD
              regex: ^(POST|GET|FILES)$

      - patterns:
          - pattern-inside: |
              from flask import $IMPORTS
              ...
              def $FUNC(...):
                  ...
          - pattern-either:
              - pattern: $USER_INPUT = request.args.get(...)
              - pattern: $USER_INPUT = request.form.get(...)
              - pattern: $USER_INPUT = request.json.get(...)
              - pattern: $USER_INPUT = request.values.get(...)
              - pattern: $USER_INPUT = request.$METHOD.get(...)
          - metavariable-regex:
              metavariable: $IMPORTS
              regex: .*request.*|.*

      - patterns:
          - pattern-inside: |
              from fastapi import $IMPORTS
              ...
              def $FUNC(...):
                  ...
          - pattern-either:
              - pattern: $USER_INPUT = $REQUEST.query_params.get(...)
              - pattern: $USER_INPUT = $REQUEST.form()
              - pattern: $USER_INPUT = $REQUEST.json()
              - pattern: $USER_INPUT = await $REQUEST.form()
              - pattern: $USER_INPUT = await $REQUEST.json()

      - patterns:
          - pattern-inside: |
              from fastapi import FastAPI
              ...
              async def $FUNC(..., $BODY: $MODEL, ...):
                  ...
          - pattern: $BODY

      - patterns:
          - pattern-inside: |
              from fastapi import FastAPI
              ...
              def $FUNC(..., $BODY: $MODEL, ...):
                  ...
          - pattern: $BODY

      - patterns:
          - pattern-inside: |
              async def $F(..., $BODY: BaseModel, ...):
                  ...
          - pattern: $BODY.$FIELD

    pattern-sinks:
      - pattern-either:
          - pattern: |
              openai.chat.completions.create(..., messages=$USER_INPUT, ...)
          - pattern: |
              openai.ChatCompletion.create(..., messages=$USER_INPUT, ...)
          - pattern: |
              client.chat.completions.create(..., messages=$USER_INPUT, ...)


          - pattern: openai.completions.create(..., prompt=$USER_INPUT, ...)
          - pattern: openai.Completion.create(..., prompt=$USER_INPUT, ...)
          - pattern: client.completions.create(..., prompt=$USER_INPUT, ...)

          - pattern: genai.GenerativeModel(...).generate_content($USER_INPUT, ...)
          - pattern: model.generate_content($USER_INPUT, ...)
          - pattern: $MODEL.generate_content(..., $USER_INPUT, ...)

          - pattern: |
              azure_client.chat.completions.create(..., messages=$USER_INPUT, ...)
          - pattern: azure_client.completions.create(..., prompt=$USER_INPUT, ...)
  - id: "python_random_rule-random"
    languages:
      - "python"
    message: |
      Depending on the context, generating weak random numbers may expose cryptographic functions,
      which rely on these numbers, to be exploitable. When generating numbers for sensitive values
      such as tokens, nonces, and cryptographic keys, it is recommended that the `secrets` module
      be used instead.

      Example using the secrets module:
      ```
      import secrets

      # Generate a secure random 64 byte array
      random_bytes = secrets.token_bytes(64)
      print(random_bytes)

      # Generate a secure random 64 byte array as a hex string
      random_bytes_hex = secrets.token_hex(64)

      # Generate a secure random 64 byte array base64 encoded for use in URLs
      random_string = secrets.token_urlsafe(64)
      ```

      For more information on the `secrets` module see:
      - https://docs.python.org/3/library/secrets.html
    metadata:
      cwe: "CWE-338"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      shortDescription: "Use of cryptographically weak pseudo-random number generator (PRNG)"
      security-severity: "Low"
      license: MIT (c) GitLab Inc.
    pattern-either:
      - pattern: "random.random(...)"
      - pattern: "random.randrange(...)"
      - pattern: "random.randint(...)"
      - pattern: "random.choice(...)"
      - pattern: "random.uniform(...)"
      - pattern: "random.triangular(...)"
    severity: "WARNING"
  - id: "python_requests_rule-request-without-timeout"
    languages:
      - "python"
    message: |
      The application was found using the `requests` module without configuring a timeout value for
      connections. This could lead to uncontrolled resource consumption where the application could
      run out of
      socket descriptors, effectively causing a Denial of Service (DoS).

      To remediate this issue, pass in a `timeout=` argument to each `requests` call.

      Example using a timeout for an HTTP GET request:
      ```
      # Issue a GET request to https://example.com with a timeout of 10 seconds
      response = requests.get('https://example.com', timeout=10)
      # Work with the response object
      # ...
      ```

      For more information on using the requests module see:
      - https://requests.readthedocs.io/en/latest/api/
    patterns:
      - pattern-either:
          - patterns:
              - pattern: "requests.$METHOD(..., timeout=$VAL, ...)"
              - metavariable-comparison:
                  comparison: "$VAL <= 0"
                  metavariable: "$VAL"
          - patterns:
              - pattern: "requests.$METHOD(..., timeout=$VAL, ...)"
              - metavariable-regex:
                  metavariable: "$VAL"
                  regex: "(^None)"
          - patterns:
              - pattern-not: "requests.$METHOD(..., timeout=$VAL, ...)"
              - pattern-either:
                  - pattern: "requests.$METHOD(..., ...)"
                  - pattern: "requests.$METHOD(...)"
      - metavariable-regex:
          metavariable: "$METHOD"
          regex: "(get|put|delete|post|options|head|patch)"
    metadata:
      cwe: "CWE-770"
      category: "security"
      shortDescription: "Allocation of resources without limits or throttling"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/requests']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: python_rule_sql_injection
    languages:
      - python
    severity: ERROR
    message: |
      A SQL Injection vulnerability was detected in Python: 
      a SQL query containing unparameterized dynamic parameter was executed. The parameter is user controlled or an unknown function argument.

      Never directly nest user controlled variables in a SQL Query. Instead, use parameters binding: ```session.exec("SELECT :input", {"input": user_input})```.

      Or use named parameters instead (```'UPDATE user SET bio = ? WHERE id = ?', (new_bio, user_id)```)

      Never pass a user controlled input variable in a SQL query directly (f"... {user_input} ...") or using text formatting ("... %s ..." % user_input), quoted placeholder ("... '%s' ...", [user_input]) or "...".format(user_input)

      Use as much as you can ORM (for example Django's, SQLModel's, SQLAlchemy's, Peewee's query builder methods) which are designed to avoid SQL injections. 
    metadata:
      cwe: CWE-89
      owasp:
        - A1:2017-Injection
        - A03:2021-Injection
      category: security
      shortDescription: Unsafe SQL query with non parameterized parameters
      security-severity: High
      confidence: Medium
      license: License Not Found
    pattern-sources:
      - pattern-either:
        # Detecting untrusted input - potentially user controlled ---------------
          - patterns:
              - pattern-either:
              # external variable as function arg
                  - pattern-inside: |
                      def $F(..., $PARAM, ...): 
                        ...
                  - patterns:
                      - pattern-inside: |
                          $PARAM = $V
                          ...
                      - metavariable-regex:
                          metavariable: "$V"
                          regex: ^[^'"0-9]+
              # User inputs through requests
                  - patterns:
                      - pattern-either:
                          - pattern-inside: |
                              $PARAM = <... ... .$METHOD[...] ...>
                              ...
                          - pattern: <... ... .$METHOD[...] ...>
                          - pattern-inside: |
                              $PARAM = <... ... .$METHOD ...>
                              ...
                          - pattern: <... ... .$METHOD ...>
                          - pattern-inside: |
                              $PARAM = <... $METHOD(...) ...>
                              ...
                          - pattern: <... $METHOD(...) ...>
                          - pattern: <... ... .$METHOD(...) ...>
                          - pattern-inside: |
                              $PARAM = <... ... .$METHOD(...) ...>
                              ...
                      - metavariable-regex:
                          metavariable: $METHOD
                          regex: (?i).*(POST|GET|READ|CREATE|QUERY|INPUT).*
                      - pattern: $PARAM

                  - pattern-inside: |
                      $PARAM = sys.argv[...]
                      ...
                  - pattern: <... sys.argv[...] ...>
                  - pattern-inside: |
                      $PARAM = sys.stdin.read(...)
                      ...
                  - pattern: <... sys.stdin.read(...) ...>

            # previous detected input in unparameterized sql queries  
              - pattern-either:
                  - patterns:
                      - pattern-either:
                  # peewee SQL(...)
                          - patterns:
                              - pattern-inside: |
                                  import peewee
                                  ...
                              - pattern-inside: <... SQL(...) ...>
                  # in a sql like query
                          - pattern-inside: |
                              <... "=~/(?i)^(SELECT|INSERT|UPDATE|DELETE|CREATE|FROM|DROP|ALTER|TRUNCATE|GRANT|REVOKE|SAVEPOINT|ROLLBACK|WITH|AND|COUNT)\s+/" ...>
                      - pattern-either:
                  # text formatting ex: "SELECT %s" % input
                          - pattern: <... "=~/.*%[a-z].*/" % <... $PARAM ...> ...>
                          - pattern: <... "=~/.*%[a-z].*/" % <... $PARAM. ... ...> ...> # chained method 
                    # quotes around the '%s' placeholders in every case 
                          - pattern: <... "=~/.*\'%[a-z]\'.*/" ...>
                    # concatenation
                          - pattern: <... "..." + <... $PARAM ...> ...>
                          - pattern: <... "..." + <... $PARAM. ... ...> ...>
                          - pattern: <... "...".format(<... $PARAM ...>) ...>
                          - pattern: <... "...".format(..., <... $PARAM ...>, ...) ...>
                          - patterns: # using {input}
                              - pattern-either:
                                  - pattern: <... $PARAM ...>
                                  - pattern: $INPUT = <... $PARAM ...>
                              - pattern-regex: "{.*}"

              # Previous detected input in sql alchemy ORM
                  - patterns:
                      - pattern-inside: |
                          import sqlalchemy
                          ...
                # in a sql like query
                      - pattern-inside: |
                          <... "=~/(?i)^(SELECT|LIKE|INSERT|UPDATE|DELETE|CREATE|CASE|FROM|DROP|ALTER|TRUNCATE|GRANT|WITH|COUNT)(\s*)/" ...>
                      - patterns:
                          - pattern-inside: <... f"..." ...>
                          - pattern: <... $PARAM ...>
                          - pattern-regex: "{.+}"

       
    pattern-sinks:
      - pattern-either:
        # django 
          - pattern: $CLASS.$OBJ.raw(...)
          - pattern: RawSQL(...)
        # execute sql - like methods 
          - patterns:
              - pattern-either:
                  - pattern: |
                      <... $EXEC ...>
              - metavariable-regex:
                  metavariable: $EXEC
                  regex: 
                    (\n|.)*(?i)[a-z0-9\-\_]*(update|query|exec|sql|select|delete|create|commit|group_by|having|filter|join|where|order|raw)[a-z0-9\-\_]*\((\n|.)+\)


    mode: taint
  - id: python_rule_sql_injection_queries
    languages:
      - python
    severity: ERROR
    message: >
      "A potential SQL query was found to be vulnerable to SQL injections: 

      Never use quotes around a parameter placeholder in a SQL query (example: "SELECT '%s' FROM table") 

      Do not use string formatting: '"SELECT %s from table" % input'

      Never use .format(input) or "... {input} ..." or '"..." + input' to use dynamic parameters in your query. 

      Instead, to pass untrusted parameters in a SQL query, use parameters binding: "SELECT :input", {"input": user_input}),
      

      Or use separated list or tuple: '"SELECT %s from table", [input,]' or '"SELECT %s from table", (input,)'. 

      When it is possible, use an ORM to avoid SQL injections (Django, SQLModel, SQLAlchemy...). Build raw SQL query only
      if you cannot use an ORM. 

      "
    metadata:
      cwe: CWE-89
      owasp:
        - A1:2017-Injection
        - A03:2021-Injection
      category: security
      shortDescription: Unsafe SQL query with non parameterized parameters
      security-severity: High
      confidence: High
      license: License Not Found
    patterns:
      # Detect SQL query start
      - pattern-inside: |
          <... "=~/(?i)^(SELECT|INSERT|UPDATE|DELETE|CREATE)\s/" ...>
      # not in function descriptions
      - pattern-not-inside: |
          """..."""    
      - pattern-either:
          # never quote place holders: '%s'
          - pattern-regex: \'%[a-z]\'
          # format does not sanitize input
          - pattern-regex: \.format\([^'"]*\)
          # don't nest input with % : "... %s " % input
          - pattern-regex: \%[a-z].*['"]\s+%\s?+[^'"\s]+
          # don't nest input with +: "..." + input
          - pattern-regex: \s+\+\s+[^'"\s]+
          # using query += input
          - pattern-regex: \+\=\s+[^'"\s]+
          # don't nest input with {}: f"... {input} ..."
          - pattern-regex: "{.+}"
          # Don't nest input with , :"..." , input
          - pattern-regex: \",\s*[a-zA-Z]+
  - id: "python_snmp_rule-insecure-snmp-version"
    languages:
      - "python"
    message: |
      Pysnmp was detected using versions SNMPv1 or SNMPv2. SNPMv1 and SNMPv2 are insecure
      and should no longer be used as they do not offer encryption.

      If possible, query SNMP devices using SNMPv3 instead.

      Example querying a device using SNMPv3 with SHA-AES:
      ```
      from pysnmp.hlapi import *
      # Create the snpm iterator
      iterator = getCmd(
          SnmpEngine(),
          # Configure using SHA AES
          UsmUserData('usr-sha-aes', 'authkey1', 'privkey1',
                      authProtocol=USM_AUTH_HMAC96_SHA,
                      privProtocol=USM_PRIV_CFB128_AES),
          UdpTransportTarget(('demo.snmplabs.com', 161)),
          ContextData(),
          ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))
      )
      ```

      For more information on using SNMPv3 with `Pysnmp` see:
      - https://pysnmp.readthedocs.io/en/latest/examples/hlapi/v3arch/asyncore/sync/manager/cmdgen/snmp-versions.html#snmpv3-auth-sha-privacy-aes128
    metadata:
      cwe: "CWE-319"
      category: "security"
      shortDescription: "Cleartext transmission of sensitive information"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/pysnmp']
      license: MIT (c) GitLab Inc.
    pattern-either:
      - pattern: "pysnmp.hlapi.CommunityData(..., mpModel=0, ...)"
      - pattern: "pysnmp.hlapi.CommunityData(..., mpModel=1, ...)"
    severity: "WARNING"
  - id: "python_snmp_rule-snmp-weak-cryptography"
    languages:
      - "python"
    message: |
      Pysnmp was detected using SNMPv3 without authentication or encryption
      protections enabled.

      - Use of `usmNoAuthProtocol` or `usmNoPrivProtocol` indicates that
      either authentication or privacy, respectively, is not being used.  
      - The absence of  `authKey` (or `authKey=None`) implies no authentication, 
      which is equivalent to using `usmNoAuthProtocol`. 
      - The absence of `privKey` (or `privKey=None`) implies no privacy (encryption), 
      which is equivalent to using `usmNoPrivProtocol`.

      To enhance the security of your SNMP communications, it is recommended to use both
      authentication and privacy features in SNMPv3:

      - Use SHA for Authentication: SHA (Secure Hash Algorithm) is a more secure option 
      for SNMP message authentication. To use SHA, set the `authProtocol` to 
      `usmHMACSHAAuthProtocol` and provide a strong `authKey`.
      - Use AES for Privacy: AES (Advanced Encryption Standard) is recommended for 
      encrypting SNMP messages. Set the `privProtocol` to `usmAesCfb128Protocol`
      or a similar AES-based protocol and specify a strong `privKey`.

      Example of secure `UsmUserData` configuration:
      ``` 
        from pysnmp.hlapi import UsmUserData, usmHMACSHAAuthProtocol, usmAesCfb128Protocol
            
        user_data = UsmUserData('username','authKey', 'privKey',           
                              authProtocol=usmHMACSHAAuthProtocol,
                              privProtocol=usmAesCfb128Protocol)
      ```
    metadata:
      cwe: "CWE-319"
      category: "security"
      shortDescription: "Cleartext transmission of sensitive information"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/pysnmp']
      license: MIT (c) GitLab Inc.
    pattern-either:
      - pattern-regex: "UsmUserData(.*usmNoAuthProtocol.*)"
      - pattern-regex: "UsmUserData(.*usmNoPrivProtocol.*)"
      - pattern: |
          UsmUserData(..., authKey=None, ...)
      - pattern: |
          UsmUserData(..., privKey=None, ...)
      - pattern: |
          UsmUserData(..., authProtocol=(1,3,6,1,6,3,10,1,1,1), ...)
      - pattern: |
          UsmUserData(..., privProtocol=(1,3,6,1,6,3,10,1,2,1), ...)
      - patterns:
          - pattern-not: |
              UsmUserData($NAME,$AUTHKEY,"...", ...)
          - pattern-not: |
              UsmUserData(..., privKey=$PRIVKEY, ...)
          - pattern-not: |
              UsmUserData(..., privProtocol=$PRIVPROT, ...)
          - pattern: |
              UsmUserData(...)
    severity: "WARNING"
  - id: "python_ssh_rule-ssh-nohost-key-verification"
    languages:
      - "python"
    message: |
      The application was found to ignore host keys. Host keys are important as
      they provide assurance that the client can prove that the host is trusted.
      By ignoring these host keys, it is impossible for the client to validate the
      connection is to a trusted host.

      To remediate this issue, remove the call to `set_missing_host_key_policy(...)` which
      sets the host key policy. Instead, load key files using either `load_system_host_keys`
      or `load_host_keys` to only allow known good hosts. By not setting a host key policy
      for unknown hosts, `paramiko`'s default policy is to use `RejectPolicy`.

      Example configuration connecting to a known, trusted host, and not allowing connections
      to unknown hosts:
      ```
      import paramiko

      # Create an SSH client
      with paramiko.SSHClient() as ssh:
          # Load the system host keys so we can confirm the
          # host we are connecting to is legitimate
          ssh.load_system_host_keys('/home/appuser/.ssh/known_hosts')

          # Connect to the remote host using our SSH private key
          ssh.connect(hostname='example.org',
                      port=22,
                      username='appuser',
                      key_filename='/home/appuser/.ssh/private_key')
      ```

      For more information on `set_missing_host_key_policy` see:
      - https://docs.paramiko.org/en/stable/api/client.html#paramiko.client.SSHClient.set_missing_host_key_policy
    metadata:
      cwe: "CWE-322"
      category: "security"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A07:2021-Identification and Authentication Failures"
      shortDescription: "Key exchange without entity authentication"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/paramiko']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          $CLIENT = paramiko.client.SSHClient(...)
          ...
          $CLIENT.set_missing_host_key_policy(...)
      - pattern-either:
          - pattern: "paramiko.client.AutoAddPolicy"
          - pattern: "paramiko.client.WarningPolicy"
    severity: "ERROR"
  - id: "python_ssl_rule-req-no-certvalid"
    languages:
      - "python"
    message: |
      The application was found using the `requests` module without configuring a timeout value for
      connections. The `verify=False` argument has been set, which effectively disables the
      validation
      of server certificates.

      This allows for an adversary who is in between the application and the target host to intercept
      potentially sensitive information or transmit malicious data.

      To remediate this issue either remove the `verify=False` argument, or set `verify=True`to each
      `requests` call.

      Example verifying server certificates for an HTTP GET request:
      ```
      # Issue a GET request to https://example.com with a timeout of 10 seconds and verify the
      # server certificate explicitly.
      response = requests.get('https://example.com', timeout=10, verify=True)
      # Work with the response object
      # ...
      ```

      For more information on using the requests module see:
      - https://requests.readthedocs.io/en/latest/api/
    metadata:
      cwe: "CWE-295"
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      category: "security"
      shortDescription: "Improper certificate validation"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/requests']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: "requests.put(..., verify=False, ...)"
          - pattern: "requests.patch(..., verify=False, ...)"
          - pattern: "requests.delete(..., verify=False, ...)"
          - pattern: "requests.head(..., verify=False, ...)"
          - pattern: "requests.options(..., verify=False, ...)"
          - pattern: "requests.request(..., verify=False, ...)"
          - pattern: "requests.get(..., verify=False, ...)"
          - pattern: "requests.post(..., verify=False, ...)"
    severity: "ERROR"
  - id: "python_ssl_rule-ssl-no-version"
    languages:
      - "python"
    message: |
      The application was found calling `ssl.wrap_socket` without a TLS protocol version specified.
      Additionally, `ssl.wrap_socket` has been deprecated since Python 3.7. It is strongly
      recommended
      that newer applications use TLS 1.2 or 1.3 and `SSLContext.wrap_socket`.

      To remediate this issue, create a new TLS context and pass in `ssl.PROTOCOL_TLS_CLIENT`
      for clients or `ssl.PROTOCOL_TLS_SERVER` for servers to the `ssl.SSLContext(...)` `protocol=`
      argument. When converting the socket to a TLS socket, use the new `SSLContext.wrap_socket`
      method instead.


      Example creating a TLS 1.3 client socket connection by using a newer version of Python
      (3.11.4) and
      the SSL module:
      ```
      import ssl
      import socket

      # Create our initial socket
      with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
          # Connect the socket
          sock.connect(('www.example.org', 443))

          # Create a new SSLContext with protocol set to ssl.PROTOCOL_TLS_CLIENT
          # This will auto-select the highest grade TLS protocol version (1.3)
          context = ssl.SSLContext(protocol=ssl.PROTOCOL_TLS_CLIENT)
          # Load our a certificates for server certificate authentication
          context.load_verify_locations('cert.pem')
          # Create our TLS socket, and validate the server hostname matches
          with context.wrap_socket(sock, server_hostname="www.example.org") as tls_sock:
              # Send some bytes over the socket (HTTP request in this case)\
              data = bytes('GET / HTTP/1.1\r\nHost: example.org\r\n\r\n', 'utf-8')
              sent_bytes = tls_sock.send(data)
              # Validate number of sent bytes
              # ...
              # Read the response
              resp = tls_sock.recv()
              # Work with the response
              # ...
      ```

      For more information on the ssl module see:
      - https://docs.python.org/3/library/ssl.html
    metadata:
      cwe: "CWE-326"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      shortDescription: "Inadequate encryption strength"
      security-severity: "Medium"
      category: "security"
      related-packages: ['pkg:pypi/ssl']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: "ssl.wrap_socket()"
    severity: "WARNING"
  - id: "python_ssl_rule-ssl-with-bad-version"
    languages:
      - "python"
    message: |
      The application was found calling an SSL module with SSL or TLS protocols that have known
      deficiencies.
      It is strongly recommended that newer applications use TLS 1.2 or 1.3 and
      `SSLContext.wrap_socket`.

      If using the `pyOpenSSL` module, please note that it has been deprecated and the Python
      Cryptographic Authority
      strongly suggests moving to use the [pyca/cryptography](https://github.com/pyca/cryptography)
      module instead.

      To remediate this issue for the `ssl` module, create a new TLS context and pass in
      `ssl.PROTOCOL_TLS_CLIENT` for clients or `ssl.PROTOCOL_TLS_SERVER` for servers to the
      `ssl.SSLContext(...)` `protocol=`
      argument. When converting the socket to a TLS socket, use the new `SSLContext.wrap_socket`
      method instead.

      Example creating a TLS 1.3 client socket connection by using a newer version of Python
      (3.11.4) and
      the SSL module:
      ```
      import ssl
      import socket

      # Create our initial socket
      with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
          # Connect the socket
          sock.connect(('www.example.org', 443))

          # Create a new SSLContext with protocol set to ssl.PROTOCOL_TLS_CLIENT
          # This will auto-select the highest grade TLS protocol version (1.3)
          context = ssl.SSLContext(protocol=ssl.PROTOCOL_TLS_CLIENT)
          # Load our a certificates for server certificate authentication
          context.load_verify_locations('cert.pem')
          # Create our TLS socket, and validate the server hostname matches
          with context.wrap_socket(sock, server_hostname="www.example.org") as tls_sock:
              # Send some bytes over the socket (HTTP request in this case)\
              data = bytes('GET / HTTP/1.1\r\nHost: example.org\r\n\r\n', 'utf-8')
              sent_bytes = tls_sock.send(data)
              # Validate number of sent bytes
              # ...
              # Read the response
              resp = tls_sock.recv()
              # Work with the response
              # ...
      ```

      For more information on the ssl module see:
      - https://docs.python.org/3/library/ssl.html

      For more information on pyca/cryptography and openssl see:
      - https://cryptography.io/en/latest/openssl/
    metadata:
      cwe: "CWE-326"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      shortDescription: "Inadequate Encryption Strength"
      security-severity: "Medium"
      category: "security"
      related-packages: ['pkg:pypi/ssl', 'pkg:pypi/pyopenssl']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: "ssl.PROTOCOL_SSLv2"
          - pattern: "ssl.PROTOCOL_SSLv3"
          - pattern: "ssl.PROTOCOL_TLSv1"
          - pattern: "ssl.PROTOCOL_TLSv1_1"
          - pattern: "pyOpenSSL.SSL.SSLv2_METHOD"
          - pattern: "pyOpenSSL.SSL.SSLv23_METHOD"
          - pattern: "pyOpenSSL.SSL.SSLv3_METHOD"
          - pattern: "pyOpenSSL.SSL.TLSv1_METHOD"
          - pattern: "pyOpenSSL.SSL.TLSv1_1_METHOD"
    severity: "ERROR"
  - id: "python_ssl_rule-unverified-context"
    languages:
      - "python"
    message: |
      The application was found creating a SSL context using the `_create_unverified_context`.
      This effectively disables the validation of server certificates.

      This allows for an adversary who is in between the application and the target host to intercept
      potentially sensitive information or transmit malicious data.

      To remediate this issue remove the call to `_create_unverified_context` and either create a
      default
      context using `ssl.create_default_context` or create a context with TLS 1.3.

      Example creating a TLS 1.3 client socket connection by using a newer version of Python
      (3.11.4) and
      the SSL module:
      ```
      import ssl
      import socket

      # Create our initial socket
      with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
          # Connect the socket
          sock.connect(('www.example.org', 443))

          # Create a new SSLContext with protocol set to ssl.PROTOCOL_TLS_CLIENT
          # This will auto-select the highest grade TLS protocol version (1.3)
          context = ssl.SSLContext(protocol=ssl.PROTOCOL_TLS_CLIENT)
          # Load our a certificates for server certificate authentication
          context.load_verify_locations('cert.pem')
          # Create our TLS socket, and validate the server hostname matches
          with context.wrap_socket(sock, server_hostname="www.example.org") as tls_sock:
              # Send some bytes over the socket (HTTP request in this case)\
              data = bytes('GET / HTTP/1.1\r\nHost: example.org\r\n\r\n', 'utf-8')
              sent_bytes = tls_sock.send(data)
              # Validate number of sent bytes
              # ...
              # Read the response
              resp = tls_sock.recv()
              # Work with the response
              # ...
      ```

      Unverified SSL context detected. This will permit insecure connections without `verifyingSSL`
      certificates. Use `ssl.create_default_context()` instead.
    metadata:
      cwe: "CWE-295"
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      category: "security"
      shortDescription: "Improper certificate validation"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/ssl']
      license: MIT (c) GitLab Inc.
    pattern: "ssl._create_unverified_context(...)"
    severity: "ERROR"
  - id: "python_telnet_rule-import-telnib"
    languages:
      - "python"
    message: |
      The application was found using a telnet library. As telnet does not provide encryption, it is
      strongly recommended that communications use a more secure transport such as
      SSH.

      The [paramiko](https://www.paramiko.org/) library can be used to initiate SSH connections.

      Example using `paramiko` SSH client:
      ```
      import paramiko
      import scp

      # Create an SSH client
      with paramiko.SSHClient() as ssh:
          # Load the system host keys so we can confirm the
          # host we are connecting to is legitimate
          ssh.load_system_host_keys('/home/appuser/.ssh/known_hosts')

          # Connect to the remote host using our SSH private key
          ssh.connect(hostname='example.org',
                      port=22,
                      username='appuser',
                      key_filename='/home/appuser/.ssh/private_key')
          # Work with the connection
      ```

      For more information on the paramiko module see:
      - https://www.paramiko.org/
    metadata:
      cwe: "CWE-319"
      category: "security"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      shortDescription: "Cleartext transmission of sensitive information"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: "import telnetlib"
    severity: "ERROR"
  - id: "python_tmpdir_rule-hardcodedtmp"
    languages:
      - "python"
    message: |
      The application was found creating files in shared system temporary directories
      (`/tmp` or `/var/tmp`) without using the `tempfile.TemporaryFile` function. Depending
      on how the application uses this temporary file, an attacker may be able to create
      symlinks that point to other files prior to the application creating or writing
      to the target file, leading to unintended files being created or overwritten.

      Example using `tempfile.TemporaryFile` to write a file:
      ```
      import tempfile

      # Open a new temporary file using a context manager
      with tempfile.TemporaryFile() as fp:
          # Write some data to the temporary file
          fp.write(b'Some data')
          # Seek back to beginning of file
          fp.seek(0)
          # Read it
          data = fp.read()
      # File is automatically closed/removed once we exit the with context
      ```

      For more information on alternative tempfile functions see:
      - https://docs.python.org/3/library/tempfile.html
    metadata:
      cwe: "CWE-377"
      category: "security"
      shortDescription: "Insecure temporary file"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    pattern: "$CALL(\"=~/^\\/tmp.*/\", ...)"
    severity: "WARNING"
  - id: "python_tmpdir_rule-mktemp-q"
    languages:
      - "python"
    message: |
      The application was found creating temporary files with the insecure `mktemp` method.
      Depending on how the application uses this temporary file, an attacker may be able to create
      symlinks that point to other files prior to the application creating or writing
      to the target file, leading to unintended files being created or overwritten.

      To remediate this issue consider using `tempfile.TemporaryFile` instead.

      Example using `tempfile.TemporaryFile` to write a file:
      ```
      import tempfile

      # Open a new temporary file using a context manager
      with tempfile.TemporaryFile() as fp:
          # Write some data to the temporary file
          fp.write(b'Some data')
          # Seek back to beginning of file
          fp.seek(0)
          # Read it
          data = fp.read()
      # File is automatically closed/removed once we exit the with context
      ```

      For more information on alternative tempfile functions see:
      - https://docs.python.org/3/library/tempfile.html
    metadata:
      cwe: "CWE-377"
      category: "security"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A01:2021-Broken Access Control"
      shortDescription: "Insecure temporary file"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    pattern: "tempfile.mktemp(...)"
    severity: "ERROR"
  - id: python_tornado_rule_reflected_xss
    languages:
      - python
    severity: ERROR
    message: |
      User controlled input given to Tornado self.write() or self.redirect() methods can lead to XSS attacks as they do not escape the variable. Prefer using tornado.web.RequestHandler render() methods which auto escapes variables rendered in the Template. 
      If you need to use self.write() or self.redirect(), make sure to sanitize them using tornado.escape.xhtml_escape(...) or tornado.escape.url_escape(...) if it is a URL. 
    metadata:
      cwe: CWE-79
      owasp:
        - A7:2017-Cross-Site Scripting (XSS)
        - A03:2021-Injection
      related-packages:
        - pkg:pypi/tornado
      category: security
      shortDescription: Improper neutralization of user input rendered in Tornado ('XSS')
      security-severity: High
      confidence: High
      license: License Not Found
    pattern-sources:
      - patterns:
          - pattern-inside: |
              import tornado 
              ...
              class $CLASS(..., tornado.web.RequestHandler, ...):
                ...
          - pattern-inside: |
              def $METHOD(self, ...):
                ...
          - metavariable-regex:
              metavariable: $METHOD
              regex: 'get|post'
          - pattern-either:
              - pattern: <... self.get_argument(...) ...>
              - pattern: <... self.request.files[...][...] ...>


    pattern-sinks:
      - pattern-either:
          - pattern: self.write(...)
          - pattern: self.redirect(...)

    pattern-sanitizers:
      - pattern-either:
          - pattern: tornado.escape.xhtml_escape(...)
          - pattern: tornado.escape.url_escape(...)

    mode: taint
  - id: python_tornado_rule_reflected_xss_csp
    languages:
      - python
    severity: ERROR
    message: |
      Unsecure Content Security Policy in Tornado. Avoid using unsafe-inline, unsafe-eval and unsafe-hashes, as they can lead to XSS attacks. 
      Avoid overly permissive CSP containing '*'. Prefer using 'self' or allow lists. 
      Never set X-XSS-Protection to 0. 
    metadata:
      cwe: CWE-79
      owasp:
        - A7:2017-Cross-Site Scripting (XSS)
        - A03:2021-Injection
      related-packages:
        - pkg:pypi/tornado
      category: security
      shortDescription: Improper neutralization of user input rendered in HTML ('XSS')
      security-severity: High
      confidence: Hight
      license: License Not Found
    patterns:
      - pattern-inside: |
          import tornado
          ...
      - pattern-inside: |
          class $C(...):
            ...
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  self.set_header("Content-Security-Policy", ...)
              - pattern-either:
                  - pattern-regex: unsafe\-inline
                  - pattern-regex: unsafe\-eval
                  - pattern-regex: unsafe\-hashes
                  - pattern-regex: '\*[^\.]'
          - pattern: self.set_header("X-XSS-Protection", "0")



  - id: python_tornado_rule_reflected_xss_global
    languages:
      - python
    severity: ERROR
    message: |
      # Reflected XSS vulnerability in Django 
    metadata:
      cwe: CWE-79
      owasp:
        - A7:2017-Cross-Site Scripting (XSS)
        - A03:2021-Injection
      related-packages:
        - pkg:pypi/tornado
      category: security
      shortDescription: Improper neutralization of user input rendered in HTML ('XSS')
      security-severity: High
      confidence: High
      license: License Not Found
    pattern-either:
    # disable autoescape for Tornado App
      - patterns:
          - pattern-inside: |
              import tornado
              ...
          - pattern-inside: |
              class $APP(tornado.web.Application):
                ...
          - pattern: |
              dict(
              ...,
              autoescape=None,
              ...
              )
      - pattern: |
          tornado.web.Application(
            ...,
            autoescape=None, 
            ...)
      - pattern: tornado.web.Application.__init__(..., autoescape=None, ...)
      - patterns:
          - pattern-inside: |
              $T = tornado.template.Loader(...)
              ...
          - pattern: $T.autoescape = None
      - pattern: tornado.template.Loader(...).autoescape = None
      - pattern: tornado.template.Loader(..., autoescape=None, ...)



  - id: "python_urlopen_rule-urllib-urlopen"
    languages:
      - "python"
    message: |
      The application was found passing in a non-literal value to the `urllib` methods which issue
      requests. `urllib` supports the `file://` scheme, which may allow an adversary who can control
      the URL value to read arbitrary files on the file system.

      To remediate this issue either hardcode the URLs being used in urllib or use the `requests`
      module instead.

      Example using the `requests` module to issue an HTTPS request:
      ```
      import requests
      # Issue a GET request to https://example.com with a timeout of 10 seconds
      response = requests.get('https://example.com', timeout=10)
      # Work with the response object
      # ...
      ```
    patterns:
      - pattern-either:
          - patterns:
              - pattern-either:
                  - pattern: "urllib.$METHOD(...)"
                  - pattern: "urllib.request.$METHOD(...)"
              - pattern-not: "urllib.$METHOD(\"...\")"
              - pattern-not: "urllib.request.$METHOD(\"...\")"
              - pattern-not: "urllib.$METHOD(\"...\", ...)"
              - pattern-not: "urllib.request.$METHOD(\"...\", ...)"
              - metavariable-regex:
                  metavariable: "$METHOD"
                  regex: "(urlopen|urlretrieve)"
          - patterns:
              - pattern-either:
                  - pattern-inside: |
                      $OPENER = urllib.URLopener(...)
                      ...
                  - pattern-inside: |
                      $OPENER = urllib.request.URLopener(...)
                      ...
                  - pattern-inside: |
                      $OPENER = urllib.FancyURLopener(...)
                      ...
                  - pattern-inside: |
                      $OPENER = urllib.request.FancyURLopener(...)
                      ...
              - pattern-either:
                  - pattern: "$OPENER.open(...)"
                  - pattern: "$OPENER.retrieve(...)"
              - pattern-not: "$OPENER.open(\"...\")"
              - pattern-not: "$OPENER.retrieve(\"...\")"
    metadata:
      cwe: "CWE-939"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      shortDescription: "Improper authorization in handler for custom URL scheme"
      security-severity: "Medium"
      category: "security"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "python_xml_rule-celement"
    languages:
      - "python"
    message: |
      The application was found using the `xml.etree` package for processing XML.
      Pythons default xml processors suffer from various XML parsing vulnerabilities
      and care must be taken when handling XML data. Additionally, depending on the
      version of Python, more critical vulnerabilities such as eXternal XML Entity
      injection maybe exploitable.

      The `etree` package suffers from the following security risks as of Python 3.7.1:
      * Billion laughs / exponential entity expansion - May allow an adversary to cause
        a Denial of Service (DoS) against the application parsing arbitrary XML.
      * Quadratic blowup entity expansion - Similar to above, but requires a larger input
        to cause the Denial of Service.

      To remediate the above issues, consider using the
      [defusedxml](https://pypi.org/project/defusedxml/)
      library when processing untrusted XML.

      Example parsing an XML document using defusedxml:
      ```
      from defusedxml.ElementTree import parse

      # Parse the inventory.xml file
      et = parse('inventory.xml')
      # Get the root element
      root = et.getroot()
      # Work with the root element
      # ...
      ```

      For more information on the various XML parsers and their vulnerabilities please see:
      - https://docs.python.org/3/library/xml.html#xml-vulnerabilities

      For more information on XML security see OWASP's guide:
      - https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#python
    metadata:
      cwe: "CWE-611"
      owasp:
        - "A4:2017-XML External Entities (XXE)"
        - "A03:2021-Injection"
      category: "security"
      shortDescription: "Improper restriction of XML external entity reference"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    pattern-either:
      - patterns:
          - pattern: "xml.etree.cElementTree.fromstring(...)"
          - pattern-not: "xml.etree.cElementTree.fromstring(\"...\")"
      - pattern: "xml.etree.cElementTree.parse(...)"
      - pattern: "xml.etree.cElementTree.iterparse(...)"
      - pattern: "xml.etree.cElementTree.XMLParser(...)"
    severity: "WARNING"
  - id: "python_xml_rule-element"
    languages:
      - "python"
    message: |
      The application was found using the `xml.etree` package for processing XML.
      Pythons default xml processors suffer from various XML parsing vulnerabilities
      and care must be taken when handling XML data. Additionally, depending on the
      version of Python, more critical vulnerabilities such as eXternal XML Entity
      injection maybe exploitable.

      The `etree` package suffers from the following security risks as of Python 3.7.1:
      * Billion laughs / exponential entity expansion - May allow an adversary to cause
        a Denial of Service (DoS) against the application parsing arbitrary XML.
      * Quadratic blowup entity expansion - Similar to above, but requires a larger input
        to cause the Denial of Service.

      To remediate the above issues, consider using the
      [defusedxml](https://pypi.org/project/defusedxml/)
      library when processing untrusted XML.

      Example parsing an XML document using defusedxml:
      ```
      from defusedxml.ElementTree import parse

      # Parse the inventory.xml file
      et = parse('inventory.xml')
      # Get the root element
      root = et.getroot()
      # Work with the root element
      # ...
      ```

      For more information on the various XML parsers and their vulnerabilities please see:
      - https://docs.python.org/3/library/xml.html#xml-vulnerabilities

      For more information on XML security see OWASP's guide:
      - https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#python
    metadata:
      cwe: "CWE-611"
      owasp:
        - "A4:2017-XML External Entities (XXE)"
        - "A03:2021-Injection"
      category: "security"
      shortDescription: "Improper restriction of XML external entity reference"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    pattern-either:
      - patterns:
          - pattern: "xml.etree.ElementTree.fromstring(...)"
          - pattern-not: "xml.etree.ElementTree.fromstring(\"...\")"
      - pattern: "xml.etree.ElementTree.parse(...)"
      - pattern: "xml.etree.ElementTree.iterparse(...)"
      - pattern: "xml.etree.ElementTree.XMLParser(...)"
    severity: "WARNING"
  - id: "python_xml_rule-etree"
    languages:
      - "python"
    message: |
      The application was found using the `lxml.etree` package for processing XML.
      Python's default XML processors suffer from various XML parsing vulnerabilities
      and care must be taken when handling XML data. Additionally, depending on the
      version of Python, more critical vulnerabilities such as eXternal XML Entity
      injection maybe exploitable.

      The `etree` package suffers from the following security risks as of Python 3.7.1:
      * Billion laughs / exponential entity expansion - May allow an adversary to cause
        a Denial of Service (DoS) against the application parsing arbitrary XML.
      * Quadratic blowup entity expansion - Similar to above, but requires a larger input
        to cause the Denial of Service.

      To remediate the above issues, consider using the
      [defusedxml](https://pypi.org/project/defusedxml/)
      library when processing untrusted XML.

      Example parsing an XML document using defusedxml:
      ```
      from defusedxml.ElementTree import parse

      # Parse the inventory.xml file
      et = parse('inventory.xml')
      # Get the root element
      root = et.getroot()
      # Work with the root element
      # ...
      ```

      For more information on the various XML parsers and their vulnerabilities please see:
      - https://docs.python.org/3/library/xml.html#xml-vulnerabilities

      For more information on XML security see OWASP's guide:
      - https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#python
    metadata:
      cwe: "CWE-611"
      owasp:
        - "A4:2017-XML External Entities (XXE)"
        - "A03:2021-Injection"
      category: "security"
      shortDescription: "Improper restriction of XML external entity reference"
      security-severity: "Medium"
      related-packages: ['pkg:pypi/lxml']
      license: MIT (c) GitLab Inc.
    pattern-either:
      - pattern: "lxml.etree.parse(...)"
      - patterns:
          - pattern: "lxml.etree.fromstring(...)"
          - pattern-not: "lxml.etree.fromstring(\"...\")"
      - pattern: "lxml.etree.RestrictedElement(...)"
      - pattern: "lxml.etree.GlobalParserTLS(...)"
      - pattern: "lxml.etree.getDefaultParser(...)"
      - pattern: "lxml.etree.check_docinfo(...)"
    severity: "WARNING"
  - id: "python_xml_rule-expatbuilder"
    languages:
      - "python"
    message: |
      The application was found using the `xml.dom.expatbuilder` which calls the `xml.dom.minidom`
      package for processing XML. Python's default XML processors suffer from various XML parsing
      vulnerabilities
      and care must be taken when handling XML data. Additionally, depending on the
      version of Python, more critical vulnerabilities such as eXternal XML Entity
      injection maybe exploitable.

      The `xml.dom.minidom` package suffers from the following security risks as of Python 3.7.1:
      * Billion laughs / exponential entity expansion - May allow an adversary to cause
        a Denial of Service (DoS) against the application parsing arbitrary XML.
      * Quadratic blowup entity expansion - Similar to above, but requires a larger input
        to cause the Denial of Service.

      To remediate the above issues, consider using the
      [defusedxml](https://pypi.org/project/defusedxml/)
      library when processing untrusted XML.

      Example parsing an XML document using defusedxml:
      ```
      from defusedxml.ElementTree import parse

      # Parse the inventory.xml file
      et = parse('inventory.xml')
      # Get the root element
      root = et.getroot()
      # Work with the root element
      # ...
      ```

      For more information on the various XML parsers and their vulnerabilities please see:
      - https://docs.python.org/3/library/xml.html#xml-vulnerabilities

      For more information on XML security see OWASP's guide:
      - https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#python
    metadata:
      cwe: "CWE-611"
      owasp:
        - "A4:2017-XML External Entities (XXE)"
        - "A03:2021-Injection"
      category: "security"
      shortDescription: "Improper restriction of XML external entity reference"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    pattern-either:
      - patterns:
          - pattern: "xml.dom.expatbuilder.parse(...)"
          - pattern-not: "xml.dom.expatbuilder.parse(\"...\")"
      - pattern: "xml.dom.expatbuilder.parseString(...)"
    severity: "WARNING"
  - id: "python_xml_rule-expatreader"
    languages:
      - "python"
    message: |
      The application was found using the `xml.sax.expatreader` package for processing XML. Python's
      default XML processors suffer from various XML parsing vulnerabilities
      and care must be taken when handling XML data. Additionally, depending on the
      version of Python, more critical vulnerabilities such as eXternal XML Entity
      injection maybe exploitable.

      The `xml.sax` package suffers from the following security risks as of Python 3.7.1:
      * Billion laughs / exponential entity expansion - May allow an adversary to cause
        a Denial of Service (DoS) against the application parsing arbitrary XML.
      * Quadratic blowup entity expansion - Similar to above, but requires a larger input
        to cause the Denial of Service.

      To remediate the above issues, consider using the
      [defusedxml](https://pypi.org/project/defusedxml/)
      library when processing untrusted XML.

      Example parsing an XML document using defusedxml:
      ```
      from defusedxml.ElementTree import parse

      # Parse the inventory.xml file
      et = parse('inventory.xml')
      # Get the root element
      root = et.getroot()
      # Work with the root element
      # ...
      ```

      For more information on the various XML parsers and their vulnerabilities please see:
      - https://docs.python.org/3/library/xml.html#xml-vulnerabilities

      For more information on XML security see OWASP's guide:
      - https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#python
    metadata:
      cwe: "CWE-611"
      owasp:
        - "A4:2017-XML External Entities (XXE)"
        - "A03:2021-Injection"
      category: "security"
      shortDescription: "Improper restriction of XML external entity reference"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    pattern-either:
      - pattern: "xml.dom.expatreader.parse(...)"
      - patterns:
          - pattern: "xml.dom.expatreader.parseString(...)"
          - pattern-not: "xml.dom.expatreader.parseString(\"...\")"
      - pattern: "xml.dom.expatreader.parseString(...)"
      - pattern: "xml.dom.expatreader.create_parser(...)"
    severity: "WARNING"
  - id: "python_xml_rule-minidom"
    languages:
      - "python"
    message: |
      The application was found using the `xml.dom.minidom` package for processing XML. Python's
      default XML processors suffer from various XML parsing vulnerabilities
      and care must be taken when handling XML data. Additionally, depending on the
      version of Python, more critical vulnerabilities such as eXternal XML Entity
      injection maybe exploitable.

      The `xml.dom.minidom` package suffers from the following security risks as of Python 3.7.1:
      * Billion laughs / exponential entity expansion - May allow an adversary to cause
        a Denial of Service (DoS) against the application parsing arbitrary XML.
      * Quadratic blowup entity expansion - Similar to above, but requires a larger input
        to cause the Denial of Service.

      To remediate the above issues, consider using the
      [defusedxml](https://pypi.org/project/defusedxml/)
      library when processing untrusted XML.

      Example parsing an XML document using defusedxml:
      ```
      from defusedxml.ElementTree import parse

      # Parse the inventory.xml file
      et = parse('inventory.xml')
      # Get the root element
      root = et.getroot()
      # Work with the root element
      # ...
      ```

      For more information on the various XML parsers and their vulnerabilities please see:
      - https://docs.python.org/3/library/xml.html#xml-vulnerabilities

      For more information on XML security see OWASP's guide:
      - https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#python
    metadata:
      cwe: "CWE-611"
      owasp:
        - "A4:2017-XML External Entities (XXE)"
        - "A03:2021-Injection"
      category: "security"
      shortDescription: "Improper restriction of XML external entity reference"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    pattern-either:
      - patterns:
          - pattern: "xml.dom.minidom.parseString(...)"
          - pattern-not: "xml.dom.minidom.parseString(\"...\")"
      - pattern: "xml.dom.minidom.parse(...)"
    severity: "WARNING"
  - id: "python_xml_rule-pulldom"
    languages:
      - "python"
    message: |
      The application was found using the `xml.dom.pulldom` package for processing XML. Python's
      default XML processors suffer from various XML parsing vulnerabilities
      and care must be taken when handling XML data. Additionally, depending on the
      version of Python, more critical vulnerabilities such as eXternal XML Entity
      injection maybe exploitable.

      The `xml.dom.pulldom` package suffers from the following security risks as of Python 3.7.1:
      * Billion laughs / exponential entity expansion - May allow an adversary to cause
        a Denial of Service (DoS) against the application parsing arbitrary XML.
      * Quadratic blowup entity expansion - Similar to above, but requires a larger input
        to cause the Denial of Service.

      To remediate the above issues, consider using the
      [defusedxml](https://pypi.org/project/defusedxml/)
      library when processing untrusted XML.

      Example parsing an XML document using defusedxml:
      ```
      from defusedxml.ElementTree import parse

      # Parse the inventory.xml file
      et = parse('inventory.xml')
      # Get the root element
      root = et.getroot()
      # Work with the root element
      # ...
      ```

      For more information on the various XML parsers and their vulnerabilities please see:
      - https://docs.python.org/3/library/xml.html#xml-vulnerabilities

      For more information on XML security see OWASP's guide:
      - https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#python
    metadata:
      cwe: "CWE-611"
      owasp:
        - "A4:2017-XML External Entities (XXE)"
        - "A03:2021-Injection"
      category: "security"
      shortDescription: "Improper restriction of XML external entity reference"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    pattern-either:
      - patterns:
          - pattern: "xml.dom.pulldom.parseString(...)"
          - pattern-not: "xml.dom.pulldom.parseString(\"...\")"
      - pattern: "xml.dom.pulldom.parse(...)"
    severity: "WARNING"
  - id: "python_xml_rule-sax"
    languages:
      - "python"
    message: |
      The application was found using the `xml.sax` package for processing XML.
      Python's default XML processors suffer from various XML parsing vulnerabilities
      and care must be taken when handling XML data. Additionally, depending on the
      version of Python, more critical vulnerabilities such as eXternal XML Entity
      injection maybe exploitable.

      The `xml.sax` package suffers from the following security risks as of Python 3.7.1:
      * Billion laughs / exponential entity expansion - May allow an adversary to cause
        a Denial of Service (DoS) against the application parsing arbitrary XML.
      * Quadratic blowup entity expansion - Similar to above, but requires a larger input
        to cause the Denial of Service.

      To remediate the above issues, consider using the
      [defusedxml](https://pypi.org/project/defusedxml/)
      library when processing untrusted XML.

      Example parsing an XML document using defusedxml:
      ```
      from defusedxml.ElementTree import parse

      # Parse the inventory.xml file
      et = parse('inventory.xml')
      # Get the root element
      root = et.getroot()
      # Work with the root element
      # ...
      ```

      For more information on the various XML parsers and their vulnerabilities please see:
      - https://docs.python.org/3/library/xml.html#xml-vulnerabilities

      For more information on XML security see OWASP's guide:
      - https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#python
    metadata:
      cwe: "CWE-611"
      owasp:
        - "A4:2017-XML External Entities (XXE)"
        - "A03:2021-Injection"
      category: "security"
      shortDescription: "Improper restriction of XML external entity reference"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    pattern-either:
      - pattern: "xml.sax.parse(...)"
      - patterns:
          - pattern: "xml.sax.parseString(...)"
          - pattern-not: "xml.sax.parseString(\"...\")"
      - pattern: "xml.sax.make_parser(...)"
    severity: "WARNING"
  - id: rule-js-ts-child-process-exec-injection
    languages:
      - javascript
    severity: ERROR
    message: |
      A Remote Command Injection vulnerability was found using Express or Node.js. A user-controlled input was directly used in exec() function from child_process. This gives the user shell access. 

      Avoid using user-controlled variables in exec(), or use a whitelist to verify it. Alternatively, use spawn function from 'child_process'.

    metadata:
      cwe: CWE-78
      owasp:
        - A1:2017-Injection
        - A03:2021-Injection
      category: security
      shortDescription: "Command injection using exec() from child_process"
      security-severity: High
      confidence: High
      license: License Not Found
    pattern-sources:
      - patterns:
          - pattern-inside: |
              import { exec } from 'child_process';
              ...
          - pattern-inside: |
              $APP.get(..., (..., $REQ, ...) => {
                ...
              })  
          - pattern-either:
              - pattern: const $INPUT = $REQ. ...;
              - pattern: <... $REQ ...>;
    pattern-sinks:
      - patterns:
          - pattern: exec($INPUT, ...);
          - focus-metavariable: $INPUT
    mode: taint
  - id: "rules-javascript-xss-generic"
    mode: taint
    options:
      taint_unify_mvars: true

    pattern-sources:
      - pattern-either:
      # user input from request
          - patterns:
              - pattern-inside: |
                  function $FUNC($REQ, $RES, ...) {...}
              - pattern: $REQ
      # from get* and read* methods and prompt() 
          - patterns:
              - pattern-either:
                  - pattern: $PARAM = $GET(...)
                  - pattern: $PARAM = ... .$GET(...)
              - pattern-not: $PARAM = config.$GET('...')
              - metavariable-regex:
                  metavariable: $GET
                  regex: ((?i)(get|read|search))|^prompt$
      # other
          - pattern: $PARAM = decodeURIComponent(...)
          - pattern: $PARAM = location.hash
          - pattern: $PARAM = location.pathname
          - pattern: $PARAM = location.href
          - pattern: $PARAM = document.cookie
          - pattern: $PARAM = arguments[...]
      # from function arg
          - patterns:
              - pattern-either:
                  - pattern: function $F(..., $PARAM, ...) {...}
                  - pattern: (..., $PARAM, ...) => {...}
              - pattern: $PARAM
      # from DOM
          - pattern: $PARAM = document.querySelector(...).value;
          - pattern: $PARAM = document.getElementById(...).value;
          - pattern: form.elements[...].value;
      # JQuery
          - pattern: $PARAM = <... $("...").val() ...>;

    pattern-propagators:
      - pattern: $ARR.push($IN)
        from: $IN
        to: $ARR
      - pattern: $ARR = $.parseHTML($IN)
        from: $IN
        to: $ARR

    pattern-sanitizers:
      - pattern: "encodeURI(...)"
      - pattern: "encodeURIComponent(...)" # URL context
      - pattern: "DOMPurify.sanitize(...)" # HTML context
      - pattern: "JSON.stringify(...)" # JS string 

    pattern-sinks:
      - patterns:
          - focus-metavariable: $INPUT
          - pattern-not: $RES. ... .send($R('...'))
          - pattern-either:
      # sending response
              - pattern: $RES. ... .send($INPUT)
              - pattern: $RES. ... .write($INPUT)
      # writing in html
              - pattern: document.writeln($INPUT);
      # injecting in html
              - pattern: '... .innerHTML = $INPUT'
              - pattern: '... .outerHTML = $INPUT'
              - pattern: '... .setTimeout = $INPUT'
              - pattern: '... .insertAdjacentHTML = $INPUT'
              - pattern: '... .setInterval = $INPUT'
              - pattern: '... .onevent = $INPUT'
              - pattern: "... .setAttribute('=~/^(on|href)/', $INPUT);"
      # jquery
              - pattern: $(...).append($INPUT);
              - pattern: $(...).html($INPUT);
              - pattern: $(...).prepend($INPUT);
              - pattern: $(...).after($INPUT);
              - pattern: $(...).before($INPUT);
              - pattern: $(...).replaceAll($INPUT);
              - pattern: $(...).replaceWith($INPUT);
              - pattern: $(...).wrap($INPUT);
              - pattern: $(...).wrapInner($INPUT);
              - pattern: $(...).wrapAll($INPUT);

    message: |
      Unsanitized user input is passed to a sensitive HTML or JavaScript sink. This can result in a Cross-Site Scripting (XSS) vulnerability if the input is not properly validated or sanitized.

      What’s the issue:
      - User-controlled data is reaching a function like `.send()`, `.innerHTML`, or jQuery DOM injection methods.
      - These sinks directly render the content into the DOM or send it in HTTP responses.
      - If the input contains HTML or JavaScript, it may be executed by the browser.

      Common sources include:
      - Request data (`req`, `req.query`, `req.body`, etc.)
      - DOM values (`document.querySelector().value`, `location.href`, etc.)
      - Potentialluy user-controlled function arguments

      How to fix it:
      - Use output encoding or context-specific sanitizers such as:
        - `DOMPurify.sanitize()` for HTML
        - `encodeURIComponent()` for URLs
        - `textContent` or `innerText` instead of `innerHTML`
        Be aware to sanitize your data in the correct context where it will be rendered. 
      - Avoid inserting raw HTML or untrusted input into HTML attributes or script contexts.
      - Consider using templating engines that auto-escape by default (e.g., Handlebars, Pug).

      Always validate and sanitize any data from external sources before rendering it in the browser.

    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      shortDescription: "Improper neutralization of input during web page generation ('Cross-site Scripting')"
      category: "security"
      cwe: "CWE-79"
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      security-severity: "MEDIUM"
      license: GNU Lesser General Public License v3.0
  - id: "rules_javascript_xss_bad_csp"

    patterns:
      - pattern-either:
          - pattern-inside: $CSP = `...`
          - pattern-inside: $SET_HEADER(..., `...`, ...)
      - pattern-either:
          - pattern-regex: (unsafe-eval|unsafe-inline|data:|blob:)
      # * in CSP
          - pattern-regex: '[a-z]+-src.*\s+\*(\s+|;)'
    message: |
      This Content Security Policy (CSP) configuration contains insecure directives that could allow Cross-Site Scripting (XSS) or other client-side attacks.

      Detected insecure CSP value:
      - `unsafe-eval`: Allows JavaScript execution via `eval()`, `setTimeout(string)`, etc.
      - `unsafe-inline`: Permits inline `<script>` tags and `on*` event handlers.
      - `data:` or `blob:`: Allows execution or loading of potentially attacker-controlled resources from data or blob URIs.
      - `*` wildcard: Permits loading content from any origin, which undermines the purpose of CSP.

      Recommended remediation:
      - Remove or replace `unsafe-eval` and `unsafe-inline` with secure alternatives (e.g., use nonces or hashes for inline scripts).
      - Avoid using `data:` and `blob:` in `script-src`, `object-src`, or `worker-src`.
      - Replace `*` with a specific list of trusted domains (e.g., `'self'`, your CDN, etc.).
      - Apply least privilege to each directive; allow only the sources truly necessary.

      Example of a safer CSP:
      ```http
      Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none';
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      shortDescription: "Unsafe CSP header"
      category: "security"
      cwe: "CWE-79"
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      security-severity: "MEDIUM"
      license: License Not Found
  - id: rules_lgpl_java_webview_rule-ignore-ssl-certificate-errors
    pattern: >
      $RET onReceivedSslError(WebView $W, SslErrorHandler $H, SslError
      $E) {
        ...
        $H.proceed();
      }
    message: |
      Insecure WebView Implementation. leading to a security problem known as SSL certificate 
      validation bypass. This occurs when the app fails to properly validate SSL certificates, 
      allowing potentially malicious or spoofed certificates to be accepted, leading to a 
      Man-in-the-Middle (MitM) attack where an attacker intercepts and manipulates communication 
      between the app and the server. 

      To fix this security issue, you should properly handle SSL errors and only proceed with 
      the connection if the SSL certificate is valid and trusted. Here's an example code in Java:
      ```      
      public class MyWebViewClient extends WebViewClient {      
        @Override
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
          // Check the SSL error type
          switch (error.getPrimaryError()) {
            case SslError.SSL_UNTRUSTED:
              // Certificate is untrusted
              // Handle the error appropriately, such as showing an error message
              break;
            case SslError.SSL_EXPIRED:
              // Certificate has expired
              // Handle the error appropriately
              break;
            case SslError.SSL_IDMISMATCH:
              // Certificate hostname mismatch
              // Handle the error appropriately
              break;
            case SslError.SSL_NOTYETVALID:
              // Certificate is not yet valid
              // Handle the error appropriately
              break;
          }
          // Cancel the connection
          // This prevents the WebView from loading the content
          handler.cancel();
        }
      }
      ```
    languages:
      - java
    severity: WARNING
    metadata:
      category: security
      cwe: "CWE-295"
      shortDescription: Improper certificate validation"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: MEDIUM
      related-packages: ['pkg:maven/androidx.webkit/webkit']
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_java_webview_rule-webview-debugging
    patterns:
      - pattern-either:
          - pattern: |
              $WB.setWebContentsDebuggingEnabled(true);
          - pattern: |
              $X = true;
              ...
              $WB.setWebContentsDebuggingEnabled($X);
    message: |
      Remote WebView debugging is enabled. This allows an attacker with
      debugging access to interact with the webview and steal or corrupt data.
      To fix these security issues, it is recommended to disable remote 
      debugging and restrict file access in the WebView. 
      Here's how you can do it:
      ```
      WebView webView = new WebView(context);

      // Disable remote debugging
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
          WebView.setWebContentsDebuggingEnabled(false);
      }

      // Restrict file access from file URLs
      webView.getSettings().setAllowFileAccessFromFileURLs(false);

      // Load a web page
      webView.loadUrl("https://example.com");
      ```
    languages:
      - java
    severity: WARNING
    metadata:
      category: security
      cwe: "CWE-489"
      shortDescription: "Active debug code"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      security-severity: MEDIUM
      related-packages: ['pkg:maven/androidx.webkit/webkit']
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_java_webview_rule-webview-external-storage
    patterns:
      - pattern-either:
          - pattern: |
              $X = <... $E.getExternalStorageDirectory() ...>;
              ...
              $WV.loadUrl(<... $X ...>);
          - pattern: |
              $WV.loadUrl(<... $E.getExternalStorageDirectory().$F() ...>);
          - pattern: |
              $X = <... Environment.getExternalStorageDirectory().$F() ...>;
              ...
              $WV.loadUrl(<... $X ...>);
          - pattern: |
              $X = <... $E.getExternalFilesDir(...) ...>;
              ...
              $WV.loadUrl(<... $X ...>);
    message: |
      WebView load files from external storage. Files in external storage can be
      modified by any application.

      Loading files from external storage in a WebView can introduce security risks, 
      as it allows web content to access potentially sensitive data stored on the 
      device's external storage. This can lead to unauthorized access to user data, 
      including personal files, credentials, or other sensitive information, by 
      malicious web content.

      To fix this security issue, you should avoid loading files directly from external 
      storage in a WebView. Instead, you should use a Content Provider or a secure file 
      storage mechanism to access files and provide them to the WebView as content.

      Here's a general approach to fix this problem:
      (1) Use a Content Provider: If you need to load files from external storage in a WebView, 
      consider using a Content Provider to securely access the files. Content Providers 
      provide controlled access to files stored on external storage and allow you to define 
      permissions for accessing them.
      (2) Secure File Storage: Store files containing sensitive data in a secure location, such 
      as internal storage or encrypted storage, and provide access to them through a secure 
      API. Avoid exposing sensitive files directly to the WebView.
      (3) Restrict WebView Access: Configure the WebView to restrict access to external resources 
      and content. Use methods like setAllowFileAccess() to control file access and 
      setAllowContentAccess() to control access to content from other origins.
      Here's an example of how you can use a Content Provider to provide secure access to 
      files in a WebView:
      ```
      // Define the URI of the content provider for accessing files
      Uri contentProviderUri = Uri.parse("content://com.example.myapp.provider/files");
      // Load the content from the Content Provider into the WebView
      webView.loadUrl(contentProviderUri.toString());
      ```
      In the above code, we define the URI of a Content Provider that provides access to files 
      stored in the app's external storage. The content is loaded from the Content Provider into 
      the WebView using loadUrl(), which ensures that access to files is controlled and secure, 
      preventing unauthorized access to sensitive data.
    languages:
      - java
    severity: ERROR
    metadata:
      category: security
      cwe: "CWE-749"
      shortDescription: "Exposed dangerous method or function"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: CRITICAL
      related-packages: ['pkg:maven/androidx.webkit/webkit']
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_java_webview_rule-webview-set-allow-file-access
    pattern: $WB.setAllowFileAccess(true);
    message: |
      WebView File System Access is enabled. An attacker able to inject 
      script into a WebView, could exploit the opportunity to unauthorized 
      access to sensitive user data or system files.

      To fix this security issue, you should disable file access in the 
      WebView or restrict it to specific directories. 
      An example:
      ```
      // Create a WebView instance
      WebView webView = new WebView(context);
      // Disable file access in the WebView
      webView.getSettings().setAllowFileAccess(false);
      ```
    languages:
      - java
    severity: WARNING
    metadata:
      category: security
      cwe: "CWE-73"
      shortDescription: "External control of file name or path"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: MEDIUM
      related-packages: ['pkg:maven/androidx.webkit/webkit']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_crypto_rule-node-aes-ecb"
    patterns:
      - pattern-either:
          - pattern: |
              $X.createCipheriv("=~/^aes-([0-9]+)-ecb$/i", ...)
          - pattern: |
              $X.createDecipheriv("=~/^aes-([0-9]+)-ecb$/i", ...)
    message: |
      AES with ECB mode is deterministic in nature and not suitable for encrypting large amount of repetitive data.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-327"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/crypto']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_crypto_rule-node-aes-noiv"
    patterns:
      - pattern-either:
          - pattern: |
              $X.createCipheriv("=~/^aes-/i", $KEY, "", ...)
    message: |
      AES algorithms requires an initialization vector (IV). Providing no or null IV in some implementation results to a 0 IV. Use of a deterministic IV makes dictionary attacks easier.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-327"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/crypto']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_crypto_rule-node-insecure-random-generator"
    pattern-either:
      - patterns:
          - pattern-inside: |
              require('crypto')
              ...
          - pattern: |
              $X.pseudoRandomBytes(...)
      - pattern: |
          Math.random(...)
    message: |
      This rule identifies use of cryptographically weak random number generators.
      Using cryptographically weak random number generators like `crypto.pseudoRandomBytes()` 
      and `Math.random()` for security-critical tasks can expose systems to significant 
      vulnerabilities. Attackers might predict the generated random numbers, compromising 
      the integrity and confidentiality of cryptographic operations. This could lead to 
      breaches where sensitive data is accessed or manipulated, authentication mechanisms 
      are bypassed, or secure communications are intercepted, ultimately undermining the 
      security of the entire system or application.

      Mitigation strategy:
      Replace the use of these cryptographically weak random number generators with 
      `crypto.randomBytes()`, a method provided by Node.js's `crypto` module that 
      generates cryptographically secure random numbers. This method should be used 
      for all operations requiring secure randomness, such as generating keys, tokens, 
      or any cryptographic material.

      Secure Code Example:
      ```
      const crypto = require('crypto');
      const secureBytes = crypto.randomBytes(256);
      console.log(`Secure random bytes: ${secureBytes.toString('hex')}`);
      ```
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-338"
      shortDescription: "Use of cryptographically weak pseudo-random number generator (PRNG)"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/crypto']
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_javascript_crypto_rule-node-md5
    pattern-either:
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern-inside: |
                      $Y = $X.createHash('md5')
                      ...
                  - pattern: |
                      $Y.update("...")
              - pattern: |
                  $X.createHash('md5').update("...")
      - patterns:
          - pattern-inside: |
              require('crypto-js')
              ...
          - pattern: |
              $X.MD5("...")
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  $M = require('md5')
                  ...
              - pattern-inside: |
                  $M = require('blueimp-md5')
                  ...
              - pattern-inside: |
                  $M = require('js-md5')
                  ...
          - pattern: |
              $M("...")
      - patterns:
          - pattern-inside: |
              require('node-forge')
              ...
          - pattern-either:
              - patterns:
                  - pattern-inside: |
                      $Y = $X.md.md5.create()
                      ...
                  - pattern: |
                      $Y.update("...")
              - pattern: |
                  $X.md.md5.create().update("...")
      - patterns:
          - pattern-inside: |
              $X = require('jshashes')
              ...
              $Y = new $X.MD5()
              ...
          - pattern: |
              $Y.$METHOD("...")
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern-inside: |
                      $X = require('create-hash')
                      ...
                      $Y = $X('md5')
                      ...
                  - pattern: |
                      $Y.update("...")
              - patterns:
                  - pattern-inside: |
                      $X = require('create-hash')
                      ...
                  - pattern: |
                      $X('md5').update("...")      
    message: |
      The MD5 hashing algorithm is considered cryptographically weak and 
      vulnerable to collision attacks, where two different inputs generate 
      the same output hash. When used for hashing sensitive data, attackers 
      can exploit this weakness to generate collisions, allowing them to bypass 
      security checks or masquerade malicious data as legitimate. This 
      vulnerability is particularly critical in authentication mechanisms, 
      digital signatures, SSL/TLS certificates, and data integrity checks.

      Remediation:
      To mitigate this vulnerability, replace the MD5 hashing algorithm with 
      stronger cryptographic hash functions, such as SHA-256 or SHA-3. These 
      algorithms offer significantly improved security and are resistant to 
      collision attacks, making them suitable for cryptographic purposes in 
      modern applications.

      Secure Code example :
      ```
      const crypto = require('crypto');
      const hash = crypto.createHash('sha256').update('sensitive data').digest('hex');
      console.log(hash); 
      ```
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      shortDescription: "Use of weak hash"
      category: "security"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-328"
      security-severity: "MEDIUM"
      related-packages: ['pkg:npm/crypto']
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_javascript_crypto_rule-node-sha1
    pattern-either:
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern-inside: |
                      $Y = $X.createHash('sha1')
                      ...
                  - pattern: |
                      $Y.update("...")
              - pattern: |
                  $X.createHash('sha1').update("...")
      - patterns:
          - pattern-inside: |
              require('crypto-js')
              ...
          - pattern: |
              $X.SHA1("...")
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  $M = require('sha1')
                  ...
              - pattern-inside: |
                  $M = require('js-sha1')
                  ...
          - pattern: |
              $M("...")
      - patterns:
          - pattern-inside: |
              require('node-forge')
              ...
          - pattern-either:
              - patterns:
                  - pattern-inside: |
                      $Y = $X.md.sha1.create()
                      ...
                  - pattern: |
                      $Y.update("...")
              - pattern: |
                  $X.md.sha1.create().update("...")
      - patterns:
          - pattern-inside: |
              $X = require('jshashes')
              ...
              $Y = new $X.SHA1()
              ...
          - pattern: |
              $Y.$METHOD("...")
      - patterns:
          - pattern-inside: |
              require('hash.js')
              ...
          - pattern-either:
              - patterns:
                  - pattern-inside: |
                      $Y = $X.sha1()
                      ...
                  - pattern: |
                      $Y.update("...")
              - pattern: |
                  $X.sha1().update("...")
      - patterns:
          - pattern-inside: |
              $X = require('jssha')
              ...
          - pattern-either:
              - pattern: |
                  new $X('SHA-1', ...).update("...")
              - patterns:
                  - pattern-inside: |
                      $Y = new $X('SHA-1', ...)
                      ...
                  - pattern: |
                      $Y.update("...")
      - patterns:
          - pattern-either:
              - pattern: |
                  $X('sha1').update("...")
              - patterns:
                  - pattern-inside: |
                      $Y = $X('sha1')
                      ...
                  - pattern: |
                      $Y.update("...")
      - pattern: |
          $X.subtle.digest('SHA-1', ...)
      - patterns:
          - pattern-either:
              - pattern: |
                  $X.SHA1.digest(...)
              - patterns:
                  - pattern-inside: |
                      $Y = $X.SHA1
                      ...
                  - pattern: |
                      $Y.digest(...)  
      - patterns:
          - pattern-either:
              - pattern: |
                  $X.codec.hex.fromBits($SJCL)
              - patterns:
                  - pattern-inside: |
                      $Y = $SJCL
                      ...
                  - pattern: |
                      $X.codec.hex.fromBits($Y)
          - metavariable-pattern:
              metavariable: $SJCL
              pattern: |
                $K.hash.sha1.hash("...")
    message: |
      The SHA-1 hashing algorithm is no longer considered secure for
      cryptographic applications due to its vulnerability to collision attacks,
      where two different inputs produce the same output hash. SHA-1's
      susceptibility to collision attacks undermines the security of
      cryptographic operations, allowing attackers to forge signatures or
      manipulate data without detection. This poses significant risks in
      authentication systems, data integrity validations, and secure
      communications. 

      Remediation: To mitigate this vulnerability, replace the SHA1 hashing 
      algorithm with  stronger cryptographic hash functions, such as SHA-256 
      or SHA-3. These  algorithms offer significantly improved security and 
      are resistant to  collision attacks, making them suitable for cryptographic 
      purposes in  modern applications.

      Secure Code example:  
      ```  
      const crypto = require('crypto');  
      const hash = crypto.createHash('sha256').update('sensitive data').digest('hex');  
      console.log(hash);   
      ```
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      shortDescription: "Use of weak hash"
      category: "security"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-328"
      security-severity: "MEDIUM"
      related-packages: ['pkg:npm/crypto']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_crypto_rule-node-timing-attack"
    patterns:
      - pattern-not: "if ($Z == null) { ... };"
      - pattern-not: "if ($Z === null) { ... };"
      - pattern-not: "if ($Z != null) { ... };"
      - pattern-not: "if ($Z !== null) { ... };"
      - pattern-not: "if ($Q != undefined) { ... };"
      - pattern-not: "if ($Q !== undefined) { ... };"
      - pattern-not: "if ($Q == undefined) { ... };"
      - pattern-not: "if ($Q === undefined) { ... };"
      - pattern-not: "return $Y == null;"
      - pattern-not: "return $Y === null;"
      - pattern-not: "return $Y != null;"
      - pattern-not: "return $Y !== null;"
      - pattern-not: "return $Y == undefined;"
      - pattern-not: "return $Y === undefined;"
      - pattern-not: "return $Y != undefined;"
      - pattern-not: "return $Y !== undefined;"
      - pattern-either:
          - pattern: |
              if (password == $X) {
                  ...
              }
          - pattern: |
              if ($X == password) {
                  ...
              }
          - pattern: |
              if (password === $X) {
                  ...
              }
          - pattern: |
              if ($X === password) {
                  ...
              }
          - pattern: |
              if (pass == $X) {
                  ...
              }
          - pattern: |
              if ($X == pass) {
                  ...
              }
          - pattern: |
              if (pass === $X) {
                  ...
              }
          - pattern: |
              if ($X === pass) {
                  ...
              }
          - pattern: |
              if (secret == $X) {
                  ...
              }
          - pattern: |
              if ($X == secret) {
                  ...
              }
          - pattern: |
              if (secret === $X) {
                  ...
              }
          - pattern: |
              if ($X === secret) {
                  ...
              }
          - pattern: |
              if (api == $X) {
                  ...
              }
          - pattern: |
              if ($X == api) {
                  ...
              }
          - pattern: |
              if (api === $X) {
                  ...
              }
          - pattern: |
              if ($X === api) {
                  ...
              }
          - pattern: |
              if (apiKey == $X) {
                  ...
              }
          - pattern: |
              if ($X == apiKey) {
                  ...
              }
          - pattern: |
              if (apiKey === $X) {
                  ...
              }
          - pattern: |
              if ($X === apiKey) {
                  ...
              }
          - pattern: |
              if (apiSecret == $X) {
                  ...
              }
          - pattern: |
              if ($X == apiSecret) {
                  ...
              }
          - pattern: |
              if (apiSecret === $X) {
                  ...
              }
          - pattern: |
              if ($X === apiSecret) {
                  ...
              }
          - pattern: |
              if (token == $X) {
                  ...
              }
          - pattern: |
              if ($X == token) {
                  ...
              }
          - pattern: |
              if (token === $X) {
                  ...
              }
          - pattern: |
              if ($X === token) {
                  ...
              }
          - pattern: |
              if (hash == $X) {
                  ...
              }
          - pattern: |
              if ($X == hash) {
                  ...
              }
          - pattern: |
              if (hash === $X) {
                  ...
              }
          - pattern: |
              if ($X === hash) {
                  ...
              }
          - pattern: |
              if (auth_token == $X) {
                  ...
              }
          - pattern: |
              if ($X == auth_token) {
                  ...
              }
          - pattern: |
              if (auth_token === $X) {
                  ...
              }
          - pattern: |
              if ($X === auth_token) {
                  ...
              }
          - pattern: |
              if (password != $X) {
                  ...
              }
          - pattern: |
              if ($X != password) {
                  ...
              }
          - pattern: |
              if (password !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== password) {
                  ...
              }
          - pattern: |
              if (pass != $X) {
                  ...
              }
          - pattern: |
              if ($X != pass) {
                  ...
              }
          - pattern: |
              if (pass !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== pass) {
                  ...
              }
          - pattern: |
              if (secret != $X) {
                  ...
              }
          - pattern: |
              if ($X != secret) {
                  ...
              }
          - pattern: |
              if (secret !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== secret) {
                  ...
              }
          - pattern: |
              if (api != $X) {
                  ...
              }
          - pattern: |
              if ($X != api) {
                  ...
              }
          - pattern: |
              if (api !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== api) {
                  ...
              }
          - pattern: |
              if (apiKey != $X) {
                  ...
              }
          - pattern: |
              if ($X != apiKey) {
                  ...
              }
          - pattern: |
              if (apiKey !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== apiKey) {
                  ...
              }
          - pattern: |
              if (apiSecret != $X) {
                  ...
              }
          - pattern: |
              if ($X != apiSecret) {
                  ...
              }
          - pattern: |
              if (apiSecret !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== apiSecret) {
                  ...
              }
          - pattern: |
              if (token != $X) {
                  ...
              }
          - pattern: |
              if ($X != token) {
                  ...
              }
          - pattern: |
              if (token !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== token) {
                  ...
              }
          - pattern: |
              if (hash != $X) {
                  ...
              }
          - pattern: |
              if ($X != hash) {
                  ...
              }
          - pattern: |
              if (hash !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== hash) {
                  ...
              }
          - pattern: |
              if (auth_token != $X) {
                  ...
              }
          - pattern: |
              if ($X != auth_token) {
                  ...
              }
          - pattern: |
              if (auth_token !== $X) {
                  ...
              }
          - pattern: |
              if ($X !== auth_token) {
                  ...
              }
          - pattern: |
              return $X === auth_token;
          - pattern: |
              return auth_token === $X;
          - pattern: |
              return $X === token;
          - pattern: |
              return token === $X;
          - pattern: |
              return $X === hash;
          - pattern: |
              return hash === $X;
          - pattern: |
              return $X === password;
          - pattern: |
              return password === $X;
          - pattern: |
              return $X === pass;
          - pattern: |
              return pass === $X;
          - pattern: |
              return $X === apiKey;
          - pattern: |
              return apiKey === $X;
          - pattern: |
              return $X === apiSecret;
          - pattern: |
              return apiSecret === $X;
          - pattern: |
              return $X === api_key;
          - pattern: |
              return api_key === $X;
          - pattern: |
              return $X === api_secret;
          - pattern: |
              return api_secret === $X;
          - pattern: |
              return $X === secret;
          - pattern: |
              return secret === $X;
          - pattern: |
              return $X === api;
          - pattern: |
              return api === $X;
          - pattern: |
              return $X == auth_token;
          - pattern: |
              return auth_token == $X;
          - pattern: |
              return $X == token;
          - pattern: |
              return token == $X;
          - pattern: |
              return $X == hash;
          - pattern: |
              return hash == $X;
          - pattern: |
              return $X == password;
          - pattern: |
              return password == $X;
          - pattern: |
              return $X == pass;
          - pattern: |
              return pass == $X;
          - pattern: |
              return $X == apiKey;
          - pattern: |
              return apiKey == $X;
          - pattern: |
              return $X == apiSecret;
          - pattern: |
              return apiSecret == $X;
          - pattern: |
              return $X == api_key;
          - pattern: |
              return api_key == $X;
          - pattern: |
              return $X == api_secret;
          - pattern: |
              return api_secret == $X;
          - pattern: |
              return $X == secret;
          - pattern: |
              return secret == $X;
          - pattern: |
              return $X == api;
          - pattern: |
              return api == $X;
          - pattern: |
              return $X !== auth_token;
          - pattern: |
              return auth_token !== $X;
          - pattern: |
              return $X !== token;
          - pattern: |
              return token !== $X;
          - pattern: |
              return $X !== hash;
          - pattern: |
              return hash !== $X;
          - pattern: |
              return $X !== password;
          - pattern: |
              return password !== $X;
          - pattern: |
              return $X !== pass;
          - pattern: |
              return pass !== $X;
          - pattern: |
              return $X !== apiKey;
          - pattern: |
              return apiKey !== $X;
          - pattern: |
              return $X !== apiSecret;
          - pattern: |
              return apiSecret !== $X;
          - pattern: |
              return $X !== api_key;
          - pattern: |
              return api_key !== $X;
          - pattern: |
              return $X !== api_secret;
          - pattern: |
              return api_secret !== $X;
          - pattern: |
              return $X !== secret;
          - pattern: |
              return secret !== $X;
          - pattern: |
              return $X !== api;
          - pattern: |
              return api !== $X;
          - pattern: |
              return $X != auth_token;
          - pattern: |
              return auth_token != $X;
          - pattern: |
              return $X != token;
          - pattern: |
              return token != $X;
          - pattern: |
              return $X != hash;
          - pattern: |
              return hash != $X;
          - pattern: |
              return $X != password;
          - pattern: |
              return password != $X;
          - pattern: |
              return $X != pass;
          - pattern: |
              return pass != $X;
          - pattern: |
              return $X != apiKey;
          - pattern: |
              return apiKey != $X;
          - pattern: |
              return $X != apiSecret;
          - pattern: |
              return apiSecret != $X;
          - pattern: |
              return $X != api_key;
          - pattern: |
              return api_key != $X;
          - pattern: |
              return $X != api_secret;
          - pattern: |
              return api_secret != $X;
          - pattern: |
              return $X != secret;
          - pattern: |
              return secret != $X;
          - pattern: |
              return $X != api;
          - pattern: |
              return api != $X;
    message: |
      'String comparisons using ''==='', ''!=='', ''!='' and ''=='' is vulnerable to timing attacks. More info: https://snyk.io/blog/node-js-timing-attack-ccc-ctf/'
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      cwe: "CWE-208"
      shortDescription: "Observable timing discrepancy"
      category: "security"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: "MEDIUM"
      related-packages: ['pkg:npm/crypto']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_crypto_rule-node-tls-reject"
    patterns:
      - pattern-either:
          - pattern: |
              $X.env.NODE_TLS_REJECT_UNAUTHORIZED = $VAL
          - pattern: |
              $X.env['NODE_TLS_REJECT_UNAUTHORIZED']= $VAL
      - metavariable-pattern:
          metavariable: $VAL
          pattern-either:
            - pattern: |
                '0'
            - pattern: |
                0
    message: |
      The application sets NODE_TLS_REJECT_UNAUTHORIZED to '0', which instructs Node.js to disable TLS/SSL certificate validation. 
      This configuration allows the application to accept self-signed certificates or certificates from untrusted authorities, 
      undermining the TLS security model. Disabling TLS/SSL certificate validation compromises the integrity and confidentiality 
      of data in transit between the client and server. It makes the application vulnerable to man-in-the-middle (MITM) attacks, 
      where an attacker could intercept or alter the data being exchanged.

      Mitigation Strategy:
      Do not disable TLS/SSL certificate validation in production environments. Ensure that NODE_TLS_REJECT_UNAUTHORIZED is 
      set to '1' or is removed entirely from the production configuration (as the default configuration of validating SSL 
      certificate is safe).

      Secure Code Example:
      ```
      const https = require('https')
      process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '1'
      const req = https.request(options, res => {
        let data = ''
        res.on('data', chunk => {
          data += chunk
        })
        res.on('end', () => {
          console.log('Response Body:', data)
        })
      })
      req.end()
      ```
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-295"
      shortDescription: "Improper Certificate Validation"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/node-fetch']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_crypto_rule-node-weak-crypto"
    patterns:
      - pattern-either:
          - pattern: |
              $X.createCipher('des', ...)
    message: |
      A weak or broken cryptographic algorithm was identified. Using these functions will introduce vulnerabilities or downgrade the security of your application.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-327"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/crypto']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_database_rule-node-knex-sqli-injection"
    patterns:
      - pattern-either:
          - pattern-inside: |
              $KNEX = require('knex')
              ...
          - pattern-inside: |
              $KNEX = require('knex')(...)
              ...
      - pattern-either:
          - pattern: |
              $K.raw(<... $REQ.$QUERY.$VAR ...>, ...)
          - pattern: |
              $K.raw(<... $REQ.$QUERY ...>, ...)
          - pattern: |
              $SQL = <... $REQ.$QUERY.$VAR ...>;
              ...
              $K.raw(<... $SQL ...>, ...)
          - pattern: |
              $SQL = <... $REQ.$QUERY ...>;
              ...
              $K.raw(<... $SQL ...>, ...)
          - pattern: |
              $INP = <... $REQ.$QUERY.$VAR ...>;
              ...
              $SQL = <... $INP ...>;
              ...
              $K.raw(<... $SQL ...>, ...)
          - pattern: |
              $INP = <... $REQ.$QUERY ...>;
              ...
              $SQL = <... $INP ...>;
              ...
              $K.raw(<... $SQL ...>, ...)
          - pattern: |
              $K.whereRaw(<... $REQ.$QUERY.$VAR ...>, ...)
          - pattern: |
              $K.whereRaw(<... $REQ.$QUERY ...>, ...)
          - pattern: |
              $SQL = <... $REQ.$QUERY.$VAR ...>;
              ...
              $K.whereRaw(<... $SQL ...>, ...)
          - pattern: |
              $SQL = <... $REQ.$QUERY ...>;
              ...
              $K.whereRaw(<... $SQL ...>, ...)
          - pattern: |
              $INP = <... $REQ.$QUERY.$VAR ...>;
              ...
              $SQL = <... $INP ...>;
              ...
              $K.whereRaw(<... $SQL ...>, ...)
          - pattern: |
              $INP = <... $REQ.$QUERY ...>;
              ...
              $SQL = <... $INP ...>;
              ...
              $K.whereRaw(<... $SQL ...>, ...)
    message: |
      Untrusted input concatinated with raw SQL query using knex raw()  or whereRaw() functions can result in SQL Injection.
    languages:
      - "javascript"
    severity: "ERROR"
    metadata:
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-89"
      shortDescription: "Improper neutralization of special elements used in an SQL command (SQL Injection)"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/knex']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_database_rule-node-nosqli-injection"
    patterns:
      - pattern-not-inside: |
          $SANITIZE = require('mongo-sanitize')
          ...
          $SANITIZE(...)
          ...
      - pattern-not-inside: |
          import $SANITIZE from 'mongo-sanitize'
          ...
          $SANITIZE(...)
          ...
      - pattern-not: |
          $OBJ.findOne({$KEY : String(...).$FUNC()}, ...)
      - pattern-not: |
          $OBJ.findOne({$KEY : String(...).$FUNC}, ...)
      - pattern-not: |
          $OBJ.findOne({$KEY : String(...)}, ...)
      - pattern-either:
          - pattern: |
              $OBJ.findOne({$KEY : <... $REQ.$FOO.$BAR ...> }, ...)
          - pattern: |
              $OBJ.findOne({$KEY: <... $REQ.$FOO ...> }, ...)
          - pattern: |
              $INP = <... $REQ.$FOO.$BAR ...>;
              ...
              $OBJ.findOne({$KEY : <... $INP ...> }, ...)
          - pattern: |
              $INP = <... $REQ.$FOO ...>;
              ...
              $OBJ.findOne({$KEY: <... $INP ...> }, ...)
          - pattern: |
              $QUERY = {$KEY: <... $REQ.$FOO.$BAR ...>};
              ...
              $OBJ.findOne($QUERY, ...)
          - pattern: |
              $QUERY = {$KEY: <... $REQ.$FOO ...>};
              ...
              $OBJ.findOne($QUERY, ...)
          - pattern: |
              $INP = <... $REQ.$FOO.$BAR ...>;
              ...
              $QUERY = {$KEY : <... $INP ...> };
              ...
              $OBJ.findOne(<... $QUERY  ...>, ...)
          - pattern: |
              $INP = <... $REQ.$FOO ...>;
              ...
              $QUERY = {$KEY : <... $INP ...> };
              ...
              $OBJ.findOne(<... $QUERY  ...>, ...)
          - pattern: |
              $QUERY[$KEY] = <... $REQ.$FOO.$BAR ...>;
              ...
              $OBJ.findOne($QUERY, ...)
          - pattern: |
              $QUERY[$KEY] = <... $REQ.$FOO ...>;
              ...
              $OBJ.findOne($QUERY, ...)
          - pattern: |
              $INP = <... $REQ.$FOO.$BAR ...>;
              ...
              $QUERY[$KEY] = <... $INP ...>;
              ...
              $OBJ.findOne(<... $QUERY  ...>, ...)
          - pattern: |
              $INP = <... $REQ.$FOO ...>;
              ...
              $QUERY[$KEY] = <... $INP ...>;
              ...
              $OBJ.findOne(<... $QUERY  ...>, ...)
    message: |
      Untrusted user input in findOne() function can result in NoSQL Injection.
    languages:
      - "javascript"
    severity: "ERROR"
    metadata:
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-943"
      shortDescription: "Improper neutralization of special elements in data query logic"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/mongo-sanitize']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_database_rule-node-nosqli-js-injection"
    patterns:
      - pattern-either:
          - pattern: |
              $OBJ.$FUNC({$where: <... $REQ.$FOO.$BAR ...>}, ...)
          - pattern: |
              $OBJ.$FUNC({$where: <... $REQ.$QUERY ...>}, ...)
          - pattern: |
              $NSQL = <... $REQ.$QUERY.$...>;
              ...
              $OBJ.$FUNC({$where: <... $NSQL ...>}, ...)
          - pattern: |
              $NSQL = <... $REQ.$QUERY ...>;
              ...
              $OBJ.$FUNC({$where: <... $NSQL ...>}, ...)
          - pattern: |
              $INP = $REQ.$FOO.$BAR;
              ...
              $QRY = {$where: <... $INP ...>};
              ...
              $OBJ.$FUNC(<... $QRY ...>, ...)
          - pattern: |
              $INP = $REQ.$FOO;
              ...
              $QRY = {$where: <... $INP ...>};
              ...
              $OBJ.$FUNC(<... $QRY ...>, ...)
          - pattern: |
              $QRY["$where"] = <... $REQ.$FOO ...>;
              ...
              $OBJ.$FUNC(<... $QRY ...>, ...)
          - pattern: |
              $QRY["$where"] = <... $REQ.$FOO.$BAR ...>;
              ...
              $OBJ.$FUNC(<... $QRY ...>, ...)
          - pattern: |
              $INP = $REQ.$FOO;
              ...
              $QRY["$where"] = <... $INP ...>;
              ...
              $OBJ.$FUNC(<... $QRY ...>, ...)
          - pattern: |
              $INP = $REQ.$FOO.$BAR;
              ...
              $QRY["$where"] = <... $INP ...>;
              ...
              $OBJ.$FUNC(<... $QRY ...>, ...)
    message: |
      Untrusted user input in MongoDB $where operator can result in NoSQL JavaScript Injection.
    languages:
      - "javascript"
    severity: "ERROR"
    metadata:
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-943"
      shortDescription: "Improper neutralization of special elements in data query logic"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/mongo-sanitize']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_database_rule-node-sqli-injection"
    patterns:
      - pattern-inside: |
          require('$LIB')
          ...
      - metavariable-regex:
          metavariable: "$LIB"
          regex: "\\b(sql-client|mysql|pg|mssql|oracledb|sequelize)\\b"
      - pattern-not: |
          $CON.query("..." + "...", ...)
      - pattern-not: |
          $SQL = "...";
          ...
          $CON.query(<... $SQL ...>, ...)
      - pattern-either:
          - pattern: |
              $CON.query(<... $REQ.$QUERY ...>, ...)
          - pattern: |
              $SQL = <... $REQ.$QUERY ...>;
              ...
              $CON.query(<... $SQL ...>, ...)
          - pattern: |
              $INP = <... $REQ.$QUERY ...>;
              ...
              $SQL = <... $INP ...>;
              ...
              $CON.query(<... $SQL ...>, ...)
          - pattern: |
              $CON.query(`...${...}...`, ...)
          - pattern: |
              $CON.query("..."+...+"...", ...)
          - pattern: |
              $SQL = <... $INP ...>;
              ...
              $CON.query(<... $SQL ...>, ...)
    message: |
      Untrusted input concatinated with raw SQL query can result in SQL Injection.
    languages:
      - "javascript"
    severity: "ERROR"
    metadata:
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-89"
      shortDescription: "Improper neutralization of special elements used in an SQL command (SQL Injection)"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/mysql', 'pkg:npm/pg', 'pkg:npm/mssql', 'pkg:npm/oracledb', 'pkg:npm/sequelize']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_database_rule-sequelize-tls"
    message: |
      'The Sequelize connection string indicates that database server does not use TLS. Non TLS connections are susceptible to man in the middle (MITM) attacks.'
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-319"
      shortDescription: "Cleartext transmission of sensitive information"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/sequelize']
      license: GNU Lesser General Public License v3.0
    patterns:
      - pattern: |
          {
            host: $HOST,
            database: $DATABASE,
            dialect: $DIALECT
           }
      - pattern-not: |
          {
            host: $HOST,
            database: $DATABASE,
            dialect: "postgres",
            dialectOptions: {
                ssl: true
            }
          }
      - pattern-not: |
          {
            host: $HOST,
            database: $DATABASE,
            dialect: $DIALECT,
            dialectOptions: {
              ssl: { ... }
            }
          }
      - metavariable-regex:
          metavariable: "$DIALECT"
          regex: "['\"](mariadb|mysql|postgres|oracle)['\"]"
  - id: "rules_lgpl_javascript_database_rule-sequelize-tls-cert-validation"
    message: |
      The Sequelize connection string indicates that TLS certificate validation 
      of database server is disabled. This is equivalent to not having TLS. An 
      attacker can present any invalid certificate and Sequelize will make database 
      connection ignoring certificate errors. This setting make the connection
      susceptible to man in the middle (MITM) attacks. Not applicable to SQLite database.
    severity: "ERROR"
    languages:
      - "javascript"
    metadata:
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-295"
      shortDescription: "Improper certificate validation"
      security-severity: "HIGH"
      category: "security"
      related-packages: ['pkg:npm/sequelize']
      license: GNU Lesser General Public License v3.0
    patterns:
      - pattern: |
          {
            host: $HOST,
            database: $DATABASE,
            dialect: $DIALECT,
            dialectOptions: {
              ssl: {
                rejectUnauthorized: false
              }
            }
           }
      - metavariable-regex:
          metavariable: "$DIALECT"
          regex: "['\"](mariadb|mysql|postgres)['\"]"
  - id: "rules_lgpl_javascript_database_rule-sequelize-weak-tls"
    message: |
      'The Sequelize connection string indicates that an older version of TLS is in use. TLS1.0 and TLS1.1 are deprecated and should be used. By default, Sequelize use TLSv1.2 but it''s recommended to use TLS1.3. Not applicable to SQLite database.'
    metadata:
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-757"
      shortDescription: "Selection of Less-Secure Algorithm During Negotiation (Algorithm Downgrade)"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/sequelize']
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"
    languages:
      - "javascript"
    patterns:
      - pattern-inside: |
          {
            host: $HOST,
            database: $DATABASE,
            dialect: $DIALECT,
            dialectOptions:
              { ssl: ... }
           }
      - pattern-either:
          - pattern: |
              {
                minVersion: 'TLSv1'
              }
          - pattern: |
              {
                minVersion: 'TLSv1.1'
              }
      - metavariable-regex:
          metavariable: "$DIALECT"
          regex: "['\"](mariadb|mysql|postgres)['\"]"
  - id: "rules_lgpl_javascript_dos_rule-layer7-object-dos"
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-inside: function ($REQ, $RES, ...) {...}
          - pattern: $REQ.$FUNC. ...
          - metavariable-regex:
              metavariable: "$FUNC"
              regex: "^(body|params|query|baseUrl|cookies|hostname|subdomains|ip|ips|originalUrl|path)$"
    pattern-sinks:
      - patterns:
          - pattern: |
              for(...; $COND; ...){...}
          - focus-metavariable: $COND
          - metavariable-pattern:
              metavariable: $COND
              pattern-either:
                - pattern: |
                    Object.Keys($VAR).length
                - pattern: $VAR.length
      - patterns:
          - pattern-either:
              - pattern: $OBJ.forEach
              - pattern: $OBJ.map
              - pattern: Object.keys($OBJ).map
              - pattern: $OBJ.filter
              - pattern: $OBJ.reduce
              - pattern: $OBJ.reduceRight
          - focus-metavariable: $OBJ
    message: |
      This application is looping over user controlled objects, which can lead to a layer 7 denial of service vulnerability.

      A layer 7 denial of service attack refers to overloading the application layer of the OSI model, typically layer 7. 
      This can happen when user-controlled input such as objects, arrays, strings, etc. are iterated or looped over without proper validation or limits in place.

      For example, if a user can control the size of an array or object passed into the application, 
      they could create an extremely large input that gets looped over. This would consume excessive CPU cycles or memory, 
      potentially crashing or slowing down the application.

      To prevent this, limits should be set on the number of iterations, input sizes, recursion depth, etc.

      Sample case of secure array looped over with user-controlled input
      ```
      // Potential DoS if req.body.list.length is large.
      app.post('/dos/layer7-object-dos/for-loop/1', function (req, res) {
          var list = req.body.list;
          for (let i = 0; i <= 10; i++) {
              if(!list[i]){
                // return;
              }  
          }
          res.send("res")
      });
      ```

      Implementing protections against layer 7 denial of service attacks is important for securing modern web applications and APIs.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-606"
      shortDescription: "Unchecked input for loop condition"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/express']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_dos_rule-regex-dos"
    patterns:
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              $REGEX.test(<... $REQ ...>)
          - pattern: |
              $REGEX.test(<... $REQ.$QUERY ...>)
          - pattern: |
              $REGEX.test(<... $REQ.$BODY.$PARAM ...>)
          - pattern: |
              $INP = <... $REQ ...>;
              ...
              $REGEX.test(<... $INP ...>)
          - pattern: |
              $INP = <... $REQ.$QUERY ...>;
              ...
              $REGEX.test(<... $INP ...>)
          - pattern: |
              $INP = <... $REQ.$BODY.$PARAM ...>;
              ...
              $REGEX.test(<... $INP ...>)
          - pattern: |
              /.../g.exec(<... $REQ ...>)
          - pattern: |
              /.../g.exec(<... $REQ.$QUERY ...>)
          - pattern: |
              /.../.exec(<... $REQ.$BODY.$PARAM ...>)
          - pattern: |
              $INP = <... $REQ ...>;
              ...
              /.../.exec(<... $INP ...>)
          - pattern: |
              $INP = <... $REQ.$QUERY ...>;
              ...
              /.../.exec(<... $INP ...>)
          - pattern: |
              $INP = <... $REQ.$BODY.$PARAM ...>;
              ...
              /.../.exec(<... $INP ...>)
          - pattern: |
              $RE = /.../;
              ...
              $RE.exec(<... $REQ ...>)
          - pattern: |
              $RE = /.../;
              ...
              $RE.exec(<... $REQ.$QUERY ...>)
          - pattern: |
              $RE = /.../;
              ...
              $RE.exec(<... $REQ.$BODY.$PARAM ...>)
    message: |
      Ensure that the regex used to compare with user supplied input is safe from regular expression denial of service.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-185"
      shortDescription: "Incorrect regular expression"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/re2']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_electronjs_rule-electron-allow-http"
    patterns:
      - pattern-either:
          - pattern: |
              new BrowserWindow({webPreferences: {allowRunningInsecureContent: true}})
          - pattern: |
              var $X = {webPreferences: {allowRunningInsecureContent: true}};
    message: |
      Application can load content over HTTP and that makes the app vulnerable to Man in the middle attacks.
    languages:
      - "javascript"
    severity: "ERROR"
    metadata:
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-319"
      shortDescription: "Cleartext Transmission of Sensitive Information"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/electron']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_electronjs_rule-electron-blink-integration"
    patterns:
      - pattern-either:
          - pattern: |
              new BrowserWindow({webPreferences: {enableBlinkFeatures: '...'}})
          - pattern: |
              var $X = {webPreferences: {enableBlinkFeatures: '...'}};
    message: |
      Blink's expirimental features are enabled in this application. Some of the features may affect the security of the application.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-272"
      shortDescription: "Least privilege violation"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/electron']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_electronjs_rule-electron-context-isolation"
    patterns:
      - pattern-either:
          - pattern: |
              new BrowserWindow({webPreferences: {contextIsolation: false}})
          - pattern: |
              var $X = {webPreferences: {contextIsolation: false}};
    message: |
      Disabling context isolation can introduce Prototype Pollution vulnerabilities.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-1321"
      shortDescription: "Improperly controlled modification of object prototype attributes ('Prototype Pollution')"
      security-severity: "MEDIUM"
      related-packages: ['pkg:npm/electron']
      category: "security"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_electronjs_rule-electron-disable-websecurity"
    patterns:
      - pattern-either:
          - pattern: |
              new BrowserWindow({webPreferences: {webSecurity: false}})
          - pattern: |
              var $X = {webPreferences: {webSecurity: false}};
    message: |
      Disabling webSecurity will disable the same-origin policy and allows the execution of insecure code from any domain.
    languages:
      - "javascript"
    severity: "ERROR"
    metadata:
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-346"
      shortDescription: "Origin validation error"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/electron']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_electronjs_rule-electron-experimental-features"
    patterns:
      - pattern-either:
          - pattern: |
              new BrowserWindow({webPreferences: {experimentalFeatures: true}})
          - pattern: |
              var $X = {webPreferences: {experimentalFeatures: true}};
    message: |
      Experimental features are not expected to be in production ready applications.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-272"
      shortDescription: "Least privilege violation"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/electron']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_electronjs_rule-electron-nodejs-integration"
    patterns:
      - pattern-either:
          - pattern: |
              new BrowserWindow({webPreferences: {nodeIntegration: true}})
          - pattern: |
              var $X = {webPreferences: {nodeIntegration: true}};
    message: |
      Node integration exposes node.js APIs to the electron app and this can introduce remote code execution vulnerabilities to the application if the app is vulnerable to Cross Site Scripting (XSS).
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-272"
      shortDescription: "Least privilege violation"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/electron']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_eval_rule-eval-nodejs"
    patterns:
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              new Function(..., <... $REQ.$QUERY.$VAR ...>, ...)
          - pattern: |
              new Function(..., <... $REQ.$QUERY ...>, ...)
          - pattern: |
              eval(..., <... $REQ.$QUERY.$VAR ...>, ...)
          - pattern: |
              eval(..., <... $REQ.$QUERY ...>, ...)
          - pattern: |
              setTimeout(..., <... $REQ.$QUERY.$VAR ...>, ...)
          - pattern: |
              setTimeout(..., <... $REQ.$QUERY ...>, ...)
          - pattern: |
              setInterval(..., <... $REQ.$QUERY.$VAR ...>, ...)
          - pattern: |
              setInterval(..., <... $REQ.$QUERY ...>, ...)
          - pattern: |
              $INP = <... $REQ.$QUERY.$VAR ...>;
              ...
              new Function(..., <... $INP ...>, ...)
          - pattern: |
              $INP = <... $REQ.$QUERY ...>;
              ...
              new Function(..., <... $INP ...>, ...)
          - pattern: |
              $INP = <... $REQ.$QUERY.$VAR ...>;
              ...
              eval(..., <... $INP ...>, ...)
          - pattern: |
              $INP = <... $REQ.$QUERY ...>;
              ...
              eval(..., <... $INP ...>, ...)
          - pattern: |
              $INP = <... $REQ.$QUERY.$VAR ...>;
              ...
              setTimeout(..., <... $INP ...>, ...)
          - pattern: |
              $INP = <... $REQ.$QUERY ...>;
              ...
              setTimeout(..., <... $INP ...>, ...)
          - pattern: |
              $INP = <... $REQ.$QUERY.$VAR ...>;
              ...
              setInterval(..., <... $INP ...>, ...)
          - pattern: |
              $INP = <... $REQ.$QUERY ...>;
              ...
              setInterval(..., <... $INP ...>, ...)
    message: |
      User controlled data in eval() or similar functions may result in Server Side Injection or Remote Code Injection
    languages:
      - "javascript"
    severity: "ERROR"
    metadata:
      cwe: "CWE-95"
      shortDescription: "Improper neutralization of directives in dynamically evaluated code ('Eval Injection')"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "HIGH"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_eval_rule-eval-require"
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-inside: |
              function ($REQ, $RES, ...) {...}
          - focus-metavariable: $REQ
    pattern-sinks:
      - patterns:
          - pattern: |
              require($REQ, ...)
          - focus-metavariable: $REQ
    pattern-sanitizers:
      - patterns:
          - pattern-either:
              - pattern: |
                  if($VALIDATION){
                  ...
                  require($REQ, ...) 
                  ...
                  } 
              - pattern: |
                  $A = $VALIDATION
                  ...
                  if($A){
                  ...
                  require($REQ, ...)
                  ...
                  }
          - metavariable-pattern:
              metavariable: $VALIDATION
              pattern-either:
                - pattern: |
                    $AL.includes(...)  
                - pattern: |
                    $AL.indexOf(...) !== -1
                - pattern: |
                    $AL.find(...) !== undefined
                - pattern: |
                    $ALS.has(...)
    message: |
      Passing untrusted user input directly into the require() function without proper 
      validation or sanitization can possibly cause a vulnerability known as remote code execution (RCE). 
      An attacker could manipulate the input to load and execute arbitrary code from external sources, 
      potentially leading to severe security breaches such as data theft, system compromise, 
      or unauthorized access.
      To mitigate this risk, it's crucial to validate and sanitize user input
      thoroughly before passing it to functions like require(), ensuring that only trusted and safe inputs are utilized.

      Following is an example of secure validation against allowlist to prevent the vulnerability:
      ```
      // Define a list of explicitly allowed packages for require
      const allowedPkgs = [
          'package1',
          'package2',
          'package3'
      ];

      app.get("/eval/require/7", async (req, res) => {
          var isAllowed = allowedPkgs.includes(req.query.name);  
          if (isAllowed) {
              // ok: rules_lgpl_javascript_eval_rule-eval-require
              var cp = require(req.query.name);
              cp.exec('ls', (error, stdout, stderr) => {
                  console.log("exec output : \n", stdout)
              });        
          }
          res.send("Please check console logs.");
      });
      ```
    severity: "ERROR"
    languages:
      - "javascript"
    metadata:
      cwe: "CWE-706"
      shortDescription: "Use of incorrectly-resolved name or reference"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_eval_rule-grpc-insecure-connection"
    patterns:
      - pattern-either:
          - pattern-inside: |
              require('grpc')
              ...
          - pattern-inside: |
              import $MOD from 'grpc'
              ...
      - pattern: "$CREDENTIALS.createInsecure()"
    message: |
      Found an insecure gRPC connection. This creates a connection without encryption to a gRPC client/server. A malicious attacker could  tamper with the gRPC message, which could compromise the machine.
    metadata:
      owasp:
        - "A8:2017-Insecure Deserialization"
        - "A08:2021-Software and Data Integrity Failures"
      cwe: "CWE-502"
      shortDescription: "Deserialization of Untrusted Data"
      security-severity: "CRITICAL"
      category: "security"
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"
    languages:
      - "javascript"
      - "typescript"
  - id: "rules_lgpl_javascript_eval_rule-node-deserialize"
    patterns:
      - pattern-either:
          - pattern: |
              require('node-serialize').unserialize(...)
          - pattern-inside: |
              $MOD = require('node-serialize')
              ...
          - pattern-inside: |
              import $MOD from 'node-serialize'
              ...
      - pattern: |
          $MOD.unserialize(...)
    message: |
      User controlled data in 'unserialize()' or 'deserialize()' function can result in Object Injection or Remote Code Injection.
    languages:
      - "javascript"
      - "typescript"
    severity: "ERROR"
    metadata:
      owasp:
        - "A8:2017-Insecure Deserialization"
        - "A08:2021-Software and Data Integrity Failures"
      cwe: "CWE-502"
      shortDescription: "Deserialization of Untrusted Data"
      security-severity: "CRITICAL"
      category: "security"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_eval_rule-sandbox-code-injection"
    patterns:
      - pattern-inside: |
          require('sandbox')
          ...
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              $S.run(<... $REQ.$QUERY.$FOO ...>,...)
          - pattern: |
              $CODE = <... $REQ.$QUERY.$FOO ...>;
              ...
              $S.run(<... $CODE ...>,...)
          - pattern: |
              new $SANDBOX(...).run(<... $REQ.$QUERY.$FOO ...>,...)
          - pattern: |
              $CODE = <... $REQ.$QUERY.$FOO ...>;
              ...
              new $SANDBOX(...).run(<... $CODE ...>,...)
          - pattern: |
              $S.run(<... $REQ.$BODY ...>,...)
          - pattern: |
              $CODE = <... $REQ.$BODY ...>;
              ...
              $S.run(<... $CODE ...>,...)
          - pattern: |
              new $SANDBOX(...).run(<... $REQ.$BODY ...>,...)
          - pattern: |-
              $CODE = <... $REQ.$BODY ...>;
              ...
              new $SANDBOX(...).run(<... $CODE ...>,...)
    message: |
      Unrusted data in `sandbox` can result in code injection.
    severity: "ERROR"
    languages:
      - "javascript"
    metadata:
      cwe: "CWE-94"
      shortDescription: "Improper control of generation of code (Code Injection)"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_eval_rule-serializetojs-deserialize"
    patterns:
      - pattern-either:
          - pattern: |
              require('serialize-to-js').deserialize(...)
          - pattern-inside: |
              $MOD = require('serialize-to-js')
              ...
          - pattern-inside: |
              import $MOD from 'serialize-to-js'
              ...
      - pattern: |
          $MOD.deserialize(...)
    message: |
      User controlled data in 'unserialize()' or 'deserialize()' function can result in Object Injection or Remote Code Injection.
    languages:
      - "javascript"
      - "typescript"
    severity: "ERROR"
    metadata:
      owasp:
        - "A8:2017-Insecure Deserialization"
        - "A08:2021-Software and Data Integrity Failures"
      cwe: "CWE-502"
      shortDescription: "Deserialization of Untrusted Data"
      security-severity: "CRITICAL"
      category: "security"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_eval_rule-server-side-template-injection"
    patterns:
      - pattern-either:
          - pattern-inside: |
              require('handlebars')
              ...
          - pattern-inside: |
              require('pug')
              ...
          - pattern-inside: |
              require('hamljs')
              ...
          - pattern-inside: |
              require('ejs')
              ...
          - pattern-inside: |
              require('squirrelly')
              ...
          - pattern-inside: |
              require('eta')
              ...
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              $HB.compile(..., <... $REQ.$FOO ...>, ...)
          - pattern: |
              $HB.compile(..., <... $REQ.$FOO.$BAR ...>, ...)
          - pattern: |
              $X = <... $REQ.$FOO ...>;
              ...
              $HB.compile(..., <... $X ...>, ...)
          - pattern: |
              $X = <... $REQ.$FOO.$BAR ...>;
              ...
              $HB.compile(..., <... $X ...>, ...)
          - pattern: |
              $X = $SOURCE.replace('...', <... $REQ.$FOO ...>, ...)
              ...
              $HB.compile(..., <... $X ...>, ...)
          - pattern: |
              $X = $SOURCE.replace('...', <... $REQ.$FOO.$BAR ...>, ...)
              ...
              $HB.compile(..., <... $X ...>, ...)
          - pattern: |
              $HB.Compile(..., <... $REQ.$FOO ...>, ...)
          - pattern: |
              $HB.Compile(..., <... $REQ.$FOO.$BAR ...>, ...)
          - pattern: |
              $X = <... $REQ.$FOO ...>;
              ...
              $HB.Compile(..., <... $X ...>, ...)
          - pattern: |
              $X = <... $REQ.$FOO.$BAR ...>;
              ...
              $HB.Compile(..., <... $X ...>, ...)
          - pattern: |
              $X = $SOURCE.replace('...', <... $REQ.$FOO ...>, ...)
              ...
              $HB.Compile(..., <... $X ...>, ...)
          - pattern: |
              $X = $SOURCE.replace('...', <... $REQ.$FOO.$BAR ...>, ...)
              ...
              $HB.Compile(..., <... $X ...>, ...)
    message: |
      Untrusted user input in templating engine's compile() function can result in Remote Code Execution via server side template injection.
    languages:
      - "javascript"
    severity: "ERROR"
    metadata:
      cwe: "CWE-94"
      shortDescription: "Improper control of generation of code (Code Injection)"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_eval_rule-vm-code-injection"
    patterns:
      - pattern-inside: |
          $VM = require('vm')
          ...
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: "$VM.runInContext(<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$VM.runInContext(<... $REQ.$BODY ...>,...)"
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $VM.runInContext($INPUT,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $VM.runInContext($INPUT,...)
          - pattern: "$VM.runInNewContext(<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$VM.runInNewContext(<... $REQ.$BODY ...>,...)"
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $VM.runInNewContext($INPUT,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $VM.runInNewContext($INPUT,...)
          - pattern: "$VM.runInThisContext(<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$VM.runInThisContext(<... $REQ.$BODY ...>,...)"
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $VM.runInThisContext($INPUT,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $VM.runInThisContext($INPUT,...)
          - pattern: "$VM.compileFunction(<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$VM.compileFunction(<... $REQ.$BODY ...>,...)"
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $VM.compileFunction($INPUT,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $VM.compileFunction($INPUT,...)
          - pattern: "new $VM.Script(<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "new $VM.Script(<... $REQ.$BODY ...>,...)"
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              new $VM.Script($INPUT,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              new $VM.Script($INPUT,...)
    message: |
      Untrusted user input reaching `vm` can result in code injection.
    severity: "ERROR"
    languages:
      - "javascript"
    metadata:
      cwe: "CWE-94"
      shortDescription: "Improper control of generation of code (Code Injection)"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_eval_rule-vm-compilefunction-injection"
    patterns:
      - pattern-inside: |
          require('vm')
          ...
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $REQ.$QUERY.$FOO ...>},...)
          - pattern: |
              $CONTEXT = <... $REQ.$QUERY.$FOO ...>; ... $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $CONTEXT ...>},...)
          - pattern: |
              $CONTEXT = <... {$NAME:$REQ.$QUERY.$FOO} ...>; ... $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $CONTEXT ...>},...)
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$QUERY.$FOO ...>}; ... $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $CONTEXT ...>},...)
          - pattern: |
              $VAR = <... $REQ.$QUERY.$FOO ...>; ... $CONTEXT = {$NAME: <... $VAR ...>}; ... $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $CONTEXT ...>},...)
          - pattern: |
              $OPTS = {parsingContext: <... $REQ.$QUERY.$FOO ...>};
              ...
              $VM.compileFunction($CODE,$PARAMS,$OPTS,...)
          - pattern: |
              $CONTEXT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $OPTS = {parsingContext: <... $CONTEXT ...>};
              ...
              $VM.compileFunction($CODE,$PARAMS,$OPTS,...)
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$QUERY.$FOO ...>};
              ...
              $OPTS = {parsingContext: <... $CONTEXT ...>};
              ...
              $VM.compileFunction($CODE,$PARAMS,$OPTS,...)
          - pattern: |
              $VAR = <... $REQ.$QUERY.$FOO ...>;
              ...
              $CONTEXT = {$NAME: <... $VAR ...>};
              ...
              $OPTS = {parsingContext: <... $CONTEXT ...>};
              ...
              $VM.compileFunction($CODE,$PARAMS,$OPTS,...)
          - pattern: |
              $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $REQ.$BODY ...>},...)
          - pattern: |
              $CONTEXT = <... $REQ.$BODY ...>; ... $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $CONTEXT ...>},...)
          - pattern: |
              $CONTEXT = <... {$NAME:$REQ.$BODY} ...>; ... $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $CONTEXT ...>},...)
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$BODY ...>}; ... $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $CONTEXT ...>},...)
          - pattern: |
              $VAR = <... $REQ.$BODY ...>; ... $CONTEXT = {$NAME: <... $VAR ...>}; ... $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $CONTEXT ...>},...)
          - pattern: |
              $OPTS = {parsingContext: <... $REQ.$BODY ...>};
              ...
              $VM.compileFunction($CODE,$PARAMS,$OPTS,...)
          - pattern: |
              $CONTEXT = <... $REQ.$BODY ...>;
              ...
              $OPTS = {parsingContext: <... $CONTEXT ...>};
              ...
              $VM.compileFunction($CODE,$PARAMS,$OPTS,...)
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$BODY ...>};
              ...
              $OPTS = {parsingContext: <... $CONTEXT ...>};
              ...
              $VM.compileFunction($CODE,$PARAMS,$OPTS,...)
          - pattern: |
              $VAR = <... $REQ.$BODY ...>;
              ...
              $CONTEXT = {$NAME: <... $VAR ...>};
              ...
              $OPTS = {parsingContext: <... $CONTEXT ...>};
              ...
              $VM.compileFunction($CODE,$PARAMS,$OPTS,...)
    message: |
      Untrusted user input in `vm.compileFunction()` can result in code injection.
    severity: "ERROR"
    languages:
      - "javascript"
    metadata:
      cwe: "CWE-94"
      shortDescription: "Improper control of generation of code (Code Injection)"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_eval_rule-vm-runincontext-injection"
    patterns:
      - pattern-inside: |
          require('vm')
          ...
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              $CONTEXT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $VM.runInContext($CODE,<... $CONTEXT ...>,...)
          - pattern: |
              $CONTEXT = <... {$NAME:$REQ.$QUERY.$FOO} ...>;
              ...
              $VM.runInContext($CODE,<... $CONTEXT ...>,...)
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$QUERY.$FOO ...>};
              ...
              $VM.runInContext($CODE,<... $CONTEXT ...>,...)
          - pattern: |
              $VAR = <... $REQ.$QUERY.$FOO ...>;
              ...
              $CONTEXT = {$NAME: <... $VAR ...>};
              ...
              $VM.runInContext($CODE,<... $CONTEXT ...>,...)
          - pattern: |
              $CONTEXT = <... $REQ.$BODY ...>;
              ...
              $VM.runInContext($CODE,<... $CONTEXT ...>,...)
          - pattern: |
              $CONTEXT = <... {$NAME:$REQ.$BODY} ...>;
              ...
              $VM.runInContext($CODE,<... $CONTEXT ...>,...)
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$BODY ...>};
              ...
              $VM.runInContext($CODE,<... $CONTEXT ...>,...)
          - pattern: |
              $VAR = <... $REQ.$BODY ...>;
              ...
              $CONTEXT = {$NAME: <... $VAR ...>};
              ...
              $VM.runInContext($CODE,<... $CONTEXT ...>,...)
    message: |
      Untrusted user input in `vm.runInContext()` can result in code injection.
    severity: "ERROR"
    languages:
      - "javascript"
    metadata:
      cwe: "CWE-94"
      shortDescription: "Improper control of generation of code (Code Injection)"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_eval_rule-vm-runinnewcontext-injection"
    patterns:
      - pattern-inside: |
          require('vm')
          ...
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              $VM.runInNewContext($CODE,<... $REQ.$QUERY.$FOO ...>,...)
          - pattern: |
              $CONTEXT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $VM.runInNewContext($CODE,<... $CONTEXT ...>,...)
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$QUERY.$FOO ...>};
              ...
              $VM.runInNewContext($CODE,<... $CONTEXT ...>,...)
          - pattern: |
              $CONTEXT = <... {$NAME:$REQ.$QUERY.$FOO} ...>;
              ...
              $VM.runInNewContext($CODE,<... $CONTEXT ...>,...)
          - pattern: |
              $VAR = <... $REQ.$QUERY.$FOO ...>;
              ...
              $CONTEXT = {$NAME: <... $VAR ...>};
              ...
              $VM.runInNewContext($CODE,<... $CONTEXT ...>,...)
          - pattern: |
              $VM.runInNewContext($CODE,<... $REQ.$BODY ...>,...)
          - pattern: |
              $CONTEXT = <... $REQ.$BODY ...>;
              ...
              $VM.runInNewContext($CODE,<... $CONTEXT ...>,...)
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$BODY ...>};
              ...
              $VM.runInNewContext($CODE,<... $CONTEXT ...>,...)
          - pattern: |
              $CONTEXT = <... {$NAME:$REQ.$BODY} ...>;
              ...
              $VM.runInNewContext($CODE,<... $CONTEXT ...>,...)
          - pattern: |
              $VAR = <... $REQ.$BODY ...>;
              ...
              $CONTEXT = {$NAME: <... $VAR ...>};
              ...
              $VM.runInNewContext($CODE,<... $CONTEXT ...>,...)
    message: |
      Untrusted user input in `vm.runInNewContext()` can result in code injection.
    severity: "ERROR"
    languages:
      - "javascript"
    metadata:
      cwe: "CWE-94"
      shortDescription: "Improper control of generation of code (Code Injection)"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_eval_rule-vm2-code-injection"
    patterns:
      - pattern-inside: |
          require('vm2')
          ...
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              $VM.run(<... $REQ.$QUERY.$FOO ...>,...)
          - pattern: |
              $CODE = <... $REQ.$QUERY.$FOO ...>;
              ...
              $VM.run(<... $CODE ...>,...)
          - pattern: |
              new VM(...).run(<... $REQ.$QUERY.$FOO ...>,...)
          - pattern: |
              new NodeVM(...).run(<... $REQ.$QUERY.$FOO ...>,...)
          - pattern: |
              $CODE = <... $REQ.$QUERY.$FOO ...>;
              ...
              new NodeVM(...).run(<... $CODE ...>,...)
          - pattern: |
              $CODE = <... $REQ.$QUERY.$FOO ...>;
              ...
              new VMScript(<... $CODE ...>,...)
          - pattern: |
              $VM.run(<... $REQ.$BODY ...>,...)
          - pattern: |
              $CODE = <... $REQ.$BODY ...>;
              ...
              $VM.run(<... $CODE ...>,...)
          - pattern: |
              new VM(...).run(<... $REQ.$BODY ...>,...)
          - pattern: |
              $CODE = <... $REQ.$BODY ...>;
              ...
              new VM(...).run($CODE,...)
          - pattern: |
              new NodeVM(...).run(<... $REQ.$BODY ...>,...)
          - pattern: |
              $CODE = <... $REQ.$BODY ...>;
              ...
              new NodeVM(...).run(<... $CODE ...>,...)
          - pattern: |
              $CODE = <... $REQ.$BODY ...>;
              ...
              new VMScript(<... $CODE ...>,...)
    message: |
      Untrusted user input reaching `vm2` can result in code injection.
    severity: "WARNING"
    languages:
      - "javascript"
    metadata:
      cwe: "CWE-94"
      shortDescription: "Improper control of generation of code (Code Injection)"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_eval_rule-vm2-context-injection"
    patterns:
      - pattern-inside: |
          require('vm2')
          ...
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              new VM({sandbox: <... $REQ.$QUERY.$FOO ...>},...)
          - pattern: |
              $CONTEXT = <... $REQ.$QUERY.$FOO ...>;
              ...
              new VM({sandbox: <... $CONTEXT ...>},...)
          - pattern: |
              $CONTEXT = <... {$NAME:$REQ.$QUERY.$FOO} ...>;
              ...
              new VM({sandbox: <... $CONTEXT ...>},...)
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$QUERY.$FOO ...>};
              ...
              new VM({sandbox: <... $CONTEXT ...>},...)
          - pattern: |
              $VAR = <... $REQ.$QUERY.$FOO ...>;
              ...
              $CONTEXT = {$NAME: <... $VAR ...>};
              ...
              new VM({sandbox: <... $CONTEXT ...>},...)
          - pattern: |
              $OPTS = {sandbox: <... $REQ.$QUERY.$FOO ...>};
              ...
              new VM($OPTS,...)
          - pattern: |
              $CONTEXT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $OPTS = {sandbox: <... $CONTEXT ...>};
              ...
              new VM($OPTS,...)
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$QUERY.$FOO ...>};
              ...
              $OPTS = {sandbox: <... $CONTEXT ...>};
              ...
              new VM($OPTS,...)
          - pattern: |
              $VAR = <... $REQ.$QUERY.$FOO ...>;
              ...
              $CONTEXT = {$NAME: <... $VAR ...>};
              ...
              $OPTS = {sandbox: <... $CONTEXT ...>};
              ...
              new VM($OPTS,...)
          - pattern: |
              new NodeVM({sandbox: <... $REQ.$QUERY.$FOO ...>},...)
          - pattern: |
              $CONTEXT = <... $REQ.$QUERY.$FOO ...>;
              ...
              new NodeVM({sandbox: <... $CONTEXT ...>},...)
          - pattern: |
              $CONTEXT = <... {$NAME:$REQ.$QUERY.$FOO} ...>;
              ...
              new NodeVM({sandbox: <... $CONTEXT ...>},...)
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$QUERY.$FOO ...>};
              ...
              new NodeVM({sandbox: <... $CONTEXT ...>},...)
          - pattern: |
              $VAR = <... $REQ.$QUERY.$FOO ...>;
              ...
              $CONTEXT = {$NAME: <... $VAR ...>};
              ...
              new NodeVM({sandbox: <... $CONTEXT ...>},...)
          - pattern: |
              $OPTS = {sandbox: <... $REQ.$QUERY.$FOO ...>};
              ...
              new NodeVM($OPTS,...)
          - pattern: |
              $CONTEXT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $OPTS = {sandbox: <... $CONTEXT ...>};
              ...
              new NodeVM($OPTS,...)
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$QUERY.$FOO ...>};
              ...
              $OPTS = {sandbox: <... $CONTEXT ...>};
              ...
              new NodeVM($OPTS,...)
          - pattern: |
              $VAR = <... $REQ.$QUERY.$FOO ...>;
              ...
              $CONTEXT = {$NAME: <... $VAR ...>};
              ...
              $OPTS = {sandbox: <... $CONTEXT ...>};
              ...
              new NodeVM($OPTS,...)
          - pattern: |
              new VM({sandbox: <... $REQ.$BODY ...>},...)
          - pattern: |
              $CONTEXT = <... $REQ.$BODY ...>;
              ...
              new VM({sandbox: <... $CONTEXT ...>},...)
          - pattern: |
              $CONTEXT = <... {$NAME:$REQ.$BODY} ...>;
              ...
              new VM({sandbox: <... $CONTEXT ...>},...)
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$BODY ...>};
              ...
              new VM({sandbox: <... $CONTEXT ...>},...)
          - pattern: |
              $VAR = <... $REQ.$BODY ...>;
              ...
              $CONTEXT = {$NAME: <... $VAR ...>};
              ...
              new VM({sandbox: <... $CONTEXT ...>},...)
          - pattern: |
              $OPTS = {sandbox: <... $REQ.$BODY ...>};
              ...
              new VM($OPTS,...)
          - pattern: |
              $CONTEXT = <... $REQ.$BODY ...>;
              ...
              $OPTS = {sandbox: <... $CONTEXT ...>};
              ...
              new VM($OPTS,...)
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$BODY ...>};
              ...
              $OPTS = {sandbox: <... $CONTEXT ...>};
              ...
              new VM($OPTS,...)
          - pattern: |
              $VAR = <... $REQ.$BODY ...>;
              ...
              $CONTEXT = {$NAME: <... $VAR ...>};
              ...
              $OPTS = {sandbox: <... $CONTEXT ...>};
              ...
              new VM($OPTS,...)
          - pattern: |
              new NodeVM({sandbox: <... $REQ.$BODY ...>},...)
          - pattern: |
              $CONTEXT = <... $REQ.$BODY ...>;
              ...
              new NodeVM({sandbox: <... $CONTEXT ...>},...)
          - pattern: |
              $CONTEXT = <... {$NAME:$REQ.$BODY} ...>;
              ...
              new NodeVM({sandbox: <... $CONTEXT ...>},...)
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$BODY ...>};
              ...
              new NodeVM({sandbox: <... $CONTEXT ...>},...)
          - pattern: |
              $VAR = <... $REQ.$BODY ...>;
              ...
              $CONTEXT = {$NAME: <... $VAR ...>};
              ...
              new NodeVM({sandbox: <... $CONTEXT ...>},...)
          - pattern: |
              $OPTS = {sandbox: <... $REQ.$BODY ...>};
              ...
              new NodeVM($OPTS,...)
          - pattern: |
              $CONTEXT = <... $REQ.$BODY ...>;
              ...
              $OPTS = {sandbox: <... $CONTEXT ...>};
              ...
              new NodeVM($OPTS,...)
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$BODY ...>};
              ...
              $OPTS = {sandbox: <... $CONTEXT ...>};
              ...
              new NodeVM($OPTS,...)
          - pattern: |-
              $VAR = <... $REQ.$BODY ...>;
              ...
              $CONTEXT = {$NAME: <... $VAR ...>};
              ...
              $OPTS = {sandbox: <... $CONTEXT ...>};
              ...
              new NodeVM($OPTS,...)
    message: |
      Untrusted user input reaching `vm2` sandbox can result in context injection.
    severity: "ERROR"
    languages:
      - "javascript"
    metadata:
      cwe: "CWE-94"
      shortDescription: "Improper control of generation of code (Code Injection)"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_eval_rule-yaml-deserialize"
    patterns:
      - pattern-either:
          - pattern: |
              require('js-yaml').load(...)
          - pattern-inside: |
              $MOD = require('js-yaml')
              ...
          - pattern-inside: |
              import $MOD from 'js-yaml'
              ...
      - pattern: |
          $MOD.load(...)
    message: |
      User controlled data in 'yaml.load()' function can result in Remote Code Injection.
    languages:
      - "javascript"
      - "typescript"
    severity: "ERROR"
    metadata:
      owasp:
        - "A8:2017-Insecure Deserialization"
        - "A08:2021-Software and Data Integrity Failures"
      cwe: "CWE-502"
      shortDescription: "Deserialization of Untrusted Data"
      security-severity: "CRITICAL"
      category: "security"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_exec_rule-shelljs-os-command-exec"
    patterns:
      - pattern-inside: |
          require('shelljs')
          ...
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              $EXEC.exec(<... $REQ.$QUERY.$VAR ...>, ...)
          - pattern: |
              $EXEC.exec( <... $REQ.$QUERY ...>, ...)
          - pattern: |
              $INP = <... $REQ.$QUERY.$VAR ...>;
              ...
              $EXEC.exec(<... $INP ...>, ...)
          - pattern: |
              $INP = <... $REQ.$QUERY ...>;
              ...
              $EXEC.exec(<... $INP ...>, ...)
    message: |
      User controlled data in 'shelljs.exec()' can result in Remote OS Command Execution.
    languages:
      - "javascript"
    severity: "ERROR"
    metadata:
      shortDescription: "Improper neutralization of special elements used in an OS command ('OS Command Injection')"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-78"
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_headers_rule-cookie-session-default"
    patterns:
      - pattern-either:
          - pattern-inside: |
              $SESSION = require('cookie-session')
              ...
          - pattern-inside: |
              $SESSION = require('express-session')
              ...
      - pattern: "$SESSION(...)"
      - pattern-not-inside: |-
          $SESSION(<... {name:...} ...>,...)
      - pattern-not-inside: |
          $OPTS = <... {name:...} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $OPTS.name = ...;
          ...
          $SESSION($OPTS,...)
    message: |
      Consider changing the default session cookie name. An attacker can use it to fingerprint the server and target attacks accordingly.
    severity: "INFO"
    languages:
      - "javascript"
    metadata:
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      cwe: "CWE-522"
      shortDescription: "Insufficiently protected credentials"
      security-severity: "INFO"
      category: "security"
      related-packages: ['pkg:npm/cookie-session', 'pkg:npm/express-session']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_headers_rule-cookie-session-no-domain"
    patterns:
      - pattern-either:
          - pattern-inside: |
              $SESSION = require('cookie-session')
              ...
          - pattern-inside: |
              $SESSION = require('express-session')
              ...
      - pattern: "$SESSION(...)"
      - pattern-not-inside: |-
          $SESSION(<... {cookie:{domain:...}} ...>,...)
      - pattern-not-inside: |
          $OPTS = <... {cookie:{domain:...}} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $COOKIE = <... {domain:...} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $OPTS.cookie = <... {domain:...} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $COOKIE.domain = ...;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $OPTS.cookie.domain = ...;
          ...
          $SESSION($OPTS,...)
    message: |
      'Default session middleware settings: `domain` not set. It indicates the domain of the cookie; use it to compare against the domain of the server in which the URL is being requested. If they match, then check the path attribute next.'
    severity: "INFO"
    languages:
      - "javascript"
    metadata:
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      cwe: "CWE-522"
      shortDescription: "Insufficiently protected credentials"
      security-severity: "INFO"
      category: "security"
      related-packages: ['pkg:npm/cookie-session', 'pkg:npm/express-session']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_headers_rule-cookie-session-no-httponly"
    patterns:
      - pattern-either:
          - pattern-inside: |
              $SESSION = require('cookie-session')
              ...
          - pattern-inside: |
              $SESSION = require('express-session')
              ...
      - pattern-either:
          - pattern-inside: |-
              $SESSION(<... {cookie:{httpOnly:false}} ...>,...)
          - pattern-inside: |
              $OPTS = <... {cookie:{httpOnly:false}} ...>;
              ...
              $SESSION($OPTS,...)
          - pattern-inside: |
              $OPTS = ...;
              ...
              $COOKIE = <... {httpOnly:false} ...>;
              ...
              $SESSION($OPTS,...)
          - pattern-inside: |
              $OPTS = ...;
              ...
              $OPTS.cookie = <... {httpOnly:false} ...>;
              ...
              $SESSION($OPTS,...)
          - pattern-inside: |
              $OPTS = ...;
              ...
              $COOKIE.httpOnly = false;
              ...
              $SESSION($OPTS,...)
          - pattern-inside: |
              $OPTS = ...;
              ...
              $OPTS.cookie.httpOnly = false;
              ...
              $SESSION($OPTS,...)
    message: |
      'Session middleware settings: `httpOnly` is explicitly set to false.  It ensures that sensitive cookies cannot be accessed by client side  JavaScript and helps to protect against cross-site scripting attacks.'
    severity: "WARNING"
    languages:
      - "javascript"
    metadata:
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      cwe: "CWE-1004"
      shortDescription: "Sensitive cookie without 'HttpOnly' flag"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/cookie-session', 'pkg:npm/express-session']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_headers_rule-cookie-session-no-maxage"
    patterns:
      - pattern-either:
          - pattern-inside: |
              $SESSION = require('cookie-session')
              ...
          - pattern-inside: |
              $SESSION = require('express-session')
              ...
      - pattern: "$SESSION(...)"
      - pattern-not-inside: |-
          $SESSION(<... {cookie:{maxAge:...}} ...>,...)
      - pattern-not-inside: |
          $OPTS = <... {cookie:{maxAge:...}} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $COOKIE = <... {maxAge:...} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $OPTS.cookie = <... {maxAge:...} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $COOKIE.maxAge = ...;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |-
          $OPTS = ...;
          ...
          $OPTS.cookie.maxAge = ...;
          ...
          $SESSION($OPTS,...)
    message: |
      'Session middleware settings: `maxAge` not set. Use it to set expiration date for cookies.'
    severity: "INFO"
    languages:
      - "javascript"
    metadata:
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      cwe: "CWE-613"
      shortDescription: "Insufficient session expiration"
      security-severity: "INFO"
      related-packages: ['pkg:npm/cookie-session', 'pkg:npm/express-session']
      category: "security"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_headers_rule-cookie-session-no-path"
    patterns:
      - pattern-either:
          - pattern-inside: |
              $SESSION = require('cookie-session')
              ...
          - pattern-inside: |
              $SESSION = require('express-session')
              ...
      - pattern: "$SESSION(...)"
      - pattern-not-inside: |-
          $SESSION(<... {cookie:{path:...}} ...>,...)
      - pattern-not-inside: |
          $OPTS = <... {cookie:{path:...}} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $COOKIE = <... {path:...} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $OPTS.cookie = <... {path:...} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $COOKIE.path = ...;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $OPTS.cookie.path = ...;
          ...
          $SESSION($OPTS,...)
    message: |
      'Default session middleware settings: `path` not set. It indicates the path of the cookie; use it to compare against the request path. If this and domain match, then send the cookie in the request.'
    severity: "INFO"
    languages:
      - "javascript"
    metadata:
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      cwe: "CWE-522"
      shortDescription: "Insufficiently protected credentials"
      security-severity: "INFO"
      category: "security"
      related-packages: ['pkg:npm/cookie-session', 'pkg:npm/express-session']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_headers_rule-cookie-session-no-samesite"
    patterns:
      - pattern-either:
          - pattern-inside: |
              $SESSION = require('cookie-session')
              ...
          - pattern-inside: |
              $SESSION = require('express-session')
              ...
      - pattern: "$SESSION(...)"
      - pattern-not-inside: |-
          $SESSION(<... {cookie:{sameSite:true}} ...>,...)
      - pattern-not-inside: |-
          $SESSION(<... {cookie:{sameSite:'lax'}} ...>,...)
      - pattern-not-inside: |-
          $SESSION(<... {cookie:{sameSite:'strict'}} ...>,...)
      - pattern-not-inside: |
          $OPTS = <... {cookie:{sameSite:true}} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $COOKIE = <... {sameSite:true} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $OPTS.cookie = <... {sameSite:true} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $COOKIE.sameSite = true;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $OPTS.cookie.sameSite = true;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = <... {cookie:{sameSite:'strict'}} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $COOKIE = <... {sameSite:'strict'} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $OPTS.cookie = <... {sameSite:'strict'} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $COOKIE.sameSite = 'strict';
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $OPTS.cookie.sameSite = 'strict';
          ...
          $SESSION($OPTS,...)
    message: |
      'Default session middleware settings: `sameSite` attribute is not configured to strict or lax. These configurations provides protection against Cross Site Request Forgery attacks.'
    severity: "WARNING"
    languages:
      - "javascript"
    metadata:
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      cwe: "CWE-1275"
      shortDescription: "Sensitive cookie with improper SameSite attribute"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/cookie-session', 'pkg:npm/express-session']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_headers_rule-cookie-session-no-secure"
    patterns:
      - pattern-either:
          - pattern-inside: |
              $SESSION = require('cookie-session')
              ...
          - pattern-inside: |
              $SESSION = require('express-session')
              ...
      - pattern: "$SESSION(...)"
      - pattern-not-inside: |-
          $SESSION(<... {cookie:{secure:true}} ...>,...)
      - pattern-not-inside: |
          $OPTS = <... {cookie:{secure:true}} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $COOKIE = <... {secure:true} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $OPTS.cookie = <... {secure:true} ...>;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $COOKIE.secure = true;
          ...
          $SESSION($OPTS,...)
      - pattern-not-inside: |
          $OPTS = ...;
          ...
          $OPTS.cookie.secure = true;
          ...
          $SESSION($OPTS,...)
    message: |
      'Default session middleware settings: `secure` not set. It ensures the browser only sends the cookie over HTTPS.'
    severity: "WARNING"
    languages:
      - "javascript"
    metadata:
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      cwe: "CWE-614"
      shortDescription: "Sensitive cookie in HTTPS session without 'Secure' attribute"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/cookie-session', 'pkg:npm/express-session']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_headers_rule-express-cors"
    patterns:
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              $APP.options('*', cors(...))
          - pattern: |
              $RES.set("=~/access-control-allow-origin/i", '*', ...)
          - pattern: |
              $RES.set(..., { "=~/access-control-allow-origin/i" : '*' }, ...)
          - pattern: |
              $RES.header("=~/access-control-allow-origin/i", '*', ...)
          - pattern: |
              $RES.writeHead(..., {"=~/access-control-allow-origin/i": '*' }, ...)
    message: |
      Access-Control-Allow-Origin response header is set to "*". This will disable CORS Same Origin Policy restrictions.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-346"
      shortDescription: "Origin validation error"
      security-severity: "MEDIUM"
      category: "security"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_headers_rule-generic-cors"
    patterns:
      - pattern: |
          $APP.options('*', cors(...))
    message: |
      Access-Control-Allow-Origin response header is set to "*". This will disable CORS Same Origin Policy restrictions.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-346"
      shortDescription: "Origin validation error"
      security-severity: "MEDIUM"
      category: "security"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_headers_rule-generic-header-injection"
    patterns:
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              $INP = $REQ.$QUERY;
              ...
              $RES.set(..., <... $INP ...>, ...)
          - pattern: |
              $INP = $REQ.$QUERY.$VAR;
              ...
              $RES.set(..., <... $INP ...>, ...)
          - pattern: |
              $INP = $REQ.$VAR;
              ...
              $RES.set(..., { $X: <... $INP ...>}, ...)
          - pattern: |
              $INP = $REQ.$QUERY.$FOO;
              ...
              $RES.set(..., { $X: <... $INP ...>}, ...)
          - pattern: |
              $INP = $REQ.$VAR;
              ...
              $RES.writeHead(..., { $X: <... $INP ...> }, ...)
          - pattern: |
              $INP = $REQ.$QUERY.$FOO;
              ...
              $RES.writeHead(..., { $X: <... $INP ...> }, ...)
          - pattern: |
              $RES.set(..., <... $REQ.$QUERY ...>, ...)
          - pattern: |
              $RES.set(..., <... $REQ.$QUERY.$VAR ...>, ...)
          - pattern: |
              $RES.set(..., { $X: <... $REQ.$VAR ...>}, ...)
          - pattern: |
              $RES.set(..., { $X: <... $REQ.$QUERY.$FOO ...>}, ...)
          - pattern: |
              $RES.writeHead(..., { $X: <... $REQ.$VAR ...> }, ...)
          - pattern: |
              $RES.writeHead(..., { $X: <... $REQ.$QUERY.$FOO ...> }, ...)
    message: |
      Untrusted user input in response header will result in HTTP Header Injection or Response Splitting Attacks.
    languages:
      - "javascript"
    severity: "ERROR"
    metadata:
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-644"
      shortDescription: "Improper neutralization of HTTP headers for scripting syntax"
      security-severity: "CRITICAL"
      category: "security"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_headers_rule-header-xss-generic"
    patterns:
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              $RES.header("=~/x-xss-protection/i", 0, ...)
          - pattern: |
              $RES.set("=~/x-xss-protection/i", 0, ...)
          - pattern: |
              $RES.set(..., { "=~/x-xss-protection/i" : 0 }, ...)
          - pattern: |
              $RES.writeHead(..., {"=~/x-xss-protection/i": 0 }, ...)
    message: |
      X-XSS-Protection header is set to 0. This will disable the browser's XSS Filter.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-358"
      shortDescription: "Improperly implemented security check for standard"
      security-severity: "MEDIUM"
      category: "security"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_headers_rule-header-xss-lusca"
    patterns:
      - pattern-inside: |
          $X = require('lusca')
          ...
      - pattern-not: |
          $X.use(helmet())
      - pattern-either:
          - pattern: |
              $X.xssProtection(false)
          - pattern: |
              $X({ xssProtection: false})
    message: |
      X-XSS-Protection header is set to 0. This will disable the browser's XSS Filter.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-358"
      shortDescription: "Improperly implemented security check for standard"
      security-severity: "MEDIUM"
      category: "security"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_headers_rule-helmet-feature-disabled"
    patterns:
      - pattern-either:
          - pattern: |
              $HELMET(..., {frameguard: false}, ...)
          - pattern: |
              $HELMET(..., {contentSecurityPolicy: false}, ...)
          - pattern: |
              $HELMET(..., {permittedCrossDomainPolicies: false}, ...)
          - pattern: |
              $HELMET(..., {dnsPrefetchControl: false}, ...)
          - pattern: |
              $HELMET(..., {expectCt: false}, ...)
          - pattern: |
              $HELMET(..., {featurePolicy: false}, ...)
          - pattern: |
              $HELMET(..., {hsts: false}, ...)
          - pattern: |
              $HELMET(..., {ieNoOpen: false}, ...)
          - pattern: |
              $HELMET(..., {noSniff: false}, ...)
          - pattern: |
              $HELMET(..., {hidePoweredBy: false}, ...)
          - pattern: |
              $HELMET(..., {referrerPolicy: false}, ...)
          - pattern: |
              $HELMET(..., {xssFilter: false}, ...)
    message: |
      One or more Security Response header is explicitly disabled in Helmet.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-358"
      shortDescription: "Improperly implemented security check for standard"
      security-severity: "MEDIUM"
      category: "security"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_headers_rule-host-header-injection"
    patterns:
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              $X = <... "=~/.*http[s]*:///i" + $REQ.host ...>;
          - pattern: |
              $X = <... "=~/.*http[s]*:///i" + $REQ["host"] ...>;
          - pattern: |
              $X = <... "=~/.*http[s]*:///i" + $REQ("host") ...>;
          - pattern: |
              $X = { $Y: <... "=~/.*http[s]*:///i" + $REQ.host ...>};
          - pattern: |
              $X = { $Y: <... "=~/.*http[s]*:///i" + $REQ["host"] ...>};
          - pattern: |
              $X = { $Y: <... "=~/.*http[s]*:///i" + $REQ("host") ...>};
          - pattern: |
              $Z = $REQ.host;
              ...
              $X = <... "=~/.*http[s]*:///i" + $Z ...>;
          - pattern: |
              $Z = $REQ["host"];
              ...
              $X = <... "=~/.*http[s]*:///i" + $Z ...>;
          - pattern: |
              $Z = $REQ("host")
              ...
              $X = <... "=~/.*http[s]*:///i" + $Z ...>;
          - pattern: |
              $Z = $REQ.host;
              ...
              $X = { $Y: <... "=~/.*http[s]*:///i" + $REQ.host ...>};
          - pattern: |
              $Z = $REQ["host"];
              ...
              $X = { $Y: <... "=~/.*http[s]*:///i" + $Z ...>};
          - pattern: |
              $Z = $REQ("host")
              ...
              $X = { $Y: <... "=~/.*http[s]*:///i" + $REQ("host") ...>};
    message: |
      Using untrusted Host header for generating dynamic URLs can result in web cache and or password reset poisoning.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-348"
      shortDescription: "Use of less trusted source"
      security-severity: "MEDIUM"
      category: "security"
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_jwt_rule-hardcoded-jwt-secret"
    patterns:
      - pattern-either:
          - pattern-inside: |
              const $JWT = require("jsonwebtoken");
              ...
          - pattern-inside: |
              const $JOSE = require("jose");
              ...
          - pattern-inside: |
              import $JWT from "jsonwebtoken"
              ...
          - pattern-inside: |
              import $JOSE from "jose"
              ...
      - pattern-either:
          - pattern: $JWT.sign($PAYLOAD, "...", ...)
          - pattern: $JWT.verify($PAYLOAD, "...", ...)
          - pattern: new $JOSE.SignJWT(...). ... .sign("...")
          - pattern: |
              var $TOKEN = new $JOSE.SignJWT(...). ...
              ...
              $TOKEN. ... .sign("...")
          - pattern: $JOSE.jwtVerify( $TKN, "...", ... )
          - patterns:
              - pattern-either:
                  - patterns:
                      - pattern-inside: |
                          var $KEY = $X
                            ...
                      - pattern-either:
                          - pattern: new $JOSE.SignJWT(...). ... .sign($KEY)
                          - patterns:
                              - pattern: |
                                  var $TOKEN = new $JOSE.SignJWT(...). ...
                                  ...
                                  $TOKEN. ... .sign($KEY)
                              - pattern: $TOKEN. ... .sign($KEY)
                          - pattern: $JOSE.jwtVerify( $TKN, $KEY, ... )
                  - pattern: new $JOSE.SignJWT(...). ... .sign($X)
                  - patterns:
                      - pattern: |
                          var $TOKEN = new $JOSE.SignJWT(...). ...
                          ...
                          $TOKEN. ... .sign($X)
                      - pattern: $TOKEN. ... .sign($X)
                  - pattern: $JOSE.jwtVerify( $TKN, $X, ... )
              - metavariable-pattern:
                  metavariable: $X
                  pattern-either:
                    - pattern: new TextEncoder().encode( "...",... )
                    - pattern: Uint8Array.from("...", ...)
    message: |
      Hardcoded JWT secret or private key was found. Hardcoding secrets like JWT signing keys poses a significant security risk. 
      If the source code ends up in a public repository or is compromised, the secret is exposed. Attackers could then use the secret to 
      generate forged tokens and access the system. Store it properly in an environment variable.

      Here are some recommended safe ways to access JWT secrets:
        - Use environment variables to store the secret and access it in code instead of hardcoding. This keeps it out of source control.
        - Use a secrets management service to securely store and tightly control access to the secret. Applications can request the secret at runtime.
        - For local development, use a .env file that is gitignored and access the secret from process.env.

      sample code snippet of accessing JWT secret from env variables
      ```
       const token = jwt.sign(payload, process.env.SECRET, { algorithm: 'HS256' });
      ```
    languages:
      - "javascript"
    severity: "ERROR"
    metadata:
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-798"
      shortDescription: "Use of hard-coded credentials"
      security-severity: "HIGH"
      category: "security"
      related-packages: ['pkg:npm/jsonwebtoken', 'pkg:npm/jose']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_jwt_rule-jwt-exposed-credentials"
    patterns:
      - pattern-either:
          - pattern-inside: |
              const $JWT = require("jsonwebtoken");
              ...
          - pattern-inside: |
              const $JOSE = require("jose");
              ...
          - pattern-inside: |
              import $JWT from "jsonwebtoken"
              ...
          - pattern-inside: |
              import $JOSE from "jose"
              ...
      - pattern-either:
          - pattern: "$JWT.sign(<... {$PASSWORD: $VALUE} ...>, ...)"
          - patterns:
              - pattern: |
                  $OBJ = <... {$PASSWORD: $VALUE} ...>
                  ...
                  $TOKEN = $JWT.sign(<... $OBJ ...>, ...)
              - pattern: $TOKEN = $JWT.sign(<... $OBJ ...>, ...)
          - patterns:
              - pattern: |
                  $OBJ. ... .$PASSWORD = ...
                  ...
                  $TOKEN = $JWT.sign(<... $OBJ ...>, ...)
              - pattern: $TOKEN = $JWT.sign(<... $OBJ ...>, ...)
          - pattern: "new $JOSE.SignJWT(<... {$PASSWORD: $VALUE} ...>)"
          - patterns:
              - pattern: |
                  $OBJ = <... {$PASSWORD: $VALUE} ...>
                  ...
                  new $JOSE.SignJWT(<... $OBJ ...>)
              - pattern: new $JOSE.SignJWT(<... $OBJ ...>)
          - patterns:
              - pattern: |
                  $OBJ. ... .$PASSWORD = ...
                  ...
                  new $JOSE.SignJWT(<... $OBJ ...>)
              - pattern: new $JOSE.SignJWT(<... $OBJ ...>)
      - metavariable-regex:
          metavariable: $PASSWORD
          regex: (?i)\b(?:.*password.*)\b
    severity: "ERROR"
    languages:
      - "javascript"
    metadata:
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-522"
      shortDescription: "Insufficiently protected credentials"
      security-severity: "HIGH"
      category: "security"
      related-packages: ['pkg:npm/jsonwebtoken', 'pkg:npm/jose']
      license: GNU Lesser General Public License v3.0
    message: |-
      The application is storing a password in the JWT token payload. Storing 
      passwords in JWT token payloads is an insecure practice that can lead to 
      compromised credentials. 

      The password transmitted in the JWT payload is not encrypted and therefore 
      visible to anyone who intercepts the token. It is recommended to avoid storing 
      sensitive information like passwords in JWTs. Instead, reference user identifiers 
      that map to credentials stored securely on the server.This helps to mitigate the 
      risk of exposing passwords through JWT tokens that could be intercepted or leaked.

      Secure code example of secure JWT signing:
      ```
      router.route("/jsonwebtoken/1").get((req, res) => {
        // any payload without passwords or any other sensitive data will be secure
        const payload = { user_id: 123, username: 'john_doe' };
        const token = jwt.sign(payload, secretKey, { algorithm: 'HS256' });
        console.log('Generated Token:', token);
        res.send({ token })
      })
      ```
  - id: "rules_lgpl_javascript_jwt_rule-jwt-exposed-data"
    patterns:
      - pattern-inside: |
          require('jose')
          ...
      - pattern-either:
          - patterns:
              - pattern-inside: |-
                  function (...,$INPUT,...) {...}
              - pattern-either:
                  - pattern: "$JOSE.JWT.sign($INPUT,...)"
                  - pattern: "$JWT.sign($INPUT,...)"
          - patterns:
              - pattern-inside: |-
                  function $F(...,$INPUT,...) {...}
              - pattern-either:
                  - pattern: "$JOSE.JWT.sign($INPUT,...)"
                  - pattern: "$JWT.sign($INPUT,...)"
    message: |
      The object is passed strictly to jose.JWT.sign(...). Make sure  that sensitive information is not exposed through JWT token payload.
    severity: "WARNING"
    metadata:
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-522"
      shortDescription: "Insufficiently protected credentials"
      security-severity: "HIGH"
      category: "security"
      related-packages: ['pkg:npm/jsonwebtoken', 'pkg:npm/jose']
      license: GNU Lesser General Public License v3.0
    languages:
      - "javascript"
  - id: "rules_lgpl_javascript_jwt_rule-jwt-express-hardcoded"
    patterns:
      - pattern-either:
          - pattern-inside: |
              import { ..., $JWT,... } from 'express-jwt';
              ...
          - pattern-inside: |
              var {..., expressjwt: $JWT,... } = require('express-jwt');
              ...
      - pattern-either:
          - pattern: $JWT(<...{...,secret:"...",...}...>)
          - patterns:
              - pattern-inside: |
                  $OPTS = <... {secret: "..."} ...>;
                  ...
              - pattern: "$JWT(<... $OPTS ...>,...)"
          - patterns:
              - pattern-inside: |
                  $OPTS = <... {secret: "..."} ...>;
                  ...
                  $OPTS2 = <... $OPTS ...>;
                  ...
              - pattern: "$JWT(<... $OPTS2 ...>,...)"
    message: |
      Hardcoded JWT secret or private key was found. Hardcoding secrets like JWT signing keys poses a significant security risk. 
      If the source code ends up in a public repository or is compromised, the secret is exposed. Attackers could then use the secret to 
      generate forged tokens and access the system. Store it properly in an environment variable.

      Here are some recommended safe ways to access JWT secrets:
        - Use environment variables to store the secret and access it in code instead of hardcoding. This keeps it out of source control.
        - Use a secrets management service to securely store and tightly control access to the secret. Applications can request the secret at runtime.
        - For local development, use a .env file that is gitignored and access the secret from process.env.

      sample code snippet of accessing JWT secret from env variables
      ```
      router.route("/auth-route-1").get(
        jwt({ secret: process.env.secret, algorithms: ['HS256'] }),
          (req, res) => {
              res.send('Token is valid');
          }
      );
      ```
    severity: "ERROR"
    languages:
      - "javascript"
    metadata:
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-522"
      shortDescription: "Insufficiently protected credentials"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/express-jwt']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_jwt_rule-jwt-not-revoked"
    patterns:
      - pattern-inside: |
          $JWT = require('express-jwt')
          ...
      - pattern: "$JWT(...)"
      - pattern-not-inside: |-
          $JWT(<... {isRevoked:...} ...>,...)
      - pattern-not-inside: |-
          $OPTS = <... {isRevoked:...} ...>;
          ...
          $JWT($OPTS,...)
    message: |
      No token revoking configured for `express-jwt`. A leaked token could still be used and unable to be revoked. Consider using function as the `isRevoked` option.
    severity: "WARNING"
    languages:
      - "javascript"
    metadata:
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-522"
      shortDescription: "Insufficiently protected credentials"
      security-severity: "HIGH"
      category: "security"
      related-packages: ['pkg:npm/express-jwt']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_jwt_rule-node-jwt-none-algorithm"
    mode: "taint"
    pattern-sources:
      - pattern: $JWT = require("jsonwebtoken")
      - pattern: import $JWT from "jsonwebtoken"
    pattern-sinks:
      - pattern: $JWT.verify($P, $X, {...,algorithms:[...,'none',...],...},...)
      - pattern: $JWT.sign($P, $X, {...,algorithm:'none',...},...)
    message: |
      Use of `{algorithm:'none'}` detected with `jsonwebtoken`. 
      Using none as the algorithm for jsonwebtoken can directly impact the integrity of the information transfer through the JWT token.
      Consider using a secure algorithm to sign your JWT token such as HMAC or RSA.
      Some safe usage examples:
      ```
      let token = jwt.sign({user:"user1"}, 'secret', {algorithm: 'HS256'});  
      ```
      Using a secure algorithm can protect the integrity of the token information. 
      Avoid using none as the algorithm when signing jwt tokens since it can violate the integrity of the JWT information.
    languages:
      - "javascript"
    severity: "ERROR"
    metadata:
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      cwe: "CWE-327"
      shortDescription: "Use of a broken or risky cryptographic algorithm"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/jsonwebtoken', 'pkg:npm/jose']
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_javascript_redirect_rule-express-open-redirect
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-inside: |
              function ($REQ, $RES, ...) {...}
          - focus-metavariable: $REQ
    pattern-sinks:
      - pattern: |
          $RES.redirect(...)
    pattern-sanitizers:
      - pattern-either:
          - patterns:
              - pattern-either:
                  - pattern: |
                      if($VALIDATION){
                        ...
                        $RES.redirect(...)
                        ...
                      } 
                  - pattern: |
                      $A = $VALIDATION
                      ...
                      if($A){
                        ...
                        $RES.redirect(...)
                        ...
                      }
              - metavariable-pattern:
                  metavariable: $VALIDATION
                  pattern-either:
                    - pattern: |
                        $AL.includes(...)  
                    - pattern: |
                        $AL.indexOf(...) !== -1
                    - pattern: |
                        $AL.find(...) !== undefined
                    - pattern: |
                        $ALS.has(...)
          - patterns:
              - pattern: |
                  $RES.redirect("$DOM" + ...)
              - metavariable-regex:
                  metavariable: $DOM
                  regex: (http(s)?:\/\/.*\/)
    message: |
      Passing untrusted user input in `redirect()` can result in an open redirect
      vulnerability. This could be abused by malicious actors to trick users into 
      being redirected to websites under their control to capture authentication
      information.  
      To prevent open redirect vulnerabilities:

      - Always validate and sanitize user inputs, especially URL parameters
       or query strings that may influence the flow of the application.
      - Use allowlists (lists of permitted URLs) to validate redirect targets 
       against known, trusted URLs before performing the redirect.
      - Avoid directly using user input for redirecting. If unavoidable, ensure
       strict validation against an allowlist.

      Following is an example of secure validation against allowlist to prevent the vulnerability:
       ```
       // Define a list of explicitly allowed URLs for redirection
       const allowedUrls = [
           'https://www.example.com/page1',
           'https://www.example.com/page2',
           'https://secure.example.com/page3'
       ];

       app.get('/redirect/:url', (req, res) => {
           const url = decodeURIComponent(req.params.url);
           const isAllowed = allowedUrls.includes(url);
           if (isAllowed) {
               // If the URL is allowed, proceed with the redirect
               res.redirect(url);
           } else {
               res.status(400).send('Invalid redirect URL');
           }
       });
       ```
    languages:
      - javascript
    severity: ERROR
    metadata:
      shortDescription: "URL redirection to untrusted site 'open redirect'"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-601"
      related-packages: ['pkg:npm/express']
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_javascript_redirect_rule-express-open-redirect2
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-inside: |
              function ($REQ, $RES, ...) {...}
          - focus-metavariable: $REQ
    pattern-sinks:
      - pattern-either:
          - patterns:
              - pattern-not: |
                  $RES.$METHOD("=~/location/i", "=~/http(s)?:\/\/.*\//" + ...)
              - pattern-not-inside: |
                  $VAR = "=~/http(s)?:\/\/.*\//" + ...;
                  ...
              - pattern: |
                  $RES.$METHOD("=~/location/i", $VAR)
              - metavariable-regex:
                  metavariable: $METHOD
                  regex: (header|set|append|setHeader)
          - patterns:
              - pattern-not-inside: |
                  $V = "=~/http(s)?:\/\/.*\//" + ...;
                  ...
              - pattern-not:
                  patterns:
                    - pattern: |
                        $RES.writeHead(..., { 
                            ..., 
                            location: $V,
                            ...
                        })
                    - metavariable-pattern:
                        metavariable: $V
                        patterns:
                          - pattern: |
                              "=~/http(s)?:\/\/.*\//" + ...
              - pattern: |
                  $RES.writeHead(..., { 
                              ..., 
                              location: $V,
                              ...
                          })
              - focus-metavariable: $V
          - patterns:
              - pattern-not: |
                  $RES.location("=~/http(s)?:\/\/.*\//" + ...)
              - pattern-not-inside: |
                  $VAR = "=~/http(s)?:\/\/.*\//" + ...;
                  ...
              - pattern: |
                  $RES.location($VAR)
    pattern-sanitizers:
      - pattern-either:
          - patterns:
              - pattern-either:
                  - pattern: |
                      if($VALIDATION){
                        ...
                      } 
                  - pattern: |
                      $A = $VALIDATION
                      ...
                      if($A){
                        ...
                      }
              - metavariable-pattern:
                  metavariable: $VALIDATION
                  pattern-either:
                    - pattern: |
                        $AL.includes(...)  
                    - pattern: |
                        $AL.indexOf(...) !== -1
                    - pattern: |
                        $AL.find(...) !== undefined
                    - pattern: |
                        $ALS.has(...)
    message: |
      Passing untrusted user input in `redirect()` can result in an open redirect
      vulnerability. This could be abused by malicious actors to trick users into 
      being redirected to websites under their control to capture authentication
      information.  
      To prevent open redirect vulnerabilities:

      - Always validate and sanitize user inputs, especially URL parameters
       or query strings that may influence the flow of the application.
      - Use allowlists (lists of permitted URLs) to validate redirect targets 
       against known, trusted URLs before performing the redirect.
      - Avoid directly using user input for redirecting. If unavoidable, ensure
       strict validation against an allowlist.

      Following is an example of secure validation against allowlist to prevent the vulnerability:
       ```
       // Define a list of explicitly allowed URLs for redirection
       const allowedUrls = [
           'https://www.example.com/page1',
           'https://www.example.com/page2',
           'https://secure.example.com/page3'
       ];

       app.get('/redirect/:url', (req, res) => {
           const url = decodeURIComponent(req.params.url);
           const isAllowed = allowedUrls.includes(url);
           if (isAllowed) {
               // If the URL is allowed, proceed with the redirect
                res.location(url).status(302).end();
           } else {
               res.status(400).send('Invalid redirect URL');
           }
       });
       ```
    languages:
      - javascript
    severity: ERROR
    metadata:
      shortDescription: "URL redirection to untrusted site 'open redirect'"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-601"
      related-packages: ['pkg:npm/express']
      security-severity: "CRITICAL"
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_javascript_ssrf_rule-node-ssrf
    mode: taint
    pattern-sources:
      - patterns:
          - focus-metavariable: $REQ
          - pattern: function ($REQ, $RES, ...) {...}
      - patterns:
          - focus-metavariable: $REQ
          - pattern: function $FUNC($REQ, $RES, ...) {...}
    pattern-sanitizers:
      - patterns:
          - pattern-either:
              - pattern: |
                  if($VALIDATION){
                  ...
                  } 
              - pattern: |
                  $A = $VALIDATION
                  ...
                  if($A){
                  ...
                  }
          - metavariable-pattern:
              metavariable: $VALIDATION
              pattern-either:
                - pattern: |
                    $AL.includes(...)  
                - pattern: |
                    $AL.indexOf(...) !== -1
                - pattern: |
                    $AL.find(...) !== undefined
                - pattern: |
                    $ALS.has(...)
    pattern-sinks:
      - patterns:
          - focus-metavariable: $REQ
          - pattern-either:
              - pattern-inside: |
                  $NEEDLE = require('needle');
                  ...
              - pattern-inside: |
                  import $NEEDLE from 'needle'
                  ...
          - pattern-either:
              - pattern: $NEEDLE('$VERB', $REQ, ...)
              - pattern: $NEEDLE.$VERB($REQ, ...)
          - metavariable-regex:
              metavariable: $VERB
              regex: ^(get|put|post|patch|delete|head)$
      - patterns:
          - focus-metavariable: $REQ
          - pattern-either:
              - pattern-inside: |
                  $AXIOS = require('axios');
                  ...
              - pattern-inside: |
                  import $AXIOS from 'axios'
                  ...
          - pattern-either:
              - patterns:
                  - metavariable-regex:
                      metavariable: $URL
                      regex: ^(url|baseURL)$
                  - pattern-either:
                      - pattern: |
                          $AXIOS({ ..., $URL: $REQ, ...})
                      - pattern: |
                          $AXIOS.create({ ..., $URL: $REQ, ...})
              - patterns:
                  - pattern: $AXIOS.$VERB($REQ, ...)
                  - metavariable-regex:
                      metavariable: $VERB
                      regex: ^(get|put|post|patch|delete|head)$
              - patterns:
                  - pattern: axios($REQ)
                  - pattern-not-inside: axios({...})
              - pattern: |
                  axios.defaults.baseURL = $REQ
      - patterns:
          - focus-metavariable: $URL
          - pattern-either:
              - pattern-inside: |
                  {..., request, ... } = require('urllib');
                  ...
              - pattern-inside: |
                  import {..., request, ... } from 'urllib'
                  ...
          - pattern: request($URL, ...)
      - patterns:
          - focus-metavariable: $URL
          - pattern-either:
              - pattern-inside: |
                  {..., HttpClient, ... } = require('urllib');
                  ...
              - pattern-inside: |
                  import {..., HttpClient, ... } from 'urllib'
                  ...
              - pattern: |
                  $HTTPCLIENT = new HttpClient({
                                      allowH2: true,
                                    });
                  ...
                  $HTTPCLIENT.request($URL, ...)
      - patterns:
          - focus-metavariable: $URL
          - pattern-either:
              - pattern-inside: |
                  $URLLIIB = require('urllib');
                  ...
              - pattern-inside: |
                  import $URLLIIB from 'urllib'
                  ...
          - pattern: $URLLIIB.request($URL, ...)
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  $SA = require('superagent');
                   ...
              - pattern-inside: |
                  import $SA from 'superagent'
                  ...
          - patterns:
              - pattern: $SA.$VERB(...)
              - metavariable-regex:
                  metavariable: $VERB
                  regex: ^(get|put|post|patch|delete|head)$
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern: fetch($REQ,...)
                  - focus-metavariable: $REQ
                  - pattern-not: fetch(new Request(...))
              - patterns:
                  - pattern: fetch(new Request($REQ,...))
                  - focus-metavariable: $REQ
          - pattern-not-inside: |
              $REQUEST_OBJ = new Request(...)
              ...
              fetch($REQUEST_OBJ)
      - patterns:
          - pattern: new Request($REQ,...)
          - focus-metavariable: $REQ
          - pattern-inside: |
              $REQUEST_OBJ = new Request($REQ,...)
              ...
              fetch($REQUEST_OBJ)
      - patterns:
          - focus-metavariable: $REQ
          - pattern-either:
              - pattern-inside: |
                  $HTTP = require('$PKG');
                  ...
              - pattern-inside: |
                  import $HTTP from $PKG
                  ...
          - metavariable-regex:
              metavariable: $PKG
              regex: ^(http|https)$
          - metavariable-regex:
              metavariable: $METHOD
              regex: ^(get|request)$
          - pattern-either:
              - patterns:
                  - pattern: $HTTP.$METHOD($REQ, ...)
                  - pattern-not-inside: |
                      $HTTP.$METHOD({...}, ...)
                  - pattern-not-inside: |
                      $OPTS = {...}
                      ...
                      $HTTP.$METHOD($OPTS, ...)
              - pattern: |
                  $HTTP.$METHOD({..., hostname: $REQ}, ...)
              - pattern: |
                  $OPTS = {..., hostname: $REQ, ...}
                  ...
                  $HTTP.$METHOD($OPTS, ...)
      - patterns:
          - focus-metavariable: $REQ
          - pattern-either:
              - pattern-inside: |
                  { ..., io, ... } = require('socket.io-client');
                  ...
              - pattern-inside: |
                  import { ..., io, ... } from 'socket.io-client'
                  ...
          - pattern: io($REQ, ...)
      - patterns:
          - focus-metavariable: $HOST
          - pattern-either:
              - pattern-inside: |
                  $NET = require('net');
                  ...
              - pattern-inside: |
                  import $NET from 'net'
                  ...
          - metavariable-regex:
              metavariable: $METHOD
              regex: ^(connect|createConnection)$
          - pattern-either:
              - pattern: $NET.$METHOD($PORT, $HOST, ...)
              - pattern: |
                  $NET.$METHOD({..., host: $HOST, ...}, ...)
              - pattern: |
                  $OPTS = {..., host: $HOST, ...}
                  ...
                  $NET.$METHOD($OPTS, ...)
              - pattern: |
                  $CLIENT = new $NET.Socket()
                  ...
                  $CLIENT.$METHOD($PORT, $HOST, ...)
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  $BENT = require('bent');
                  ...
              - pattern-inside: |
                  import $BENT from 'bent';
                  ...
          - pattern: $BENT(...)
      - patterns:
          - focus-metavariable: $REQ
          - pattern-either:
              - pattern-inside: |
                  $BENT = require('bent');
                  ...
                  $MTD = $BENT(...)
              - pattern-inside: |
                  import $BENT from 'bent';
                  ...
                  $MTD = $BENT(...)
          - pattern: $MTD($REQ, ...)
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  $BENT = require('bent');
                  ...
                  $GETBENT = $BENT('$PKG')
                  ...
              - pattern-inside: |
                  import $BENT from 'bent';
                  ...
                  $GETBENT = $BENT('$PKG')
                  ...
          - pattern: $GETBENT(...)
          - metavariable-regex:
              metavariable: $PKG
              regex: ^(json|buffer)$
      - patterns:
          - focus-metavariable: $REQ
          - pattern-either:
              - pattern-inside: |
                  import $GOT from 'got';
                  ...
          - pattern: $GOT.$VERB($REQ, ...)
          - metavariable-regex:
              metavariable: $VERB
              regex: ^(get|put|post|patch|delete|head)$
      - patterns:
          - focus-metavariable: $URL
          - pattern-either:
              - pattern-inside: |
                  $REQUEST = require('request');
                  ...
              - pattern-inside: |
                  import $REQUEST from 'request';
                  ...
          - pattern: $REQUEST($URL, ...)
    message: |
      This application allows user-controlled URLs to be passed directly to HTTP client libraries. 
      This can result in Server-Side Request Forgery (SSRF).
      SSRF refers to an attack where the attacker can abuse functionality on 
      the server to force it to make requests to other internal systems within your 
      infrastructure that are not directly exposed to the internet. 
      This allows the attacker to access internal resources they do not have direct access to.

      Some risks of SSRF are:

      - Access and manipulation of internal databases, APIs, or administrative panels
      - Ability to scan internal network architecture and services
      - Can be used to pivot attacks into the internal network
      - Circumvent network segregation and firewall rules

      To avoid this, try using hardcoded HTTP request calls or a whitelisting object to 
      check whether the user input is trying to access allowed resources or not.

      Here is an example:
      ```
      var whitelist = [
        "https://example.com", 
        "https://example.com/sample"
      ]

      app.get('/ssrf/node-ssrf/axios/safe/3', function (req, res) {
        if(whitelist.includes(req.query.url)){
            axios.get(url, {})
                .then(function (response) {
                    console.log(response);
                })
                .catch(function (response) {
                    console.log(response);  
                })
        }
      });
      ``` 
      For more information on SSRF see OWASP:
      https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
    languages:
      - javascript
    severity: ERROR
    metadata:
      owasp:
        - A1:2017-Injection
        - A03:2021-Injection
      cwe: CWE-918
      shortDescription: Server-side request forgery (SSRF)
      security-severity: HIGH
      category: security
      related-packages: ['pkg:npm/request', 'pkg:npm/got', 'pkg:npm/superagent', 'pkg:npm/axios', 'pkg:npm/socket.io-client',
        'pkg:npm/net', 'pkg:npm/bent', 'pkg:npm/urllib', 'pkg:npm/superagent']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_ssrf_rule-phantom-ssrf"
    patterns:
      - pattern-inside: |
          require('phantom')
          ...
      - pattern-either:
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: "$PAGE.open(<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$PAGE.setContent(<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$PAGE.open(<... $REQ.$BODY ...>,...)"
          - pattern: "$PAGE.setContent(<... $REQ.$BODY ...>,...)"
          - pattern: "$PAGE.openUrl(<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$PAGE.openUrl(<... $REQ.$BODY ...>,...)"
          - pattern: "$PAGE.evaluateJavaScript(<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$PAGE.evaluateJavaScript(<... $REQ.$BODY ...>,...)"
          - pattern: "$PAGE.property(\"content\",<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$PAGE.property(\"content\",<... $REQ.$BODY ...>,...)"
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.open(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.open(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.setContent(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.setContent(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.openUrl(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.openUrl(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluateJavaScript(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluateJavaScript(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.property("content",<... $INPUT ...>,...)
          - pattern: |-
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.property("content",<... $INPUT ...>,...)
    message: |
      'If unverified user data can reach the `phantom` methods it can result in Server-Side Request Forgery vulnerabilities.

      '
    metadata:
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-918"
      shortDescription: "Server-side request forgery (SSRF)"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/phantomjs']
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"
    languages:
      - "javascript"
  - id: "rules_lgpl_javascript_ssrf_rule-playwright-ssrf"
    patterns:
      - pattern-inside: |
          require('playwright')
          ...
      - pattern-either:
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: "$PAGE.goto(<... $REQ.$QUERY.$FOO ...>, ...)"
          - pattern: "$PAGE.goto(<... $REQ.$BODY ...>, ...)"
          - pattern: "$PAGE.setContent(<... $REQ.$QUERY.$FOO ...>, ...)"
          - pattern: "$PAGE.setContent(<... $REQ.$BODY ...>, ...)"
          - pattern: "$PAGE.evaluate(<... $REQ.$QUERY.$FOO ...>, ...)"
          - pattern: "$PAGE.evaluate(<... $REQ.$BODY ...>, ...)"
          - pattern: "$PAGE.evaluate($CODE,..., <... $REQ.$QUERY.$FOO ...>, ...)"
          - pattern: "$PAGE.evaluate($CODE,..., <... $REQ.$BODY ...>, ...)"
          - pattern: "$PAGE.evaluateHandle(<... $REQ.$QUERY.$FOO ...>, ...)"
          - pattern: "$PAGE.evaluateHandle(<... $REQ.$BODY ...>, ...)"
          - pattern: "$PAGE.evaluateHandle($CODE,..., <... $REQ.$QUERY.$FOO ...>, ...)"
          - pattern: "$PAGE.evaluateHandle($CODE,..., <... $REQ.$BODY ...>, ...)"
          - pattern: "$PAGE.evaluateOnNewDocument(<... $REQ.$BODY ...>, ...)"
          - pattern: "$PAGE.evaluateOnNewDocument(<... $REQ.$BODY.$FOO ...>, ...)"
          - pattern: "$CONTEXT.addInitScript(<... $REQ.$BODY ...>,...)"
          - pattern: "$CONTEXT.addInitScript(<... $REQ.$BODY.$FOO ...>,...)"
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.goto(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.goto(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.setContent(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.setContent(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluate($CODE,..., <... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluate($CODE,..., <... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluate(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluate(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluateHandle(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluateHandle(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluateHandle($CODE,..., <... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluateHandle($CODE,..., <... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluateOnNewDocument(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluateOnNewDocument(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $CONTEXT.addInitScript($INPUT,...)
    message: |
      If unverified user data can reach the `puppeteer` methods it can result in Server-Side Request Forgery vulnerabilities.
    metadata:
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-918"
      shortDescription: "Server-side request forgery (SSRF)"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/playwright']
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"
    languages:
      - "javascript"
  - id: "rules_lgpl_javascript_ssrf_rule-puppeteer-ssrf"
    patterns:
      - pattern-inside: |
          require('puppeteer')
          ...
      - pattern-either:
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: "$PAGE.goto(<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$PAGE.goto(<... $REQ.$BODY ...>,...)"
          - pattern: "$PAGE.setContent(<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$PAGE.setContent(<... $REQ.$BODY ...>,...)"
          - pattern: "$PAGE.evaluate(<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$PAGE.evaluate(<... $REQ.$BODY ...>,...)"
          - pattern: "$PAGE.evaluateHandle(<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$PAGE.evaluateHandle(<... $REQ.$BODY ...>,...)"
          - pattern: "$PAGE.evaluateOnNewDocument(<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$PAGE.evaluateOnNewDocument(<... $REQ.$BODY ...>,...)"
          - pattern: "$PAGE.evaluate($CODE,<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$PAGE.evaluate($CODE,<... $REQ.$BODY ...>,...)"
          - pattern: "$PAGE.evaluateHandle($CODE,<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$PAGE.evaluateHandle($CODE,<... $REQ.$BODY ...>,...)"
          - pattern: "$PAGE.evaluateOnNewDocument($CODE,<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$PAGE.evaluateOnNewDocument($CODE,<... $REQ.$BODY ...>,...)"
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.goto(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.goto(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.setContent(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.setContent(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluate(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluate(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluateHandle(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluateHandle(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluateOnNewDocument(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluateOnNewDocument(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluate($CODE,<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluate($CODE,<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluateHandle($CODE,<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluateHandle($CODE,<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PAGE.evaluateOnNewDocument($CODE,<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PAGE.evaluateOnNewDocument($CODE,<... $INPUT ...>,...)
    message: |
      If unverified user data can reach the `puppeteer` methods it can result in Server-Side Request Forgery vulnerabilities.
    metadata:
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-918"
      shortDescription: "Server-side request forgery (SSRF)"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/puppeteer']
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"
    languages:
      - "javascript"
  - id: rules_lgpl_javascript_ssrf_rule-wkhtmltoimage-ssrf
    mode: taint
    pattern-sources:
      - patterns:
          - pattern: |
              function($REQ, $RES, ...){
                ...
              }
          - focus-metavariable: $REQ
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  $W = require('wkhtmltoimage')
                  ...
              - pattern-inside: |
                  import $W from 'wkhtmltoimage'
                  ...
          - pattern: |
              $W.generate(...)
    pattern-sanitizers:
      - patterns:
          - pattern-inside: |
              if($ALLOWED.includes($URL)){
                ...
              }
    message: |
      This rule detects instances where user-controlled URLs are passed directly
      to the `generate` function of `wkhtmltoimage` library. This practice can
      lead to Server Side Request Forgery (SSRF) vulnerabilities, where an
      attacker can induce the server to make requests to arbitrary URLs. This
      can potentially expose internal services within the network or lead to
      information disclosure.

      To mitigate this vulnerability, ensure that URLs are safe and intended for 
      public access. Implementing allowlists for acceptable domains or schemes can 
      significantly reduce the risk of SSRF. Additionally, consider using server-side 
      proxy services that restrict the outgoing requests to trusted domains and 
      resources.

      Secure Code Example:
      ```
      const wkhtmltoimage = require('wkhtmltoimage');

      // Define an allowlist of domains
      const allowedDomains = ['example.com', 'trusted-source.com'];

      app.post('/generate-image', (req, res) => {
        const userInputUrl = req.body.url; 
        const parsedUrl = new URL(userInputUrl);

        // Check if the domain is in the allowlist
        if (allowedDomains.includes(parsedUrl.hostname)) {
            wkhtmltoimage.generate(userInputUrl, { output: 'output.jpg' }, 
              function (err, stream) {
                if (err) {
                    return res.status(500).send('Error generating image');
                }
                // Send a success response or the image itself
                res.status(200).send('Image generated successfully');
            });
        } else {
            res.status(400).send('URL is not allowed due to security policies.');
        }
      });
      ```
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A1:2017-Injection"
        - "A10:2021-Server-Side Request Forgery"
      cwe: "CWE-918"
      shortDescription: "Server-side request forgery (SSRF)"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/wkhtmltoimage']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_ssrf_rule-wkhtmltopdf-ssrf"
    patterns:
      - pattern-inside: |
          require('wkhtmltopdf')
          ...
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              $INP = <... $REQ.$VAR ...>;
              ...
              wkhtmltopdf(<... $INP ...>, ...)
          - pattern: |
              $INP = <... $REQ.$VAR.$FOO ...>;
              ...
              wkhtmltopdf(<... $INP ...>, ...)
          - pattern: |
              wkhtmltopdf(<... $REQ.$VAR ...>, ...)
          - pattern: |
              wkhtmltopdf(<... $REQ.$VAR.$FOO ...>, ...)
    message: |
      User controlled URL reached to `wkhtmltopdf` can result in Server Side Request Forgery (SSRF).
    languages:
      - "javascript"
    severity: "ERROR"
    metadata:
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-918"
      shortDescription: "Server-side request forgery (SSRF)"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/wkhtmltopdf']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_traversal_rule-admzip-path-overwrite"
    patterns:
      - pattern-inside: |
          $X = require('adm-zip')
          ...
      - pattern-not: |
          if ($FILENAME.indexOf('..'))
      - pattern-not: |
          $FS.createWriteStream($PATH.join(..., $PATH.basename($FILENAME, ...)))
      - pattern-not: |
          $FS.writeFile($PATH.join(..., $PATH.basename($FILENAME, ...)))
      - pattern-not: |
          $FS.writeFileSync($PATH.join(..., $PATH.basename($FILENAME, ...)))
      - pattern-either:
          - pattern: "$ZIPENTZ.forEach(function $FUNC($ENTRY, ...) { $FS.createWriteStream(...) }, ...)"
          - pattern: "$ZIPENTZ.forEach(function $FUNC($ENTRY, ...) { $FS.writeFile(...) }, ...)"
          - pattern: "$ZIPENTZ.forEach(function $FUNC($ENTRY, ...) { $FS.writeFileSync(...) }, ...)"
    message: |
      Insecure ZIP archive extraction using adm-zip can result in arbitrary path over write and can result in code injection.
    languages:
      - "javascript"
    metadata:
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      cwe: "CWE-22"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/adm-zip']
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"
  - id: "rules_lgpl_javascript_traversal_rule-express-lfr"
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-inside: function ($REQ, $RES, ...) {...}
          - pattern: $REQ.$FUNC. ...
          - metavariable-regex:
              metavariable: $FUNC
              regex: ^(body|params|query|cookies|hostname|subdomains|ip|ips|originalUrl|path)$
    pattern-sinks:
      - pattern: "$RES.render(..., $VAR)"
    message: |
      This application is using untrusted user input in express render() function.
      Rendering templates with untrusted user input enables arbitrary file read 
      vulnerabilities when using templating engines like Handlebars (hbs). 

      An attacker can craft malicious input that traverses the filesystem and exposes sensitive files. 
      Consider sanitizing and validating all user input before passing it to render() to prevent arbitrary file reads. 

      Sample safe use of express.render function
      ```
      app.get("/traversal/2", async (req, res) => {
          var indexPath = "index";
          res.render(indexPath, { title: "Index Page" })
      });
      ```

      For more details see: 
      https://owasp.org/www-community/attacks/Path_Traversal
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      cwe: "CWE-23"
      shortDescription: "Relative path traversal"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/express']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_traversal_rule-express-lfr-warning"
    patterns:
      - pattern-not-inside: |
          require('hbs')
          ...
      - pattern-inside: |
          require('express')
          ...
      - pattern-either:
          - pattern: |
              $INP = <... $REQ.$QUERY ...>;
              ...
              $RES.render($VIEW, <... $INP ...>)
          - pattern: |
              $INP = <... $REQ.$QUERY.$FOO ...>;
              ...
              $RES.render($VIEW, <... $INP ...>)
          - pattern: "$RES.render($VIEW, <... $REQ.$QUERY.$FOO ...>)"
          - pattern: "$RES.render($VIEW, <... $REQ.$BODY ...>)"
    message: |
      Untrusted user input in express render() function can result in arbitrary file read if hbs templating is used.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      cwe: "CWE-23"
      shortDescription: "Relative path traversal"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/express']
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_javascript_traversal_rule-generic-path-traversal
    mode: taint
    languages:
      - "javascript"
    pattern-sources:
      - patterns:
          - focus-metavariable: $REQ
          - pattern: function ($REQ, $RES, ...) {...}
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  $FS = require('fs')
                  ...
              - pattern-inside: |
                  import $FS from 'fs'
                  ...
          - pattern-either:
              - pattern: $FS.createReadStream(...)
              - patterns:
                  - pattern: $FS.readFile($REQ, ...)
                  - focus-metavariable: $REQ
              - patterns:
                  - pattern: $FS.readFileSync($REQ,...)
                  - focus-metavariable: $REQ
              - patterns:
                  - pattern: $FS.readFileAsync($REQ,...)
                  - focus-metavariable: $REQ
    message: |
      This application is using untrusted user input with the readFile() and
      readFileSync() functions. 
      This can lead to directory traversal attacks, as reading files with untrusted input enables arbitrary file access.
      An attacker could craft malicious input that traverses the file system and exposes sensitive files. 
      Please consider sanitizing and validating all user input before passing it to readFile() or readFileSync() to prevent unwanted file reads.
      Here is an example of a safer usage in readFileAsync():
      ```
      app.get('/foo', function (req, res) {
          var fileName = config.dirName + '/' + data;
          fs.readFileAsync("fileName")
              .then(function (data) {
                  res.download(fileName, downloadFileName);
              })
          })
      ```
      For more details see: 
      https://owasp.org/www-community/attacks/Path_Traversal
    severity: "WARNING"
    metadata:
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      cwe: "CWE-23"
      shortDescription: "Relative path traversal"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/fs', 'pkg:npm/fs-extra']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_traversal_rule-join-resolve-path-traversal"
    patterns:
      - pattern-inside: |
          require('path')
          ...
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: "$PATH.join(...,<... $REQ.$BODY ...>,...)"
          - pattern: "$PATH.join(...,<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: |
              $VAR = <... $REQ.$BODY ...>;
              ...
              $PATH.join(...,<... $VAR ...>,...)
          - pattern: |
              $VAR = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PATH.join(...,<... $VAR ...>,...)
          - pattern: "$PATH.resolve(...,<... $REQ.$BODY ...>,...)"
          - pattern: "$PATH.resolve(...,<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: |
              $VAR = <... $REQ.$BODY ...>;
              ...
              $PATH.resolve(...,<... $VAR ...>,...)
          - pattern: |-
              $VAR = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PATH.resolve(...,<... $VAR ...>,...)
    message: |
      'Path constructed with user input can result in Path Traversal. Ensure that user input does not reach `join()` or `resolve()`. '
    languages:
      - "javascript"
    metadata:
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      cwe: "CWE-22"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/path']
      license: GNU Lesser General Public License v3.0
    severity: "WARNING"
  - id: "rules_lgpl_javascript_traversal_rule-tar-path-overwrite"
    patterns:
      - pattern-inside: |
          $X = require('tar-stream')
          ...
      - pattern-not-inside: |
          $Y.pipe($UNZIP.Parse(...)).on('entry', function $FUNC(...) {
              ...
          }, ...)
      - pattern-inside: |
          $EXTRACT.on('entry', function $FUNC(...) {
            ...
          }, ...)
      - pattern-not: |
          if ($FILENAME.indexOf('..'))
      - pattern-not: |
          $FS.createWriteStream($PATH.join(..., $PATH.basename($FILENAME, ...)))
      - pattern-not: |
          $FS.writeFile($PATH.join(..., $PATH.basename($FILENAME, ...)))
      - pattern-not: |
          $FS.writeFileSync($PATH.join(..., $PATH.basename($FILENAME, ...)))
      - pattern-either:
          - pattern: |
              $FS.createWriteStream($FIL, ...)
          - pattern: |
              $FS.writeFile($FIL, ...)
          - pattern: |
              $FS.writeFileSync($FIL, ...)
    message: |
      Insecure TAR archive extraction can result in arbitrary path over write and can result in code injection.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      cwe: "CWE-22"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/tar-stream']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_traversal_rule-zip-path-overwrite"
    patterns:
      - pattern-either:
          - pattern-inside: |
              import $X from 'unzipper'
              ...
          - pattern-inside: |
              $X = require('unzipper')
              ...
      - pattern-inside: |
          $Y.pipe($X.Parse(...)).on('entry', function $FUNC(...) {
              ...
          }, ...)
      - pattern-not-inside: |
          if (<... $FILENAME.indexOf(...) ...>){ 
          ...
          }
      - pattern-not-inside: |
          $BASE = $PATH.basename($FILENAME, ...)
          ...
          $JOIN = $PATH.join(..., $BASE)
          ...
      - pattern-not: |
          $FS.$MTD($PATH.join(..., $PATH.basename($FILENAME, ...)))
      - pattern: |
          $FS.$MTD($FIL, ...)
      - metavariable-regex:
          metavariable: $MTD
          regex: ^(writeFileSync|writeFile|createWriteStream)$
    message: |
      This application is extracting ZIP archives without sanitizing paths or writing files to a dedicated extraction directory. 
      This allows attackers to overwrite sensitive files or inject malicious code by manipulating TAR archive contents.

      To fix, sanitize all paths from ZIP archives before writing extracted files using path.basename and path.join.

      Example  of extracting tar files safely:
      ```
      app.get("/extract", async (req, res) => {
              fs.createReadStream(zipPath)
              .pipe(unzipper.Parse())
              .on('entry', entry => {
                  const directory = 'assets/tar/extracted/';
                  const filename = entry.path;
                  entry.pipe(fs.createWriteStream(path.join(directory, filename)));
              });
      });
      ```

      Write extracted files only to a dedicated extraction directory, not the global filesystem. Limit extracts to allowed file type.

      See OWASP Path Traversal (https://owasp.org/www-community/attacks/Path_Traversal) and Unrestricted Upload of File with 
      Dangerous Type (https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload) for more details.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      cwe: "CWE-22"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/unzipper']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_xml_rule-node-entity-expansion"
    patterns:
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              $PARSER = new expat.Parser()
              ...
              $PARSER.write(..., <... $REQ.$QUERY ...>, ...)
          - pattern: |
              $PARSER = new expat.Parser()
              ...
              $PARSER.write(..., <... $REQ.$QUERY.$FOO ...>, ...)
          - pattern: |
              $PARSER = new expat.Parser()
              ...
              $PARSER.write(..., <... $REQ.$QUERY.$FOO.$FILE ...>, ...)
    message: |
      User controlled data in XML Parsers can result in XML Internal Entity Processing vulnerabilities like in DoS.
    languages:
      - "javascript"
    severity: "ERROR"
    metadata:
      owasp:
        - "A4:2017-XML External Entities (XXE)"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-776"
      shortDescription: "Improper restriction of recursive entity references in DTDs (XML Entity Expansion)"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/expat']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_xml_rule-node-xpath-injection"
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-either:
              - pattern: |
                  function ($REQ, $RES, ...) {...}
              - pattern: |
                  function $FUNC($REQ, $RES, ...) {...}
          - focus-metavariable: $REQ
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  $XPATH = require('xpath')
                  ...
              - pattern-inside: |
                  import { $XPATH } from 'xpath'
                  ...
              - pattern-inside: |
                  import { $K as $XPATH } from 'xpath'
                  ...
          - pattern: |
              $XPATH.parse($REQ, ...)    
          - focus-metavariable: $REQ
    pattern-sanitizers:
      - patterns:
          - pattern-either:
              - pattern: |
                  if($VALIDATION){
                  ...
                  $XPATH.parse($REQ, ...) 
                  ...
                  } 
              - pattern: |
                  $A = $VALIDATION
                  ...
                  if($A){
                  ...
                  $XPATH.parse($REQ, ...) 
                  ...
                  }
          - metavariable-pattern:
              metavariable: $VALIDATION
              pattern-either:
                - pattern: |
                    $AL.includes(...)  
                - pattern: |
                    $AL.indexOf(...) !== -1
                - pattern: |
                    $AL.find(...) !== undefined
                - pattern: |
                    $ALS.has(...)
    message: |
      Passing untrusted user input in `xpath.parse()` can result in XPATH injection
      vulnerability. This could be abused by malicious actors to execute expressions on
      on XML files to capture unauthorized information.
      To prevent XPATH injection vulnerabilities:

      - Always validate and sanitize user inputs, especially parameters
      or query strings that may influence the flow of the application.
      - Avoid directly using user input for parsing. If unavoidable, ensure
      strict validation against an allowlist. 
      - Use allowlists (lists of permitted expressions) to validate user input 
      against known, trusted expressions before performing the parse.


      Following is an example of secure validation against allowlist to prevent the vulnerability:
      ```
      // Define a list of explicitly allowed expressions for parsing
      const allowedExpr = [
          'expression1',
          'expression2',
          'expression3'
      ];

      app.get('/xml/xpath/1', (req, res) => {
          var expression = req.params.exp;
          var isAllowed = allowedExpr.includes(expression);
          let xml_string = fs.readFileSync("books.xml", "utf8");
          var doc = new dom().parseFromString(xml_string, 'text/xml');
          if (isAllowed) {
              // If the expression is allowed, proceed with the parsing
              var evaluator = xpath.parse("//"+expression);
              var nodes = evaluator.select({ node: doc });            
              res.send(nodes[0].firstChild.data);
          } else {
              res.status(400).send('Invalid expression');
          }
      });
      ```
    languages:
      - "javascript"
    severity: "ERROR"
    metadata:
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      cwe: "CWE-643"
      shortDescription: "Improper neutralization of data within XPath expressions (XPath Injection)"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/xpath']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_xml_rule-node-xxe"
    patterns:
      - pattern-either:
          - pattern-inside: |-
              function ($REQ, $RES, ...) {...}
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern: |
              $LIBXML.parseXmlString(..., <... $REQ.$QUERY.$VAR.$FILE ...>, ...)
          - pattern: |
              $LIBXML.parseXmlString(..., <... $REQ.$QUERY.$VAR ...>, ...)
          - pattern: |
              $LIBXML.parseXmlString(..., <... $REQ.$QUERY ...>, ...)
          - pattern: |
              $FOO = <... $REQ.$QUERY.$VAR.$FILE ...>; ... $LIBXML.parseXmlString(..., <... $FOO ...>, ...)
          - pattern: |
              $FOO = <... $REQ.$QUERY.$VAR ...>; ... $LIBXML.parseXmlString(..., <... $FOO ...>, ...)
          - pattern: |
              $FOO = <... $REQ.$QUERY ...>; ... $LIBXML.parseXmlString(..., <... $FOO ...>, ...)
          - pattern: |
              $LIBXML.parseXml(..., <... $REQ.$QUERY.$VAR.$FILE ...>, ...)
          - pattern: |
              $LIBXML.parseXml(..., <... $REQ.$QUERY.$VAR ...>, ...)
          - pattern: |
              $LIBXML.parseXml(..., <... $REQ.$QUERY ...>, ...)
          - pattern: |
              $FOO = <... $REQ.$QUERY.$VAR.$FILE ...>; ... $LIBXML.parseXml(..., <... $FOO ...>, ...)
          - pattern: |
              $FOO = <... $REQ.$QUERY.$VAR ...>; ... $LIBXML.parseXml(..., <... $FOO ...>, ...)
          - pattern: |
              $FOO = <... $REQ.$QUERY ...>;
              ...
              $LIBXML.parseXml(..., <... $FOO ...>, ...)
          - pattern: |
              $PARSER = new libxmljs.SaxParser()
              ...
              $PARSER.parseString(..., <... $REQ.$QUERY ...>, ...)
          - pattern: |
              $PARSER = new libxmljs.SaxParser()
              ...
              $PARSER.parseString(..., <... $REQ.$QUERY.$BAR ...>, ...)
          - pattern: |
              $PARSER = new libxmljs.SaxParser()
              ...
              $PARSER.parseString(..., <... $REQ.$QUERY.$BAR.$FILE ...>, ...)
          - pattern: |
              $PARSER = new libxmljs.SaxPushParser()
              ...
              $PARSER.push(..., <... $REQ.$QUERY ...>, ...)
          - pattern: |
              $PARSER = new libxmljs.SaxPushParser()
              ...
              $PARSER.push(..., <... $REQ.$QUERY.$FOO ...> , ...)
          - pattern: |
              $PARSER = new libxmljs.SaxPushParser()
              ...
              $PARSER.push(..., <... $REQ.$QUERY.$FOO.$FILE ...> , ...)
          - pattern: |
              $PARSER = new libxmljs.SaxParser()
              ...
              $FOO = <... $REQ.$QUERY ...>;
              ...
              $PARSER.parseString(..., <... $FOO ...>, ...)
          - pattern: |
              $PARSER = new libxmljs.SaxParser()
              ...
              $FOO = <... $REQ.$QUERY.$BAR ...>;
              ...
              $PARSER.parseString(..., <... $FOO ...>, ...)
          - pattern: |
              $PARSER = new libxmljs.SaxParser()
              ...
              $FOO = <... $REQ.$QUERY.$BAR.$FILE ...>;
              ...
              $PARSER.parseString(..., <... $FOO ...>, ...)
          - pattern: |
              $PARSER = new libxmljs.SaxPushParser()
              ...
              $FOO = <... $REQ.$QUERY ...>;
              ...
              $PARSER.push(..., <... $FOO ...>, ...)
          - pattern: |
              $PARSER = new libxmljs.SaxPushParser()
              ...
              $FOO = <... $REQ.$QUERY.$BAR ...>;
              ...
              $PARSER.push(..., <... $FOO ...> , ...)
          - pattern: |
              $PARSER = new libxmljs.SaxPushParser()
              ...
              $FOO = <... $REQ.$QUERY.$BAR.$FILE ...>;
              ...
              $PARSER.push(..., <... $FOO ...> , ...)
    message: |
      User controlled data in XML parsers can result in XML External or Internal Entity (XXE) Processing vulnerabilities
    languages:
      - "javascript"
    severity: "ERROR"
    metadata:
      owasp:
        - "A4:2017-XML External Entities (XXE)"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-611"
      shortDescription: "Improper restriction of XML external entity reference"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/libxmljs']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_xml_rule-xxe-expat"
    patterns:
      - pattern-inside: |
          require('node-expat')
          ...
      - pattern-either:
          - pattern-inside: |-
              function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              $X = function $FUNC($REQ, $RES, ...) {...}
          - pattern-inside: |-
              var $X = function $FUNC($REQ, $RES, ...) {...};
          - pattern-inside: |-
              $APP.$METHOD(..., function $FUNC($REQ, $RES, ...) {...})
      - pattern-either:
          - pattern-inside: |
              $PARSER = new $EXPAT.Parser(...)
              ...
          - pattern-inside: |
              $PARSER = new Parser(...)
              ...
      - pattern-either:
          - pattern: "$PARSER.parse(<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$PARSER.parse(<... $REQ.$BODY ...>,...)"
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PARSER.parse(<... $INPUT ...>,...)
          - pattern: |
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PARSER.parse(<... $INPUT ...>,...)
          - pattern: "$PARSER.write(<... $REQ.$QUERY.$FOO ...>,...)"
          - pattern: "$PARSER.write(<... $REQ.$BODY ...>,...)"
          - pattern: |
              $INPUT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $PARSER.write(<... $INPUT ...>,...)
          - pattern: |-
              $INPUT = <... $REQ.$BODY ...>;
              ...
              $PARSER.write(<... $INPUT ...>,...)
    message: |
      Make sure that unverified user data can not reach the XML Parser, as it can result in XML External or Internal Entity (XXE) Processing vulnerabilities.
    metadata:
      owasp:
        - "A4:2017-XML External Entities (XXE)"
        - "A05:2021-Security Misconfiguration"
      cwe: "CWE-611"
      shortDescription: "Improper restriction of XML external entity reference"
      security-severity: "CRITICAL"
      category: "security"
      related-packages: ['pkg:npm/node-expat']
      license: GNU Lesser General Public License v3.0
    severity: "ERROR"
    languages:
      - "javascript"
  - id: "rules_lgpl_javascript_xss_rule-handlebars-noescape"
    mode: taint
    pattern-sources:
      - patterns:
          - focus-metavariable: $REQ
          - pattern: function ($REQ, $RES, ...) {...}
          - pattern-either:
              - pattern-inside: |
                  $HB = require('handlebars');
                  ...
              - pattern-inside: |
                  import $HB from "handlebars";
    pattern-sinks:
      - pattern: "$HB.compile(..., {noEscape: true,...}, ...)(...)"
      - pattern: |
          var $METHOD = $HB.compile(..., {noEscape: true,...}, ...)
          ...
          $METHOD(...)
    message: |
      This application is compiling strings with `Handlebars.compile` using an insecure
      option of `{noEscape: true}`. This configuration bypasses the default behavior of 
      Handlebars, which is to escape input values to prevent Cross-Site Scripting (XSS) 
      attacks. 
      XSS attacks are a type of security breach that occurs when an attacker manages to 
      inject malicious scripts into web pages viewed by other users. These scripts can then 
      execute in the context of the victim's session, allowing the attacker to bypass access 
      controls and potentially access sensitive information or perform actions on behalf of the    
      user. It is important to encode the data depending on the specific context it is used in. 

      By default, `Handlebars.compile` escapes input values to prevent XSS attacks. 
      Consider using `Handlebars.compile` with default settings or set `{noEscape: false}` 
      to force encoding of input data.

      Example of using Handlebars.compile safely:
      ```
      var template = "This is {{target}}";
      var out = Handlebars.compile(template)({target: req.query.message});
      res.send(out);
      ```    
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      shortDescription: "Improper neutralization of script-related HTML tags in a web page (basic XSS)"
      cwe: "CWE-80"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/handlebars']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_xss_rule-handlebars-safestring"
    mode: taint
    pattern-sources:
      - patterns:
          - focus-metavariable: $REQ
          - pattern: function ($REQ, $RES, ...) {...}
    pattern-sinks:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  $HB = require('handlebars')
                  ...
              - pattern: "new $HB.SafeString(...)"
          - patterns:
              - pattern-inside: |
                  import $HB from "handlebars";
                  ...
              - pattern: "new $HB.SafeString(...)"
    pattern-sanitizers:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  $HB = require('handlebars')
                  ...
              - pattern: "$HB.escapeExpression(...)"
          - patterns:
              - pattern-inside: |
                  import $HB from "handlebars";
                  ...
              - pattern: "$HB.escapeExpression(...)"
    message: |
      This application is using a vulnerable method `Handlebars.SafeString(...)`.
      Handlebars SafeString method does not escape the data passed through it. 
      Untrusted user input passing through SafeString method can make the application 
      vulnerable to Cross-Site Scripting (XSS) attacks.

      XSS attacks are a type of security breach that occurs when an attacker manages to 
      inject malicious scripts into web pages viewed by other users. These scripts can then 
      execute in the context of the victim's session, allowing the attacker to bypass access 
      controls and potentially access sensitive information or perform actions on behalf of the    
      user. It is important to encode the data depending on the specific context it is used in. 

      Consider using the `Handlebars.escapeExpression` method to escape user input while 
      constructing SafeString to avoid potential security concerns. Following is a secure code 
      example.
      ```
        var returnObj = new Handlebars.SafeString("<h1>Handlebars safe string</h1>" +  Handlebars.escapeExpression(req.query.message))
        res.send(returnObj.string)
      ```
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      cwe: "CWE-79"
      shortDescription: "Improper neutralization of input during web page generation (Cross-site Scripting)"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/handlebars']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_xss_rule-squirrelly-autoescape"
    pattern-either:
      - patterns:
          - pattern-inside: |
              $SQLY = require('squirrelly')
              ...
          - pattern: "$SQLY.autoEscaping(false)"
      - patterns:
          - pattern-inside: |
              import $SQLY from 'squirrelly';
              ...
          - pattern: "$SQLY.autoEscaping(false)"
    message: |
      This application is rendering HTML with vulnerable 
      configurations by setting Sqrl.autoEscaping(false) in squirrelly.

      This could lead to Cross Site Scripting (XSS) if the input is malicious 
      script code and the application server is not properly validating the output.

      ```
      // safe use of squirrelly render
      var myTemplate = "<p>My Message is: {{message}}</p>"
      Sqrl.Render(myTemplate, {message: req.query.message})
      ```

      XSS is an attack that exploits a web application or system to treat user input as markup or script code. 
      It is important to encode the data depending on the specific context it is used in.

      By default, squirrelly autoEscaping(true) escapes input values to prevent XSS attacks. 
      Consider using squirrelly with default autoEscaping settings.
    languages:
      - "javascript"
    severity: "WARNING"
    metadata:
      shortDescription: "Improper neutralization of input during web page generation ('Cross-site Scripting')"
      category: "security"
      cwe: "CWE-79"
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      security-severity: "MEDIUM"
      related-packages: ['pkg:npm/squirrelly']
      license: GNU Lesser General Public License v3.0
  - id: "rules_lgpl_javascript_xss_rule-xss-disable-mustache-escape"
    pattern: "$OBJ.escapeMarkup = false"
    severity: "WARNING"
    languages:
      - "javascript"
    metadata:
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      cwe: "CWE-116"
      shortDescription: "Improper encoding or escaping of output"
      security-severity: "MEDIUM"
      category: "security"
      license: GNU Lesser General Public License v3.0
    message: |
      Markup escaping disabled. This can be used with some template engines to escape disabling of HTML entities, which can lead to XSS attacks.
    related-packages: ['pkg:npm/mustache']
  - id: "rules_lgpl_javascript_xss_rule-xss-serialize-javascript"
    pattern-either:
      - patterns:
          - pattern-inside: |
              $S = require('serialize-javascript')
              ...
          - pattern: "$S(..., {...,unsafe: true,...})"
      - patterns:
          - pattern-inside: |
              import serialize from "serialize-javascript";
              ...
          - pattern: "serialize(..., {...,unsafe: true,...})"
    message: |
      This application is serializing Javascript objects with vulnerable 
      configurations by setting `{unsafe: true}` in serialize-javascript. 

      This could lead to Cross Site Scripting (XSS) if the input was malicious 
      script code and the application server is not properly validating the output.

      ```
      // safe use of serialize-javascript
      const jsObj = serialize({
        foo: htmlResponse
        }
      );
      ```

      XSS is an attack which exploits a web application or system to treat user input as markup or script code. 
      It is important to encode the data depending on the specific context it is used in. 

      By default, serialize-javascript encodes input values to prevent XSS attacks. 
      Consider using serialize-javascript with default settings or set `{unsafe: false}` to encode
      input data.
    severity: "WARNING"
    languages:
      - "javascript"
    metadata:
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      shortDescription: "Improper neutralization of script-related HTML tags in a web page (basic XSS)"
      cwe: "CWE-80"
      security-severity: "MEDIUM"
      category: "security"
      related-packages: ['pkg:npm/serialize-javascript']
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_kotlin_other_rule-android-kotlin-webview-debug
    patterns:
      - pattern: |
          $X.setWebContentsDebuggingEnabled(true)
      - pattern-inside: |
          WebView
          ...
    message: |
      Remote WebView debugging is enabled.This can introduce security 
      risks as it allows remote debugging tools, such as Chrome DevTools, 
      to inspect and manipulate the WebView content. This can potentially 
      expose sensitive information, including user data, session tokens, 
      and other confidential data, to unauthorized parties.

      To fix this security issue, you should disable remote WebView 
      debugging in production builds of your app. Here's how you can do it:
      ```
      import WebKit

      class ViewController: UIViewController {

          override func viewDidLoad() {
              super.viewDidLoad()

              // Disable remote WebView debugging in production builds
              #if DEBUG
              WebViewConfiguration.shared().preferences.setValue(true, forKey: "developerExtrasEnabled")
              #else
              WebViewConfiguration.shared().preferences.setValue(false, forKey: "developerExtrasEnabled")
              #endif

              // Other setup code...
          }

          // Other methods...
      }
      ```

    languages:
      - kotlin
    severity: WARNING
    metadata:
      category: security
      cwe: "CWE-489"
      shortDescription: "Leftover debug code"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      security-severity: MEDIUM
      related-packages: ['pkg:maven/androidx.webkit/webkit']
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_oc_other_rule-ios-self-signed-ssl
    pattern-either:
      - pattern: canAuthenticateAgainstProtectionSpace
      - pattern: continueWithoutCredentialForAuthenticationChallenge
      - pattern: kCFStreamSSLAllowsExpiredCertificates
      - pattern: kCFStreamSSLAllowsAnyRoot
      - pattern: kCFStreamSSLAllowsExpiredRoots
      - patterns:
          - pattern: validatesSecureCertificate = $NO
          - metavariable-regex:
              metavariable: "$NO"
              regex: "NO|no"
      - patterns:
          - pattern: allowInvalidCertificates = $YES
          - metavariable-regex:
              metavariable: "$YES"
              regex: "YES|yes"
    paths:
      include:
        - "**/*.m"
    message: |
      App allows self signed or invalid SSL certificates. App is 
      vulnerable to MITM attacks. If the app does not verify the 
      authenticity of the server's SSL certificate, an attacker could 
      impersonate the server and intercept sensitive data transmitted 
      between the app and the server.
      To fix these security issues, you should ensure proper SSL 
      certificate validation in your Objective-C code. Here's how you 
      can do it:
      ```
      - (void)loadSecureURL {
        NSURL *url = [NSURL URLWithString:@"https://example.com"];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];

        // Create session configuration
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        configuration.TLSMinimumSupportedProtocol = kTLSProtocol12;

        // Create session with configuration
        NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];

        // Create data task
        NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (error) {
                NSLog(@"Error loading URL: %@", error);
                // Handle error
            } else {
                // Handle response
                NSLog(@"Response: %@", response);
            }
        }];

        // Start task
        [task resume];
      }

      ```

    languages:
      - generic
    severity: ERROR
    metadata:
      category: security
      cwe: "CWE-676"
      shortDescription: "Use of potentially dangerous function"
      owasp:
        - "A9:2017-Using Components with Known Vulnerabilities"
        - "A06:2021-Vulnerable and Outdated Components"
      security-severity: CRITICAL
      related-packages: ['pkg:cocoapods/CFNetwork']
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_oc_other_rule-ios-webview-ignore-ssl
    pattern-either:
      - pattern: allowsAnyHTTPSCertificateForHost
      - patterns:
          - pattern: loadingUnvalidatedHTTPSPage = $YES
          - metavariable-regex:
              metavariable: "$YES"
              regex: "YES|yes"
      - patterns:
          - pattern: allowsAnyHTTPSCertificate = $YES
          - metavariable-regex:
              metavariable: "$YES"
              regex: "YES|yes"
    paths:
      include:
        - "**/*.m"
    message: |
      UIWebView in App ignore SSL errors and accept any SSL Certificate. 
      App is vulnerable to MITM attacks. If the app does not verify the 
      authenticity of the server's SSL certificate, an attacker could 
      impersonate the server and intercept sensitive data transmitted 
      between the app and the server.
      To fix these security issues, you should ensure proper SSL certificate 
      validation in your Objective-C code. Here's how you can do it:
      ```
      - (void)loadSecureURL {
        NSURL *url = [NSURL URLWithString:@"https://example.com"];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        
        // Create session configuration
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        configuration.TLSMinimumSupportedProtocol = kTLSProtocol12;
        
        // Create session with configuration
        NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
        
        // Create data task
        NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (error) {
                NSLog(@"Error loading URL: %@", error);
                // Handle error
            } else {
                // Handle response
                NSLog(@"Response: %@", response);
            }
        }];
        
        // Start task
        [task resume];
      }
      ```
    languages:
      - generic
    severity: ERROR
    metadata:
      category: security
      cwe: "CWE-295"
      shortDescription: "Improper certificate validation"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      security-severity: CRITICAL
      related-packages: ['pkg:cocoapods/WebKit']
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_swift_other_rule-ios-biometric-acl
    patterns:
      - pattern-either:
          - pattern: .biometryAny
          - pattern: .userPresence
          - pattern: .touchIDAny
          - pattern: SecAccessControlCreateWithFlags(...)
    message: |
      Weak biometric ACL flag is associated with a key stored in Keychain. 
      With '.biometryAny/.userPresence/.touchIDAny' flag, an attacker with 
      the ability to add a biometry to the device can authenticate as the 
      user. It is recommended to use more specific and secure authentication 
      mechanisms like '.biometryCurrentSet' and '.touchIDCurrentSet'.

      Here's an example of how to fix the problem by using .biometryCurrentSet 
      for biometric authentication in Swift:
      ```
      import LocalAuthentication

      // Create an instance of LAContext for biometric authentication
      let context = LAContext()

      // Check if biometric authentication is available
      if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) {
        // Use biometryCurrentSet for biometric authentication
        context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, 
              localizedReason: "Authenticate with biometrics", 
              reply: { success, error in
                  if success {
                    print("Biometric authentication successful.")
                    // Proceed with authenticated actions
                  } else {
                    print("Biometric authentication failed: 
                          \(error?.localizedDescription ?? "Unknown error")")
                    // Handle authentication failure
                  }
        })
      } else {
        print("Biometric authentication not available.")
        // Fallback to alternative authentication method
      }

      ```
    languages:
      - swift
    severity: ERROR
    metadata:
      category: security
      cwe: "CWE-305"
      shortDescription: "Authentication bypass by primary weakness"
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      related-packages: ['pkg:cocoapods/LocalAuthentication']
      security-severity: CRITICAL
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_swift_other_rule-ios-dtls1-used
    patterns:
      - pattern: $Y.TLSMinimumSupportedProtocolVersion
      - pattern-inside: |
          ...
          $X = "tls_protocol_version_t.DTLSv10"
          ...
    message: |
      DTLS 1.2 should be used. Detected old version - DTLS 1.0.
      DTLS (Datagram Transport Layer Security) 1.0 suffers from 
      various security vulnerabilities and weaknesses, as it is 
      an outdated and less secure protocol compared to newer 
      versions such as DTLS 1.2 or 1.3.

      Here's an example of how to use DTLS 1.2:
      ```
      import Network

      // Create a NWConnection instance with DTLS 1.2
      let connection = NWConnection(host: NWEndpoint.Host("example.com"), port: NWEndpoint.Port("443"), using: .dtls)

      // Start the connection
      connection.start(queue: .main)

      // Handle connection state changes
      connection.stateUpdateHandler = { newState in
        switch newState {
        case .ready:
          print("Connection ready.")
        // Perform data transfer or other operations
        case .failed(let error):
          print("Connection failed with error: \(error)")
        default:
          break
        }
      }
      ```

    languages:
      - swift
    severity: WARNING
    metadata:
      category: security
      cwe: "CWE-757"
      shortDescription: "Selection of less-secure algorithm during negotiation ('algorithm downgrade')"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      security-severity: MEDIUM
      related-packages: ['pkg:cocoapods/CocoaAsyncSocket', 'pkg:cocoapods/Network']
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_swift_other_rule-ios-file-no-special
    pattern-either:
      - pattern: .noFileProtection
      - pattern: FileProtectionType.none
    message: |
      The file has no special protections associated with it.
      Using .noFileProtection or FileProtectionType.none for 
      file protection means that the file is not encrypted on disk, 
      leaving it vulnerable to unauthorized access if the device is 
      compromised or if the file is accessed outside of the app's 
      sandbox. To enhance security, it's crucial to use appropriate 
      file protection attributes based on the sensitivity of the data 
      being stored. For sensitive data, you should use file protection 
      options that encrypt the data on disk, such as 
      FileProtectionType.complete or 
      FileProtectionType.completeUnlessOpen.

      Here's an example of how to fix the problem:
      ```
      import Foundation  
      // Define the file URL
      let fileURL = URL(fileURLWithPath: "path/to/file")
      // Define data to be written to the file
      let data = "Sensitive data".data(using: .utf8)!
      // Write data to the file with complete file protection
      do {
        try data.write(to: fileURL, options: .completeFileProtection)
        print("Data written to file with complete file protection.")
      } catch {
        print("Error writing data to file: \(error)")
      }
      ```
    languages:
      - swift
    severity: ERROR
    metadata:
      category: security
      cwe: "CWE-312"
      shortDescription: "Cleartext storage of sensitive information"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      security-severity: CRITICAL
      related-packages: ['pkg:cocoapods/Foundation', 'pkg:cocoapods/KeychainAccess']
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_swift_other_rule-ios-keychain-weak-accessibility-value
    pattern-either:
      - pattern: kSecAttrAccessibleAlways
      - pattern: kSecAttrAccessibleAfterFirstUnlock
    message: |
      A key stored in the Keychain is using a weak accessibility value. 

      kSecAttrAccessibleAlways allows access to the keychain item at all 
      times, even when the device is locked. Storing sensitive data with 
      this accessibility option means that the data is accessible to anyone 
      who gains physical access to the device, regardless of whether it's 
      locked or not. This increases the risk of unauthorized access to 
      sensitive information. kSecAttrAccessibleAfterFirstUnlock allows access
      to the keychain item only after the device has been unlocked once after
      a reboot. While this provides some level of protection, the data becomes
      accessible as soon as the device is unlocked for the first time after a
      reboot. If sensitive data is stored with this accessibility option, it 
      could still be accessed by an attacker who gains physical access to the
      device before it's unlocked for the first time after a reboot.

      To mitigate these security risks, it's important to use the appropriate 
      accessibility option based on the sensitivity of the data being stored. 
      For sensitive data that should only be accessible when the device is 
      unlocked, the kSecAttrAccessibleWhenUnlocked or 
      kSecAttrAccessibleWhenUnlockedThisDeviceOnly 
      options should be used.

      Here's an example code that fixes the problem by using the 
      kSecAttrAccessibleWhenUnlocked option:
      ```
      import Foundation
      import Security

      // Define the data to be stored in the keychain
      let secretData = "superSecretData".data(using: .utf8)!

      // Create query dictionary to specify the keychain item
      let query: [String: Any] = [
                 kSecClass as String: kSecClassGenericPassword,
                 kSecAttrService as String: "com.example.myApp",
                 kSecAttrAccount as String: "userPassword",
                 kSecValueData as String: secretData,
                 kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked
      ]

      // Add the keychain item
      let status = SecItemAdd(query as CFDictionary, nil)
      if status == errSecSuccess {
        print("Secret data successfully stored in keychain.")
      } else {
        print("Error storing secret data in keychain: \(status)")
      }
      ```
    languages:
      - swift
    severity: WARNING
    metadata:
      category: security
      cwe: "CWE-305"
      shortDescription: "Authentication bypass by primary weakness"
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      security-severity: MEDIUM
      related-packages: ['pkg:cocoapods/Security']
      license: GNU Lesser General Public License v3.0
  - id: rules_lgpl_swift_other_rule-ios-tls3-not-used
    pattern-either:
      - patterns:
          - pattern: $X.TLSMinimumSupportedProtocolVersion = $VAL
          - metavariable-pattern:
              metavariable: $VAL
              pattern-either:
                - pattern: |
                    .TLSv1_0
                - pattern: |
                    .TLSv1_1
                - pattern: |
                    .TLSv1_2
    message: |
      The app uses TLS 1.0, TLS 1.1 or TLS 1.2. TLS 1.3 should be used instead.
      TLS versions 1.1 and 1.0 were deprecated by the IETF in June 2018 due to 
      a number of attacks against the vulnerable versions. Use of a deprecated 
      TLS version may result in the unauthorized retrieval of sensitive 
      information. It is strongly recommended that all TLS connections
      use TLS 1.3

      TLS 1.3 includes several security improvements over previous versions, such
      as stronger cryptographic algorithms and negotiation mechanisms, reducing 
      the risk of security vulnerabilities and attacks like BEAST and POODLE.


      Example using TLS 1.3:
      ```
      import Foundation
      // Create a URLSession configuration with TLS 1.3 support
      let configuration = URLSessionConfiguration.default
      configuration.tlsMinimumSupportedProtocol = .TLSv13
      // Create a URLSession with the custom configuration
      let session = URLSession(configuration: configuration)        
      // Define the URL to connect to
      let url = URL(string: "https://example.com")!
      // Create a data task to fetch data from the URL
      let task = session.dataTask(with: url) { data, response, error in
          // Handle response
          if let error = error {
          print("Error: \(error)")
          return
        }
        if let httpResponse = response as? HTTPURLResponse {
          print("Status code: \(httpResponse.statusCode)")
        }  
        if let data = data {
          // Process received data
          print("Received data: \(data)")
        }
      }
      // Start the data task
      task.resume()
      ```
    languages:
      - swift
    severity: ERROR
    metadata:
      category: security
      cwe: "CWE-757"
      shortDescription: "Selection of less-secure algorithm during negotiation ('algorithm downgrade')"
      owasp:
        - "A6:2017-Security Misconfiguration"
        - "A05:2021-Security Misconfiguration"
      security-severity: CRITICAL
      related-packages: ['pkg:cocoapods/Foundation']
      license: GNU Lesser General Public License v3.0
  - id: "scala_cookie_rule-CookieHTTPOnly"
    languages:
      - "scala"
    message: |
      A new cookie is created without the HttpOnly flag set. The HttpOnly flag is a directive to the
      browser to make sure that the cookie can not be red by malicious script. When a user is the
      target of a "Cross-Site Scripting", the attacker would benefit greatly from getting the session
      id for example.
    metadata:
      category: "security"
      cwe: "CWE-1004"
      shortDescription: "Sensitive Cookie Without 'HttpOnly' Flag"
      technology:
        - "scala"
      security-severity: "Low"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: MIT (c) GitLab Inc.
    pattern-either:
      - patterns:
          - pattern: |
              val $C = new javax.servlet.http.Cookie(..., ...);
              ...
              $RESP.addCookie($C);
          - pattern-not-inside: |
              val $C = new javax.servlet.http.Cookie(..., ...);
              ...
              $C.setHttpOnly(true);
              ...
              $RESP.addCookie($C);
      - pattern: "(javax.servlet.http.Cookie $C).setHttpOnly(false);"
    severity: "WARNING"
  - id: "scala_cookie_rule-CookieInsecure"
    languages:
      - "scala"
    message: |
      "A new cookie is created without the Secure flag set. The Secure flag is a
       directive to the browser to make sure that the cookie is not sent for insecure communication
      (http://)"
    metadata:
      category: "security"
      cwe: "CWE-539"
      shortDescription: "Information Exposure Through Persistent Cookies"
      technology:
        - "scala"
      security-severity: "Low"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-not-inside: |
          val $C = new javax.servlet.http.Cookie(..., ...);
          ...
          $C.setSecure(true);
          ...
          $RESP.addCookie($C);
      - pattern-either:
          - pattern: |
              val $C = new javax.servlet.http.Cookie(..., ...);
              ...
              $RESP.addCookie($C);
          - pattern: "($C:javax.servlet.http.Cookie).setSecure(false);"
    severity: "WARNING"

  - id: "scala_cookie_rule-CookiePersistent"
    languages:
      - "scala"
    message: |
      "Storing sensitive data in a persistent cookie for an extended period can lead to a breach of
      confidentiality or account compromise."
    metadata:
      category: "security"
      cwe: "CWE-614"
      shortDescription: "Sensitive Cookie in HTTPS Session Without 'Secure' Attribute"
      technology:
        - "scala"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: |
          ($C: Cookie).setMaxAge($AGE)
      - metavariable-comparison:
          comparison: "$AGE >= 31536000"
          metavariable: "$AGE"
    severity: "WARNING"
  - id: "scala_cookie_rule-CookieUsage"
    languages:
      - "scala"
    message: |
      The information stored in a custom cookie should not be sensitive or related to the session.
      In most cases, sensitive data should only be stored in session and referenced by the user's
      session cookie.
    metadata:
      category: "security"
      cwe: "CWE-614"
      shortDescription: "Sensitive Cookie in HTTPS Session Without 'Secure' Attribute"
      technology:
        - "scala"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          def $FUNC(..., $REQ: HttpServletRequest, ...): $TYPE = {
            ...
          }
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  for ($C <- $REQ.getCookies) {
                      ...
                  }
              - pattern-either:
                  - pattern: "$C.getName"
                  - pattern: "$C.getValue"
                  - pattern: "$C.getPath"
          - pattern: "($C: Cookie).getName()"
          - pattern: "($C: Cookie).getValue"
          - pattern: "($C: Cookie).getPath"
    severity: "WARNING"
  - id: "scala_cookie_rule-HttpResponseSplitting"
    languages:
      - "scala"
    message: |
      When an HTTP request contains unexpected CR and LF characters, the server may respond with an
      output stream that is interpreted as two different HTTP responses (instead of one). An attacker
      can control the second response and mount attacks such as cross-site scripting and cache
      poisoning attacks.
    metadata:
      category: "security"
      cwe: "CWE-113"
      shortDescription: "Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting')"
      technology:
        - "scala"
      security-severity: "High"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sanitizers:
      - patterns:
          - pattern-inside: |-
              $STR.replaceAll("$REPLACE_CHAR", "$REPLACE");
          - pattern: "$STR"
          - metavariable-regex:
              metavariable: "$REPLACE_CHAR"
              regex: "(.*\\\\r\\\\n.*)"
          - metavariable-regex:
              metavariable: "$REPLACE"
              regex: "(?!(\\\\r\\\\n))"
      - pattern: "org.owasp.encoder.Encode.forUriComponent(...)"
      - pattern: "org.owasp.encoder.Encode.forUri(...)"
      - pattern: "java.net.URLEncoder.encode(..., $CHARSET)"
    pattern-sinks:
      - pattern: "new javax.servlet.http.Cookie(\"$KEY\", ...)"
      - pattern: "($C:javax.servlet.http.Cookie).setValue(...)"
    pattern-sources:
      - pattern: "($REQ: javax.servlet.http.HttpServletRequest).getParameter(...)"
    severity: "WARNING"
  - id: "scala_cookie_rule-RequestParamToCookie"
    languages:
      - "scala"
    message: |
      This code constructs an HTTP Cookie using an untrusted HTTP parameter. If this cookie is added
      to an HTTP response, it will allow a HTTP response splitting vulnerability. See
      http://en.wikipedia.org/wiki/HTTP_response_splitting for more information.
    metadata:
      category: "security"
      cwe: "CWE-113"
      shortDescription: "Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting')"
      technology:
        - "scala"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sanitizers:
      - patterns:
          - pattern-inside: |-
              $STR.replaceAll("$REPLACE_CHAR", "$REPLACE");
          - pattern: "$STR"
          - metavariable-regex:
              metavariable: "$REPLACE_CHAR"
              regex: "(.*\\\\r\\\\n.*)"
          - metavariable-regex:
              metavariable: "$REPLACE"
              regex: "(?!(\\\\r\\\\n))"
      - pattern: "org.owasp.encoder.Encode.forUriComponent(...)"
      - pattern: "org.owasp.encoder.Encode.forUri(...)"
      - pattern: "java.net.URLEncoder.encode(..., $CHARSET)"
    pattern-sinks:
      - pattern: "new javax.servlet.http.Cookie(\"$KEY\", ...);"
      - patterns:
          - pattern-inside: |
              $C = new javax.servlet.http.Cookie("$KEY", ...);
              ...
          - pattern: "$C.setValue(...);"
    pattern-sources:
      - pattern: "($REQ: HttpServletRequest).getParameter(...);"
    severity: "ERROR"
  - id: "scala_cookie_rule-RequestParamToHeader"
    languages:
      - "scala"
    message: |
      This code directly writes an HTTP parameter to an HTTP header, which allows for a HTTP
      response splitting vulnerability. See http://en.wikipedia.org/wiki/HTTP_response_splitting for
      more information.
    metadata:
      category: "security"
      cwe: "CWE-113"
      shortDescription: "Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting')"
      technology:
        - "scala"
      security-severity: "High"
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sanitizers:
      - patterns:
          - pattern-inside: |-
              $STR.replaceAll("$REPLACE_CHAR", "$REPLACE");
          - pattern: "$STR"
          - metavariable-regex:
              metavariable: "$REPLACE_CHAR"
              regex: "(.*\\\\r\\\\n.*)"
          - metavariable-regex:
              metavariable: "$REPLACE"
              regex: "(?!(\\\\r\\\\n))"
      - pattern: "org.owasp.encoder.Encode.forUriComponent(...)"
      - pattern: "org.owasp.encoder.Encode.forUri(...)"
      - pattern: "java.net.URLEncoder.encode(..., $CHARSET)"
    pattern-sinks:
      - pattern: "($RES: HttpServletResponse).setHeader(\"$KEY\", ...);"
      - pattern: "($RES: HttpServletResponse).addHeader(\"$KEY\", ...);"
      - pattern: "($WRP: HttpServletResponseWrapper).setHeader(\"$KEY\", ...);"
      - pattern: "($WRP: HttpServletResponseWrapper).addHeader(\"$KEY\", ...);"
    pattern-sources:
      - pattern: "($REQ: HttpServletRequest).getParameter(...);"
    severity: "ERROR"
  - id: "scala_cookie_rule-TrustBoundaryViolation"
    languages:
      - "scala"
    message: |
      A trust boundary can be thought of as line drawn through a program. On one side
      of the line, data is untrusted. On the other side of the line, data is assumed
      to be trustworthy. The purpose of validation logic is to allow data to safely
      cross the trust boundary - to move from untrusted to trusted. A trust boundary
      violation occurs when a program blurs the line between what is trusted and what
      is untrusted. By combining trusted and untrusted data in the same data
      structure, it becomes easier for programmers to mistakenly trust unvalidated
      data.
    metadata:
      category: "security"
      cwe: "CWE-501"
      shortDescription: "Trust Boundary Violation"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - patterns:
              - pattern: "($H: HttpServletRequest). ... .setAttribute($ARG1, $ARG2)"
              - pattern-not: "($H: HttpServletRequest). ... .setAttribute(\"...\", \"...\")"
          - patterns:
              - pattern: "($H: HttpServletRequest). ... .putValue($ARG1, $ARG2)"
              - pattern-not: "($H: HttpServletRequest). ... .putValue(\"...\", \"...\")"
    severity: "WARNING"
  - id: "scala_cors_rule-PermissiveCORS"
    languages:
      - "scala"
    message: |
      Prior to HTML5, Web browsers enforced the Same Origin Policy which ensures that in order for
      JavaScript to access the contents of a Web page, both the JavaScript and the Web page must
      originate from the same domain. Without the Same Origin Policy, a malicious website could serve
      up JavaScript that loads sensitive information from other websites using a client's
      credentials, cull through it, and communicate it back to the attacker. HTML5 makes it possible
      for JavaScript to access data across domains if a new HTTP header called
      Access-Control-Allow-Origin is defined. With this header, a Web server defines which other
      domains are allowed to access its domain using cross-origin requests. However, caution should
      be taken when defining the header because an overly permissive CORS policy will allow a
      malicious application to communicate with the victim application in an inappropriate way,
      leading to spoofing, data theft, relay and other attacks.
    metadata:
      category: "security"
      cwe: "CWE-942"
      shortDescription: "Permissive Cross-domain Policy with Untrusted Domains"
      technology:
        - "scala"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
    pattern-either:
      - patterns:
          - pattern-either:
              - pattern: "($RESP:javax.servlet.http.HttpServletResponse).setHeader(\"$HEADER\", \"$VAL\")"
              - pattern: "($RESP:javax.servlet.http.HttpServletResponse).addHeader(\"$HEADER\", \"$VAL\")"
          - metavariable-regex:
              metavariable: "$HEADER"
              regex: "(?i)(Access-Control-Allow-Origin)"
          - metavariable-regex:
              metavariable: "$VAL"
              regex: "(\\*|null)"
      - patterns:
          - pattern-inside: |
              $REQVAL = ($REQ: javax.servlet.http.HttpServletRequest).getParameter(...)
              ...
          - pattern-either:
              - pattern-inside: |-
                  ($RESP:javax.servlet.http.HttpServletResponse).setHeader("$HEADER", $REQVAL)
              - pattern-inside: |-
                  ($RESP:javax.servlet.http.HttpServletResponse).addHeader("$HEADER", $REQVAL)
      - patterns:
          - pattern-either:
              - pattern-inside: |-
                  ($RESP:javax.servlet.http.HttpServletResponse).setHeader("$HEADER",($REQ: javax.servlet.http.HttpServletRequest).getParameter(...))
              - pattern-inside: |-
                  ($RESP:javax.servlet.http.HttpServletResponse).addHeader("$HEADER",($REQ: javax.servlet.http.HttpServletRequest).getParameter(...))
    severity: "ERROR"
  - id: "scala_cors_rule-PermissiveCORSInjection"
    languages:
      - "java"
    message: |
      Prior to HTML5, Web browsers enforced the Same Origin Policy which ensures that in order for
      JavaScript to access the contents of a Web page, both the JavaScript and the Web page must
      originate from the same domain. Without the Same Origin Policy, a malicious website could serve
      up JavaScript that loads sensitive information from other websites using a client's
      credentials, cull through it, and communicate it back to the attacker. HTML5 makes it possible
      for JavaScript to access data across domains if a new HTTP header called
      Access-Control-Allow-Origin is defined. With this header, a Web server defines which other
      domains are allowed to access its domain using cross-origin requests. However, caution should
      be taken when defining the header because an overly permissive CORS policy will allow a
      malicious application to communicate with the victim application in an inappropriate way,
      leading to spoofing, data theft, relay and other attacks.
    metadata:
      category: "security"
      cwe: "CWE-942"
      shortDescription: "Permissive Cross-domain Policy with Untrusted Domains"
      technology:
        - "java"
      security-severity: "Low"
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern: "(HttpServletResponse $RES).setHeader(\"$HEADER\", ...)"
              - pattern: "(HttpServletResponse $RES).addHeader(\"$HEADER\", ...)"
          - metavariable-regex:
              metavariable: "$HEADER"
              regex: "(?i)(Access-Control-Allow-Origin)"
    pattern-sources:
      - pattern: "(HttpServletRequest $REQ).getParameter(...)"
    severity: "ERROR"
  - id: "scala_crypto_rule-BlowfishKeySize"
    languages:
      - "scala"
    message: |
      A small key size makes the ciphertext vulnerable to brute force attacks. At least 128 bits of
      entropy should be used when generating the key if use of Blowfish is required.
    metadata:
      category: "security"
      cwe: "CWE-326"
      shortDescription: "Inadequate Encryption Strength"
      technology:
        - "scala"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          $KEYGEN = javax.crypto.KeyGenerator.getInstance("Blowfish", ...);
          ...
          $KEYGEN.init($KEY_SIZE);
      - metavariable-comparison:
          comparison: "$KEY_SIZE < 128"
          metavariable: "$KEY_SIZE"
    severity: "WARNING"

  - id: "scala_crypto_rule-CipherDESInsecure"
    languages:
      - "scala"
    message: |
      DES is considered strong ciphers for modern applications. Currently, NIST recommends the usage
      of AES block ciphers instead of DES.
    metadata:
      category: "security"
      cwe: "CWE-326"
      shortDescription: "Inadequate Encryption Strength"
      technology:
        - "scala"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |-
          javax.crypto.Cipher.getInstance("$ALG")
      - metavariable-regex:
          metavariable: "$ALG"
          regex: "^(DES)/.*"
    severity: "WARNING"
  - id: "scala_crypto_rule-CipherDESedeInsecure"
    languages:
      - "scala"
    message: |
      Triple DES (also known as 3DES or DESede) is considered strong ciphers for modern
      applications. NIST recommends the usage of AES block ciphers instead of 3DES.
    metadata:
      category: "security"
      cwe: "CWE-326"
      shortDescription: "Inadequate Encryption Strength"
      technology:
        - "scala"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |-
          javax.crypto.Cipher.getInstance("$ALG")
      - metavariable-regex:
          metavariable: "$ALG"
          regex: "^(DESede)/.*"
    severity: "WARNING"
  - id: "scala_crypto_rule-CipherECBMode"
    languages:
      - "scala"
    message: |
      An authentication cipher mode which provides better confidentiality of the encrypted data
      should be used instead of Electronic Code Book (ECB) mode, which does not provide good
      confidentiality. Specifically, ECB mode produces the same output for the same input each time.
      This allows an attacker to intercept and replay the data.
    metadata:
      category: "security"
      cwe: "CWE-326"
      shortDescription: "Inadequate Encryption Strength"
      technology:
        - "scala"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |-
          javax.crypto.Cipher.getInstance("...")
      - pattern-regex: "(AES|DES(ede)?)(/ECB/*)"
    severity: "ERROR"
  - id: "scala_crypto_rule-CipherIntegrity"
    languages:
      - "scala"
    message: |
      The ciphertext produced is susceptible to alteration by an adversary. This mean that the
      cipher provides no way to detect that the data has been tampered with. If the ciphertext can be
      controlled by an attacker, it could be altered without detection.
    metadata:
      category: "security"
      cwe: "CWE-353"
      shortDescription: "Missing Support for Integrity Check"
      technology:
        - "scala"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |-
          javax.crypto.Cipher.getInstance("...")
      - pattern-either:
          - pattern-regex: "(/CBC/PKCS5Padding)"
          - pattern-regex: "(AES|DES(ede)?)(/ECB/*)"
          - pattern-regex: "(AES|DES(ede)?)(/CBC/*)"
          - pattern-regex: "(AES|DES(ede)?)(/OFB/*)"
          - pattern-regex: "(AES|DES(ede)?)(/CTR/*)"
      - pattern-not-regex: ".*/(CCM|CWC|OCB|EAX|GCM)/.*"
      - pattern-not-regex: "^(RSA)/.*"
      - pattern-not-regex: "^(ECIES)$"
    severity: "ERROR"
  - id: "scala_crypto_rule-CipherPaddingOracle"
    languages:
      - "scala"
    message: |
      This specific mode of CBC with PKCS5Padding is susceptible to padding oracle attacks. An
      adversary could potentially decrypt the message if the system exposed the difference between
      plaintext with invalid padding or valid padding. The distinction between valid and invalid
      padding is usually revealed through distinct error messages being returned for each condition.
    metadata:
      category: "security"
      cwe: "CWE-696"
      shortDescription: "Incorrect Behavior Order"
      technology:
        - "scala"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |-
          javax.crypto.Cipher.getInstance("...")
      - pattern-regex: "(/CBC/PKCS5Padding)"
      - pattern-not-regex: "^(RSA)/.*"
      - pattern-not-regex: "^(ECIES)$"
    severity: "ERROR"
  - id: "scala_crypto_rule-CustomMessageDigest"
    languages:
      - "scala"
    message: |
      Implementing a custom MessageDigest is error-prone. National Institute of Standards and
      Technology(NIST) recommends the use of SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, or
      SHA-512/256.
    metadata:
      category: "security"
      cwe: "CWE-327"
      shortDescription: "Use of a Broken or Risky Cryptographic Algorithm"
      technology:
        - "scala"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: |
          class $CLAZZ extends java.security.MessageDigest(...) {
            ...
          }
    severity: "WARNING"
  - id: "scala_crypto_rule-DefaultHTTPClient"
    patterns:
      - pattern: "new org.apache.http.impl.client.DefaultHttpClient(...)"
    message: |
      DefaultHttpClient with default constructor is not compatible with TLS 1.2
    languages:
      - "scala"
    severity: "WARNING"
    metadata:
      shortDescription: "Inadequate encryption strength"
      category: "security"
      cwe: "CWE-326"
      technology:
        - "scala"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
  - id: "scala_crypto_rule-HazelcastSymmetricEncryption"
    languages:
      - "scala"
    message: |
      The network communications for Hazelcast is configured to use a symmetric cipher (probably DES
      or Blowfish). Those ciphers alone do not provide integrity or secure authentication. The use of
      asymmetric encryption is preferred.
    metadata:
      category: "security"
      cwe: "CWE-326"
      shortDescription: "Inadequate Encryption Strength"
      technology:
        - "scala"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: "new com.hazelcast.config.SymmetricEncryptionConfig()"
    severity: "WARNING"
  - id: "scala_crypto_rule-InsufficientKeySizeRsa"
    languages:
      - "scala"
    message: |
      Detected an insufficient key size for DSA. NIST recommends a key size
      of 2048 or higher.
    metadata:
      category: "security"
      cwe: "CWE-326"
      shortDescription: "Inadequate Encryption Strength"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  $GEN = KeyPairGenerator.getInstance($ALG, ...);
                  ...
              - pattern-either:
                  - pattern: "$VAR.initialize($SIZE, ...)"
                  - pattern: "new java.security.spec.RSAKeyGenParameterSpec($SIZE, ...)"
              - metavariable-comparison:
                  comparison: "$SIZE < 2048"
                  metavariable: "$SIZE"
              - metavariable-regex:
                  metavariable: "$ALG"
                  regex: "\"(RSA|DSA)\""
    severity: "WARNING"
  - id: "scala_crypto_rule-NullCipher"
    languages:
      - "scala"
    message: |
      The NullCipher implements the Cipher interface by returning ciphertext identical to the
      supplied plaintext. In a few contexts, such as testing, a NullCipher may be appropriate. Avoid
      using the NullCipher. Its accidental use can introduce a significant confidentiality risk.
    metadata:
      category: "security"
      cwe: "CWE-327"
      shortDescription: "Use of a Broken or Risky Cryptographic Algorithm"
      technology:
        - "scala"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    pattern: "new javax.crypto.NullCipher()"
    severity: "WARNING"
  - id: "scala_crypto_rule-RsaNoPadding"
    languages:
      - "scala"
    message: |
      The software uses the RSA algorithm but does not incorporate Optimal Asymmetric
      Encryption Padding (OAEP), which might weaken the encryption.
    metadata:
      cwe: "CWE-780"
      shortDescription: "Use of RSA Algorithm without OAEP"
      security-severity: "Medium"
      category: "security"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: "javax.crypto.Cipher.getInstance(\"$ALG\",...)"
      - metavariable-regex:
          metavariable: "$ALG"
          regex: ".*NoPadding.*"
    severity: "WARNING"
  - id: "scala_crypto_rule-WeakMessageDigest"
    languages:
      - "scala"
    message: |
      DES is considered strong ciphers for modern applications. Currently, NIST recommends the usage
      of AES block ciphers instead of DES.
    metadata:
      category: "security"
      cwe: "CWE-326"
      shortDescription: "Inadequate Encryption Strength"
      technology:
        - "scala"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: "MessageDigest.getInstance(\"$ALG\", ...)"
          - pattern: "Signature.getInstance(\"$ALG\", ...)"
      - metavariable-regex:
          metavariable: "$ALG"
          regex: "(.*(MD5|MD4|MD2|SHA1|SHA-1).*)"
    severity: "WARNING"
  - id: "scala_crypto_rule-WeakTLSProtocol"
    languages:
      - "scala"
    message: |
      A HostnameVerifier that accept any host are often use because of certificate
      reuse on many hosts. As a consequence, this is vulnerable to Man-in-the-middleattacks
      attacks since the client will trust any certificate.
    metadata:
      category: "security"
      cwe: "CWE-295"
      shortDescription: "Improper Certificate Validation"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: "new org.apache.http.impl.client.DefaultHttpClient()"
          - pattern: "javax.net.ssl.SSLContext.getInstance(\"SSL\")"
          - patterns:
              - pattern-inside: |
                  import javax.net.ssl._
                  ...
              - pattern: "SSLContext.getInstance(\"SSL\")"
    severity: "WARNING"
  - id: "scala_endpoint_rule-JaxRsEndpoint"
    languages:
      - "scala"
    message: |
      This method is part of a REST Web Service (JSR311). The security of this web service should be
      analyzed. For example:
      - Authentication, if enforced, should be tested.
      - Access control, if enforced, should be tested.
      - The inputs should be tracked for potential vulnerabilities.
      - The communication should ideally be over SSL.
      - If the service supports writes (e.g., via POST), its vulnerability to CSRF should be
      investigated.
    metadata:
      category: "security"
      cwe: "CWE-348"
      shortDescription: "Use of less trusted source"
      technology:
        - "scala"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.glassfish.jersey.core/jersey-server']
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sinks:
      - pattern: "<...$VAR...>"
    pattern-sources:
      - patterns:
          - pattern-inside: |
              @javax.ws.rs.Path("...")
              def $FUNC(..., $VAR: $TYPE, ...) = ...
          - pattern: "$VAR"
    severity: "INFO"
  - id: "scala_endpoint_rule-JaxWsEndpoint"
    languages:
      - "scala"
    message: |
      This method is part of a SOAP Web Service (JSR224). The security of this web service should be
      analyzed. For example:
      - Authentication, if enforced, should be tested.
      - Access control, if enforced, should be tested.
      - The inputs should be tracked for potential vulnerabilities.
      - The communication should ideally be over SSL.
    metadata:
      category: "security"
      owasp:
        - "A7:2017-Cross-Site Scripting (XSS)"
        - "A03:2021-Injection"
      cwe: "CWE-348"
      related-packages: ['pkg:maven/org.glassfish.jaxb/jaxb-runtime']
      shortDescription: "Use of less trusted source"
      technology:
        - "scala"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sinks:
      - pattern: "<...$VAR...>"
    pattern-sources:
      - patterns:
          - pattern-inside: |
              @javax.jws.WebMethod(...)
              def $FUNC(..., $VAR: $TYPE, ...) = ...
          - pattern: "$VAR"
    severity: "INFO"

  - id: "scala_endpoint_rule-UnencryptedSocket"
    languages:
      - "scala"
    message: |
      Beyond using an SSL socket, you need to make sure your use of SSLSocketFactory
      does all the appropriate certificate validation checks to make sure you are not
      subject to man-in-the-middle attacks. Please read the OWASP Transport Layer
      Protection Cheat Sheet for details on how to do this correctly.
    metadata:
      cwe: "CWE-319"
      shortDescription: "Cleartext transmission of sensitive information"
      security-severity: "Info"
      owasp:
        - "A3:2017-Sensitive Data Exposure"
        - "A02:2021-Cryptographic Failures"
      category: "security"
      related-packages: ['pkg:maven/org.glassfish.jersey.core/jersey-server']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: "new java.net.Socket(...)"
    severity: "WARNING"
  - id: "scala_endpoint_rule-UnvalidatedRedirect"
    languages:
      - "scala"
    message: |
      Unvalidated redirects occur when an application redirects a user to a
      destination URL specified by a user supplied parameter that is not validated.
      Such vulnerabilities can be used to facilitate phishing attacks.
    metadata:
      category: "security"
      cwe: "CWE-601"
      shortDescription: "URL Redirection to Untrusted Site ('Open Redirect')"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.glassfish.jersey.core/jersey-server']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - patterns:
              - pattern: "($REQ: HttpServletResponse).sendRedirect(...)"
              - pattern-not: "($REQ: HttpServletResponse).sendRedirect(\"...\")"
          - patterns:
              - pattern: "($REQ: HttpServletResponse).addHeader(...)"
              - pattern-not: "($REQ: HttpServletResponse).addHeader(\"...\", \"...\")"
          - patterns:
              - pattern: "($REQ: HttpServletResponse).encodeURL(...)"
              - pattern-not: "($REQ: HttpServletResponse).encodeURL(\"...\")"
          - patterns:
              - pattern: "($REQ: HttpServletResponse).encodeRedirectUrl(...)"
              - pattern-not: "($REQ: HttpServletResponse).encodeRedirectUrl(\"...\")"
    severity: "ERROR"
  - id: "scala_endpoint_rule-WeakHostNameVerification"
    languages:
      - "scala"
    message: |
      A HostnameVerifier that accept any host are often use because of certificate
      reuse on many hosts. As a consequence, this is vulnerable to Man-in-the-middle
      attacks since the client will trust any certificate.
    metadata:
      category: "security"
      cwe: "CWE-295"
      shortDescription: "Improper Certificate Validation"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.glassfish.jersey.core/jersey-server']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  class $V extends HostnameVerifier {
                    ...
                  }
              - pattern-either:
                  - pattern: "def verify(...) = true"
                  - pattern: |
                      def verify(...) = {
                        return true
                      }
          - patterns:
              - pattern-inside: |
                  class $V extends X509TrustManager {
                    ...
                  }
              - pattern-either:
                  - pattern: "def checkClientTrusted(...): Unit = {}"
                  - pattern: "def checkServerTrusted(...): Unit = {}"
                  - pattern: "def checkClientTrusted(...) = {}"
                  - pattern: "def checkServerTrusted(...) = {}"
                  - pattern: "def getAcceptedIssuers(): Array[X509Certificate] = null"
                  - pattern: "def getAcceptedIssuers(): Array[X509Certificate] = {}"
    severity: "WARNING"
  - id: "scala_file_rule-FileUploadFileName"
    languages:
      - "scala"
    message: |
      The filename provided by the FileUpload API can be tampered with by the client to reference
      unauthorized files. The provided filename should be properly validated to ensure it's properly
      structured, contains no unauthorized path characters (e.g., / \), and refers to an authorized
      file.
    metadata:
      category: "security"
      cwe: "CWE-22"
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      technology:
        - "scala"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.apache.commons/commons-fileupload']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: |
          def $FUNC (..., $REQ: HttpServletRequest, ... ) = {
            ...
            val $FILES = ($SFU: ServletFileUpload).parseRequest($REQ)
            ...
            for ($FILE <- $FILES.asScala) {
              ...
            }
          }
      - pattern: "$ITEM.getName()"
    severity: "WARNING"
  - id: "scala_file_rule-FilenameUtils"
    languages:
      - "scala"
    message: |
      A file is opened to read its content. The filename comes from an input
      parameter. If an unfiltered parameter is passed to this file API, files from an
      arbitrary filesystem location could be read.
    metadata:
      category: "security"
      cwe: "CWE-22"
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      technology:
        - "scala"
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.commons/commons-io']
      license: MIT (c) GitLab Inc.
    pattern-either:
      - patterns:
          - pattern-inside: |
              import org.apache.commons.io.FilenameUtils._
              ...
          - pattern-either:
              - pattern: "normalize(...)"
              - pattern: "getExtension(...)"
              - pattern: "isExtensions(...)"
              - pattern: "isExtension(...)"
              - pattern: "getName(...)"
              - pattern: "getBaseName(...)"
      - patterns:
          - pattern-either:
              - pattern: "org.apache.commons.io.FilenameUtils.normalize(...)"
              - pattern: "org.apache.commons.io.FilenameUtils.getExtension(...)"
              - pattern: "org.apache.commons.io.FilenameUtils.isExtensions(...)"
              - pattern: "org.apache.commons.io.FilenameUtils.isExtension(...)"
              - pattern: "org.apache.commons.io.FilenameUtils.getName(...)"
              - pattern: "org.apache.commons.io.FilenameUtils.getBaseName(...)"
    severity: "WARNING"
  - id: "scala_form_rule-FormValidate"
    languages:
      - "scala"
    message: |
      Form inputs should have minimal input validation. Preventive validation helps provide defense
      in depth against a variety of risks.
    metadata:
      category: "security"
      cwe: "CWE-1289"
      shortDescription: "Improper validation of unsafe equivalence in input"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.apache.struts/struts2-core']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          class $CLASS extends $SC {
            ...
          }
      - metavariable-regex:
          metavariable: "$SC"
          regex: "(ActionForm|ValidatorForm)"
      - pattern-not: "public void validate() { ... }"
    severity: "WARNING"
  - id: "scala_inject_rule-AWSQueryInjection"
    languages:
      - "scala"
    message: |
      Constructing SimpleDB queries containing user input can allow an attacker to view unauthorized
      records.
    metadata:
      category: "security"
      cwe: "CWE-943"
      shortDescription: "Improper Neutralization of Special Elements in Data Query Logic"
      technology:
        - "scala"
      security-severity: "Info"
      related-packages: ['pkg:maven/com.amazonaws/aws-java-sdk-simpledb']
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sinks:
      - pattern: "new com.amazonaws.services.simpledb.model.SelectRequest($QUERY, ...);"
      - patterns:
          - pattern-inside: |
              $DB.select(($SR: com.amazonaws.services.simpledb.model.SelectRequest).withSelectExpression($QUERY,...));
          - pattern: "$QUERY"
          - metavariable-pattern:
              metavariable: "$DB"
              pattern-either:
                - pattern: "($DB: com.amazonaws.services.simpledb.AmazonSimpleDB)"
                - pattern: "($DB: com.amazonaws.services.simpledb.AmazonSimpleDBClient)"
    pattern-sources:
      - patterns:
          - pattern-inside: |
              def $FUNC(..., $REQ: HttpServletRequest, ...): $TYPE = {
                ...
              }
          - pattern: "$REQ"
      - patterns:
          - pattern-inside: |
              def $FUNC(..., $X: $TYPE, ...): $RET_TYPE = {
                ...
                $QUERY = <...$X...>
                ...
              }
          - pattern: "$QUERY"
    severity: "ERROR"
  - id: "scala_inject_rule-BeanPropertyInjection"
    languages:
      - "scala"
    message: |
      An attacker can set arbitrary bean properties that can compromise system integrity. An
      attacker can leverage this functionality to access special bean properties like
      class.classLoader that will allow them to override system properties and potentially execute
      arbitrary code.
    metadata:
      category: "security"
      cwe: "CWE-15"
      shortDescription: "External Control of System or Configuration Setting"
      technology:
        - "scala"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.apache.commons/commons-beanutils']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |-
          def $FUNC(..., $REQ: HttpServletRequest, ...): $TYPE = { ... }
      - pattern-either:
          - pattern: |
              $MAP.put(..., $REQ.getParameter(...))
              ...
              $BEAN_UTIL.populate(..., $MAP)
          - pattern: |
              while (...) {
                  ...
                  $MAP.put(..., $REQ.getParameterValues(...). ...)
              }
              ...
              $BEAN_UTIL.populate(..., $MAP)
      - metavariable-pattern:
          metavariable: "$BEAN_UTIL"
          pattern-either:
            - pattern: "(BeanUtilsBean $B)"
            - pattern: "new BeanUtilsBean()"
            - pattern: "org.apache.commons.beanutils.BeanUtils"
    severity: "ERROR"
  - id: "scala_inject_rule-CLRFInjectionLogs"
    languages:
      - "scala"
    message: |
      When data from an untrusted source is put into a logger and not neutralized correctly, an
      attacker could forge log entries or include malicious content. Inserted false entries could be
      used to skew statistics, distract the administrator or even to implicate another party in the
      commission of a malicious act. If the log file is processed automatically, the attacker can
      render the file unusable by corrupting the format of the file or injecting unexpected
      characters. An attacker may also inject code or other commands into the log file and take
      advantage of a vulnerability in the log processing utility (e.g. command injection or XSS).
    metadata:
      category: "security"
      cwe: "CWE-93"
      shortDescription: "Improper Neutralization of CRLF Sequences ('CRLF Injection')"
      technology:
        - "scala"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sanitizers:
      - patterns:
          - pattern-inside: |-
              $STR.replaceAll("$REPLACE_CHAR", "$REPLACE");
          - pattern: "$STR"
          - metavariable-regex:
              metavariable: "$REPLACE_CHAR"
              regex: "(.*\\\\r\\\\n.*)"
          - metavariable-regex:
              metavariable: "$REPLACE"
              regex: "(?!(\\\\r\\\\n))"
      - pattern: "org.owasp.encoder.Encode.forUriComponent(...)"
      - pattern: "org.owasp.encoder.Encode.forUri(...)"
      - pattern: "java.net.URLEncoder.encode(..., $CHARSET)"
    pattern-sinks:
      - patterns:
          - patterns:
              - pattern: "$LOGGER.$METHOD(...,<...$TAINTED...>,...)"
              - focus-metavariable: "$TAINTED"
          - metavariable-regex:
              metavariable: "$METHOD"
              regex: "(log|logp|logrb|entering|exiting|fine|finer|finest|info|debug|trace|warn|warning|config|error|severe)"
          - metavariable-pattern:
              metavariable: "$LOGGER"
              pattern-either:
                - pattern: "Logger"
                - pattern: "log"
                - pattern: "logger"
                - pattern: "org.pmw.tinylog.Logger"
                - pattern: "org.apache.log4j.Logger"
                - pattern: "org.apache.logging.log4j.Logger"
                - pattern: "org.slf4j.Logger"
                - pattern: "org.apache.commons.logging.Log"
                - pattern: "java.util.logging.Logger"
    pattern-sources:
      - patterns:
          - pattern-inside: |
              def $FUNC(..., $REQ: HttpServletRequest, ...) : $TYPE = {
                ...
              }
          - pattern: "$REQ.getParameter(...)"
    severity: "ERROR"
  - id: "scala_inject_rule-CommandInjection"
    languages:
      - "scala"
    message: |
      The highlighted API is used to execute a system command. If unfiltered input is passed to this
      API, it can lead to arbitrary command execution.
    metadata:
      category: "security"
      cwe: "CWE-78"
      shortDescription: "Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')"
      technology:
        - "scala"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.apache.commons/commons-exec']
      license: MIT (c) GitLab Inc.
    pattern-either:
      - patterns:
          - pattern-inside: |
              def $FUNC(..., $PARAM: String, ...): $TYPE = {
                ...
              }
          - pattern-inside: |
              val $RT = Runtime.getRuntime
              ...
          - pattern-either:
              - pattern: "$RT.exec($PARAM)"
              - pattern: |
                  var $CMDARR = new Array[String]("$SHELL",...,$PARAM,...)
                  ...
                  $RT.exec($CMDARR,...)
              - pattern: "$RT.exec(Array[String](\"$SHELL\",...,$PARAM,...), ...)"
              - pattern: "$RT.exec(java.util.String.format(\"...\", ...,$PARAM,...))"
              - pattern: "$RT.exec(($A: String) + ($B: String))"
          - metavariable-regex:
              metavariable: "$SHELL"
              regex: "(/.../)?(sh|bash|ksh|csh|tcsh|zsh)$"
          - pattern-not: "$RT.exec(\"...\",\"...\",\"...\",...)"
          - pattern-not: "$RT.exec(new Array[String](\"...\",\"...\",\"...\",...),...)"
      - patterns:
          - pattern-inside: |
              def $FUNC(...,$PARAM: String, ...): $TYPE = {
                ...
              }
          - pattern-inside: |
              val $PB = new ProcessBuilder()
              ...
          - pattern-either:
              - pattern: "$PB.command($PARAM,...)"
              - patterns:
                  - pattern-either:
                      - pattern: "$PB.command(\"$SHELL\",...,$PARAM,...)"
                      - pattern: |
                          var $CMDARR = java.util.Arrays.asList("$SHELL",...,$PARAM,...)
                          ...
                          $PB.command($CMDARR,...)
                      - pattern: "$PB.command(java.util.Arrays.asList(\"$SHELL\",...,$PARAM,...),...)"
                      - pattern: "$PB.command(java.util.String.format(\"...\", ...,$PARAM,...))"
                      - pattern: "$PB.command(($A: String) + ($B: String))"
                  - metavariable-regex:
                      metavariable: "$SHELL"
                      regex: "(/.../)?(sh|bash|ksh|csh|tcsh|zsh)$"
          - pattern-not: "$PB.command(\"...\",\"...\",\"...\",...)"
          - pattern-not: |
              $PB.command(java.util.Arrays.asList("...","...","...",...))
    severity: "WARNING"
  - id: "scala_inject_rule-CustomInjection"
    languages:
      - "scala"
    message: |
      The method identified is susceptible to injection. The input should be validated and properly
      escaped.
    metadata:
      category: "security"
      cwe: "CWE-89"
      shortDescription: "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')"
      technology:
        - "scala"
      security-severity: "Low"
      related-packages: ['pkg:maven/org.apache.commons/commons-dbcp2']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern-inside: |
              val $ST = connection.createStatement
              ...
      - pattern-either:
          - pattern: |
              val $QUERY = ... + $VAR + ...
              ...
              $ST.executeQuery($QUERY)
          - pattern: |
              val $QUERY = ... + $VAR
              ...
              $ST.executeQuery($QUERY)
          - pattern: |
              val $QUERY = String.format("...",...,$VAR,...)
              ...
              $ST.executeQuery($QUERY)
          - pattern: "$ST.executeQuery(($SB: StringBuilder).toString())"
          - pattern: "$ST.executeQuery(... + $VAR + ...)"
          - pattern: "$ST.executeQuery(... + $VAR)"
          - pattern: "$ST.executeQuery(...,String.format(\"...\",...,$VAR,...), ...)"
    severity: "WARNING"
  - id: "scala_inject_rule-CustomInjectionSQLString"
    languages:
      - "scala"
    message: |
      The method identified is susceptible to injection. The input should be validated and properly
      escaped.
    metadata:
      category: "security"
      cwe: "CWE-89"
      shortDescription: "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')"
      technology:
        - "scala"
      security-severity: "High"
      related-packages: ['pkg:maven/org.apache.commons/commons-dbcp2']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          def $FOO(..., $SQLIN: String, ...): $TYPE = {
            ...
          }
      - pattern-either:
          - pattern: |
              "$SQL_STR" + $SQLIN
          - pattern: "String.format(\"$SQL_STR\", ... + $SQLIN + ...)"
          - pattern: |
              "$SQL_STR".concat(...)
          - pattern: "(StringBuilder $BUILDER). ... .append(\"$SQL_STR\")"
          - patterns:
              - pattern-inside: |
                  StringBuilder $BUILDER = new StringBuilder(... + "$SQL_STR" + ...);
                  ...
              - pattern: "$BUILDER.append(...)"
              - pattern-not: "$BUILDER.append(\"...\")"
          - patterns:
              - pattern-inside: |
                  $QUERY = "$SQL_STR";
                  ...
              - pattern: "$QUERY += ..."
      - metavariable-regex:
          metavariable: "$SQL_STR"
          regex: "(?i)(select|insert|create|update|alter|delete|drop)\\b"
    severity: "WARNING"
  - id: "scala_inject_rule-ELInjection"
    languages:
      - "scala"
    message: |
      An expression is built with a dynamic value. The source of the value(s) should be verified to
      avoid that unfiltered values fall into this risky code evaluation.
    metadata:
      category: "security"
      cwe: "CWE-94"
      shortDescription: "Improper Control of Generation of Code ('Code Injection')"
      technology:
        - "scala"
      security-severity: "High"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          import javax.el._
          ...
      - pattern-either:
          - pattern-inside: |
              def $FUNC(..., $EXPR: String, ...) : $TYPE = {
                ...
              }
          - pattern-inside: |
              def $FUNC(..., $EXPR: String, ...) = {
                ...
              }
      - pattern-either:
          - pattern: "$X.createValueExpression(..., $EXPR, ...)"
          - pattern: "$X.createMethodExpression(..., $EXPR, ...)"
    severity: "WARNING"
  - id: "scala_inject_rule-FileDisclosure"
    languages:
      - "scala"
    message: |
      Constructing a server-side redirect path with user input could allow an
      attacker to download application binaries (including application classes or
      jar files) or view arbitrary files within protected directories.
    metadata:
      category: "security"
      cwe: "CWE-552"
      shortDescription: "Files or Directories Accessible to External Parties"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sinks:
      - patterns:
          - pattern: "new org.springframework.web.servlet.ModelAndView($FST)"
          - pattern: "$FST"
      - patterns:
          - pattern: "new org.springframework.web.servlet.ModelAndView($FST, $SND)"
          - pattern: "$FST"
      - patterns:
          - pattern: "new org.springframework.web.servlet.ModelAndView($FST, $SND, $TRD)"
          - pattern: "$FST"
      - patterns:
          - pattern: "new org.apache.struts.action.ActionForward($FST)"
          - pattern: "$FST"
      - patterns:
          - pattern: "new org.apache.struts.action.ActionForward($FST, $SND)"
          - pattern: "$FST"
      - patterns:
          - pattern: "new org.apache.struts.action.ActionForward($FST, $SND, $TRD)"
          - pattern: "$SND"
      - patterns:
          - pattern: "new org.apache.struts.action.ActionForward($FST, $SND, $TRD)"
          - pattern: "$TRD"
      - patterns:
          - pattern-inside: |
              $ACTION = new org.apache.struts.action.ActionForward()
              ...
          - pattern: "$ACTION.setPath(...)"
      - patterns:
          - pattern-inside: |
              $MVC = new org.springframework.web.servlet.ModelAndView()
              ...
          - pattern: "$MVC.setViewName(...);"
      - patterns:
          - pattern-inside: |
              $REQ = $HTTP.getRequestDispatcher(...)
              ...
          - pattern-either:
              - pattern: "$REQ.include($FST, $SND)"
              - pattern: "$REQ.forward($FST, $SND)"
    pattern-sources:
      - pattern: "($VAR: javax.servlet.http.HttpServletRequest).getParameter(...)"
    severity: "ERROR"
  - id: "scala_inject_rule-HttpParameterPollution"
    languages:
      - "scala"
    message: |
      Concatenating unvalidated user input into a URL can allow an attacker to override the value of
      a request parameter. Attacker may be able to override existing parameter values, inject a new
      parameter or exploit variables out of a direct reach. HTTP Parameter Pollution (HPP) attacks
      consist of injecting encoded query string delimiters into other existing parameters. If a web
      application does not properly sanitize the user input, a malicious user may compromise the
      logic of the application to perform either client-side or server-side attacks.
    metadata:
      category: "security"
      cwe: "CWE-88"
      shortDescription: "Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')"
      technology:
        - "scala"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sanitizers:
      - pattern: "java.net.URLEncoder.encode(...)"
      - pattern: "com.google.common.net.UrlEscapers.urlPathSegmentEscaper().escape(...)"
    pattern-sinks:
      - pattern: "new org.apache.http.client.methods.HttpGet(...)"
      - pattern: "new org.apache.commons.httpclient.methods.GetMethod(...)"
      - pattern: "($GM: org.apache.commons.httpclient.methods.GetMethod).setQueryString(...)"
    pattern-sources:
      - pattern: "($REQ: HttpServletRequest ).getParameter(...)"
    severity: "ERROR"
  - id: "scala_inject_rule-LDAPInjection"
    languages:
      - "scala"
    message: |
      Just like SQL, all inputs passed to an LDAP query need to be passed in safely. Unfortunately,
      LDAP doesn't have prepared statement interfaces like SQL. Therefore, the primary defense
      against LDAP injection is strong input validation of any untrusted data before including it in
      an LDAP query.
    metadata:
      category: "security"
      cwe: "CWE-90"
      shortDescription: "Improper Neutralization of Special Elements used in an LDAP Query ('LDAP Injection')"
      technology:
        - "scala"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern-inside: |
              def $FUNC(..., $VAR: String, ...): $TYPE = {
                ...
              }
          - pattern-inside: |
              def $FUNC(..., $X: String, ...): $TYPE = {
                ...
                $VAR = ... + $X;
                ...
              }
      - pattern-either:
          - pattern: "($P: java.util.Properties).put($KEY, $VAR)"
          - pattern: "$CTX.lookup(..., $VAR, ...)"
          - pattern: "$CTX.search(..., $VAR, ...)"
          - pattern: "$CTX.list(..., $VAR, ...)"
      - metavariable-pattern:
          metavariable: "$CTX"
          pattern-either:
            - pattern: "($CTX: javax.naming.directory.DirContext)"
            - pattern: "($CTX: javax.naming.directory.Context)"
            - pattern: "($CTX: javax.naming.Context)"
            - pattern: "($CTX: javax.naming.directory.InitialDirContext)"
            - pattern: "($CTX: javax.naming.ldap.LdapContext)"
            - pattern: "($CTX: com.unboundid.ldap.sdk.LDAPConnection)"
            - pattern: "($CTX: javax.naming.event.EventDirContext)"
            - pattern: "($CTX: com.sun.jndi.ldap.LdapCtx)"
            - pattern: "($CTX: org.springframework.ldap.core.LdapTemplate)"
            - pattern: "($CTX: org.springframework.ldap.core.LdapOperations)"
    severity: "WARNING"
  - id: "scala_inject_rule-OgnlInjection"
    patterns:
      - pattern-either:
          - pattern-inside: |
              def $FUNC(..., $VAR: String, ...): $TYPE = {
                ...
              }
          - pattern-inside: |
              def $FUNC(..., $VAR: Map[$K,$V], ...): $TYPE = {
                ...
              }
          - pattern-inside: |
              def $FUNC(..., $VAR: java.util.HashMap[$K,$V], ...): $TYPE = {
                ...
              }
      - pattern-either:
          - pattern: "com.opensymphony.xwork2.util.TextParseUtil.translateVariables(..., $VAR, ...)"
          - pattern: "com.opensymphony.xwork2.util.TextParseUtil.translateVariablesCollection(..., $VAR, ...)"
          - pattern: "com.opensymphony.xwork2.util.TextParseUtil.shallBeIncluded(..., $VAR, ...)"
          - pattern: "com.opensymphony.xwork2.util.TextParseUtil.commaDelimitedStringToSet(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.TextParser).evaluate(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.OgnlTextParser).evaluate(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).getGetMethod(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).getSetMethod(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).getField(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).setProperties(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).setProperty(...,$VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).getValue(...,$VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlReflectionProvider).setValue(...,$VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).getGetMethod(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).getSetMethod(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).getField(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).setProperties(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).setProperty(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).getValue(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.util.reflection.ReflectionProvider).setValue(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).setProperties(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).setProperty(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).getValue(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).setValue(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).callMethod(..., $VAR, ...)"
          - pattern: "($P:com.opensymphony.xwork2.ognl.OgnlUtil).compile(..., $VAR, ...)"
          - pattern: "($P:org.apache.struts2.util.VelocityStrutsUtil).evaluate(...)"
          - pattern: "org.apache.struts2.util.StrutsUtil.findString(...)"
          - pattern: "org.apache.struts2.util.StrutsUtil.findValue(..., $VAL)"
          - pattern: "org.apache.struts2.util.StrutsUtil.getText(...)"
          - pattern: "org.apache.struts2.util.StrutsUtil.translateVariables(...)"
          - pattern: "org.apache.struts2.util.StrutsUtil.makeSelectList(..., $VAR, ...)"
          - pattern: "($T:org.apache.struts2.views.jsp.ui.OgnlTool).findValue(..., $VAR, ...)"
          - pattern: "($V:com.opensymphony.xwork2.util.ValueStack).findString(...)"
          - pattern: "($V:com.opensymphony.xwork2.util.ValueStack).findValue(..., $VAR, ...)"
          - pattern: "($V:com.opensymphony.xwork2.util.ValueStack).setValue(..., $VAR, ...)"
          - pattern: "($V:com.opensymphony.xwork2.util.ValueStack).setParameter(..., $VAR, ...)"
    message: |
      "A expression is built with a dynamic value. The source of the value(s) should be verified to
      avoid that unfiltered values fall into this risky code evaluation."
    languages:
      - "scala"
    severity: "WARNING"
    metadata:
      shortDescription: "Expression injection (OGNL)"
      category: "security"
      cwe: "CWE-917"
      technology:
        - "scala"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
  - id: "scala_inject_rule-PathTraversalIn"
    languages:
      - "scala"
    message: |
      A file is opened to read its content. The filename comes from an input parameter. If an
      unfiltered parameter is passed to this file API, files from an arbitrary filesystem location
      could be read. This rule identifies potential path traversal vulnerabilities. In many cases,
      the constructed file path cannot be controlled by the user.
    metadata:
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      category: "security"
      cwe: "CWE-22"
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      technology:
        - "scala"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  def $FUNC(...,$ARGS: Array[String], ...): $TYPE = {
                  ...
                  }
              - pattern-inside: |
                  $VAR = $ARGS($IDX)
                  ...
          - pattern-inside: |
              def $FUNC(...,$VAR: String, ...): $TYPE = {
              ...
              }
      - pattern-not-inside: |
          ...
          org.apache.commons.io.FilenameUtils.getName($VAR)
          ...
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  $U = new java.net.URI($VAR)
                  ...
              - pattern-either:
                  - pattern: "new java.io.File($U)"
                  - pattern: "java.nio.file.Paths.get($U)"
          - pattern: "new java.io.RandomAccessFile(..., $VAR,...)"
          - pattern: "new java.io.FileReader(<...$VAR...>, ...)"
          - pattern: "new javax.activation.FileDataSource(..., $VAR, ...)"
          - pattern: "new java.io.FileInputStream(..., $VAR, ...)"
          - pattern: "new java.io.File(<...$VAR...>, ...)"
          - pattern: "java.nio.file.Paths.get(...,$VAR,...)"
          - pattern: "java.io.File.createTempFile(...,$VAR, ...)"
          - pattern: "java.io.File.createTempDirectory(...,$VAR,...)"
          - pattern: "java.nio.file.Files.createTempFile(..., $VAR, ...)"
          - pattern: "java.nio.file.Files.createTempDirectory(..., $VAR, ...)"
          - pattern: "scala.io.Source.from(<...$VAR...>)"
          - pattern: "scala.io.Source.fromFile(<...$VAR...>)"
          - pattern: "scala.io.Source.fromString(<...$VAR...>)"
    severity: "ERROR"
  - id: "scala_inject_rule-PathTraversalOut"
    languages:
      - "scala"
    message: |
      A file is opened to write to its contents. The filename comes from an input parameter. If an
      unfiltered parameter is passed to this file API, files at an arbitrary filesystem location
      could be modified. This rule identifies potential path traversal vulnerabilities. In many
      cases, the constructed file path cannot be controlled by the user.
    metadata:
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      category: "security"
      cwe: "CWE-22"
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      technology:
        - "scala"
      security-severity: "High"
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sanitizers:
      - pattern: "org.apache.commons.io.FilenameUtils.getName(...)"
    pattern-sinks:
      - patterns:
          - pattern-inside: |-
              new java.io.FileWriter($PATH, ...)
          - pattern: "$PATH"
      - patterns:
          - pattern-inside: |-
              new java.io.FileOutputStream($PATH, ...)
          - pattern: "$PATH"
    pattern-sources:
      - patterns:
          - pattern-inside: |
              def $FUNC(..., $ARGS: Array[String], ...): $TYPE = {
                ...
              }
          - pattern: "$ARGS[$IDX]"
      - patterns:
          - pattern-inside: |
              def $FUNC(..., $VAR: String, ...): $TYPE = {
                ...
              }
          - pattern: "$VAR"
    severity: "WARNING"
  - id: "scala_inject_rule-SpotbugsPathTraversalAbsolute"
    languages:
      - "scala"
    message: |
      "The software uses an HTTP request parameter to construct a pathname that should be within a
      restricted directory, but it does not properly neutralize absolute path sequences such as
      "/abs/path" that can resolve to a location that is outside of that directory. See
      http://cwe.mitre.org/data/definitions/36.html for more information."
    metadata:
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      category: "security"
      cwe: "CWE-22"
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      technology:
        - "scala"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sanitizers:
      - pattern: "org.apache.commons.io.FilenameUtils.getName(...)"
    pattern-sinks:
      - patterns:
          - pattern-inside: |
              $U = new java.net.URI($VAR)
          - pattern-either:
              - pattern-inside: |-
                  new java.io.File($U)
              - pattern-inside: |-
                  java.nio.file.Paths.get($U)
          - pattern: "$VAR"
      - patterns:
          - pattern-inside: |-
              new java.io.RandomAccessFile($INPUT,...)
          - pattern: "$INPUT"
      - pattern: "new java.io.FileReader(...)"
      - pattern: "new javax.activation.FileDataSource(...)"
      - pattern: "new java.io.FileInputStream(...)"
      - pattern: "new java.io.File(...)"
      - pattern: "java.nio.file.Paths.get(...)"
      - pattern: "java.io.File.createTempFile(...)"
      - pattern: "java.io.File.createTempDirectory(...)"
      - pattern: "java.nio.file.Files.createTempFile(...)"
      - pattern: "java.nio.file.Files.createTempDirectory(...)"
      - patterns:
          - pattern-inside: |-
              new java.io.FileWriter($PATH, ...)
          - pattern: "$PATH"
      - patterns:
          - pattern-inside: |-
              new java.io.FileOutputStream($PATH, ...)
          - pattern: "$PATH"
    pattern-sources:
      - pattern: "($REQ: HttpServletRequest ).getParameter(...)"
    severity: "WARNING"
  - id: "scala_inject_rule-SpotbugsPathTraversalRelative"
    languages:
      - "scala"
    message: |
      "The software uses an HTTP request parameter to construct a pathname that should be within a
      restricted directory, but it does not properly neutralize sequences such as ".." that can
      resolve to a location that is outside of that directory. See
      http://cwe.mitre.org/data/definitions/23.html for more information."
    metadata:
      owasp:
        - "A5:2017-Broken Access Control"
        - "A01:2021-Broken Access Control"
      category: "security"
      cwe: "CWE-22"
      shortDescription: "Improper limitation of a pathname to a restricted directory ('Path Traversal')"
      technology:
        - "scala"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sanitizers:
      - pattern: "org.apache.commons.io.FilenameUtils.getName(...)"
    pattern-sinks:
      - patterns:
          - pattern-inside: |
              $U = new java.net.URI($VAR)
          - pattern-either:
              - pattern-inside: |-
                  new java.io.File($U)
              - pattern-inside: |-
                  java.nio.file.Paths.get($U)
          - pattern: "$VAR"
      - patterns:
          - pattern-inside: |-
              new java.io.RandomAccessFile($INPUT,...)
          - pattern: "$INPUT"
      - pattern: "new java.io.FileReader(...)"
      - pattern: "new javax.activation.FileDataSource(...)"
      - pattern: "new java.io.FileInputStream(...)"
      - pattern: "new java.io.File(...)"
      - pattern: "java.nio.file.Paths.get(...)"
      - pattern: "java.io.File.createTempFile(...)"
      - pattern: "java.io.File.createTempDirectory(...)"
      - pattern: "java.nio.file.Files.createTempFile(...)"
      - pattern: "java.nio.file.Files.createTempDirectory(...)"
      - patterns:
          - pattern-inside: |-
              new java.io.FileWriter($PATH, ...)
          - pattern: "$PATH"
      - patterns:
          - pattern-inside: |-
              new java.io.FileOutputStream($PATH, ...)
          - pattern: "$PATH"
    pattern-sources:
      - patterns:
          - pattern-inside: |
              $P = ($REQ: HttpServletRequest ).getParameter(...);
              ...
          - pattern-either:
              - pattern: "$P + ..."
              - pattern: "... + $P"
    severity: "WARNING"
  - id: "scala_inject_rule-SqlInjection"
    languages:
      - "scala"
    message: |
      The input values included in SQL queries need to be passed in safely. Bind
      variables in prepared statements can be used to easily mitigate the risk of
      SQL injection.
    metadata:
      category: "security"
      cwe: "CWE-89"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      shortDescription: "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-not-inside: |
          $ARG = ...
          ...
      - pattern-not-inside: |
          object $CLAZZ {
            ...
            $ARG = ...
            ...
          }
      - pattern-not-inside: |
          class $CLAZZ {
            ...
            $ARG = ...
            ...
          }
      - pattern-either:
          - patterns:
              - pattern: "($PM:javax.jdo.PersistenceManager).newQuery(<...$ARG...>)"
              - pattern-not: "($PM:javax.jdo.PersistenceManager).newQuery(\"...\")"
          - patterns:
              - pattern: "($PM:javax.jdo.PersistenceManager).newQuery(..., <...$ARG...>)"
              - pattern-not: "($PM:javax.jdo.PersistenceManager).newQuery(..., \"...\")"
          - patterns:
              - pattern: "($Q: javax.jdo.Query).setFilter(<...$ARG...>)"
              - pattern-not: "($Q: javax.jdo.Query).setFilter(\"...\")"
          - patterns:
              - pattern: "($Q: javax.jdo.Query).setGrouping(<...$ARG...>)"
              - pattern-not: "($Q: javax.jdo.Query).setGrouping(\"...\")"
          - patterns:
              - pattern: "($Q: javax.jdo.Query).setGrouping(<...$ARG...>)"
              - pattern-not: "($Q: javax.jdo.Query).setGrouping(\"...\")"
          - patterns:
              - pattern: "($H: org.hibernate.criterion.Restrictions).sqlRestriction(<...$ARG...>, ...)"
              - pattern-not: "($H: org.hibernate.criterion.Restrictions).sqlRestriction(\"...\", ...)"
          - patterns:
              - pattern: "($S: org.hibernate.Session).createQuery(<...$ARG...>, ...)"
              - pattern-not: "($S: org.hibernate.Session).createQuery(\"...\", ...)"
          - patterns:
              - pattern: "($S: org.hibernate.Session).createSQLQuery(<...$ARG...>, ...)"
              - pattern-not: "($S: org.hibernate.Session).createSQLQuery(\"...\", ...)"
          - patterns:
              - pattern: "($S: java.sql.Statement).executeQuery(<...$ARG...>, ...)"
              - pattern-not: "($S: java.sql.Statement).createSQLQuery(\"...\", ...)"
          - patterns:
              - pattern: "($S: java.sql.Statement).execute(<...$ARG...>, ...)"
              - pattern-not: "($S: java.sql.Statement).execute(\"...\", ...)"
          - patterns:
              - pattern: "($S: java.sql.Statement).executeUpdate(<...$ARG...>, ...)"
              - pattern-not: "($S: java.sql.Statement).executeUpdate(\"...\", ...)"
          - patterns:
              - pattern: "($S: java.sql.Statement).executeLargeUpdate(<...$ARG...>, ...)"
              - pattern-not: "($S: java.sql.Statement).executeLargeUpdate(\"...\", ...)"
          - patterns:
              - pattern: "($S: java.sql.Statement).addBatch(<...$ARG...>, ...)"
              - pattern-not: "($S: java.sql.Statement).addBatch(\"...\", ...)"
          - patterns:
              - pattern: "($S: java.sql.PreparedStatement).executeQuery(<...$ARG...>, ...)"
              - pattern-not: "($S: java.sql.PreparedStatement).executeQuery(\"...\", ...)"
          - patterns:
              - pattern: "($S: java.sql.PreparedStatement).execute(<...$ARG...>, ...)"
              - pattern-not: "($S: java.sql.PreparedStatement).execute(\"...\", ...)"
          - patterns:
              - pattern: "($S: java.sql.PreparedStatement).executeUpdate(<...$ARG...>, ...)"
              - pattern-not: "($S: java.sql.PreparedStatement).executeUpdate(\"...\", ...)"
          - patterns:
              - pattern: "($S: java.sql.PreparedStatement).executeLargeUpdate(<...$ARG...>, ...)"
              - pattern-not: "($S: java.sql.PreparedStatement).executeLargeUpdate(\"...\", ...)"
          - patterns:
              - pattern: "($S: java.sql.PreparedStatement).addBatch(<...$ARG...>, ...)"
              - pattern-not: "($S: java.sql.PreparedStatement).addBatch(\"...\", ...)"
          - patterns:
              - pattern: "($S: java.sql.Connection).prepareCall(<...$ARG...>, ...)"
              - pattern-not: "($S: java.sql.Connection).prepareCall(\"...\", ...)"
          - patterns:
              - pattern: "($S: java.sql.Connection).prepareStatement(<...$ARG...>, ...)"
              - pattern-not: "($S: java.sql.Connection).prepareStatement(\"...\", ...)"
          - patterns:
              - pattern: "($S: java.sql.Connection).nativeSQL(<...$ARG...>, ...)"
              - pattern-not: "($S: java.sql.Connection).nativeSQL(\"...\", ...)"
          - patterns:
              - pattern: "new org.springframework.jdbc.core.PreparedStatementCreatorFactory(<...$ARG...>, ...)"
              - pattern-not: "new org.springframework.jdbc.core.PreparedStatementCreatorFactory(\"...\", ...)"
          - patterns:
              - pattern: "(org.springframework.jdbc.core.PreparedStatementCreatorFactory $F).newPreparedStatementCreator(<...$ARG...>,
                  ...)"
              - pattern-not: "(org.springframework.jdbc.core.PreparedStatementCreatorFactory $F).newPreparedStatementCreator(\"\
                  ...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).batchUpdate(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).batchUpdate(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).execute(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).execute(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).query(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).query(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForList(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).queryForList(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForMap(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).queryForMap(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForObject(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).queryForObject(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForObject(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).queryForObject(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForRowSet(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).queryForRowSet(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForInt(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).queryForInt(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).queryForLong(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).queryForLong(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcOperations).update(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcOperations).update(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).batchUpdate(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).batchUpdate(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).execute(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).execute(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).query(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).query(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForList(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForList(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForMap(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForMap(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForObject(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForObject(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForRowSet(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForRowSet(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForInt(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForInt(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForLong(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).queryForLong(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.springframework.jdbc.core.JdbcTemplate).update(<...$ARG...>, ...)"
              - pattern-not: "($O: org.springframework.jdbc.core.JdbcTemplate).update(\"...\", ...)"
          - patterns:
              - pattern: "($O: io.vertx.sqlclient.SqlClient).query(<...$ARG...>, ...)"
              - pattern-not: "($O: io.vertx.sqlclient.SqlClient).query(\"...\", ...)"
          - patterns:
              - pattern: "($O: io.vertx.sqlclient.SqlClient).preparedQuery(<...$ARG...>, ...)"
              - pattern-not: "($O: io.vertx.sqlclient.SqlClient).preparedQuery(\"...\", ...)"
          - patterns:
              - pattern: "($O: io.vertx.sqlclient.SqlConnection).prepare(<...$ARG...>, ...)"
              - pattern-not: "($O: io.vertx.sqlclient.SqlConnection).prepare(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.apache.turbine.om.peer.BasePeer).executeQuery(<...$ARG...>, ...)"
              - pattern-not: "($O: org.apache.turbine.om.peer.BasePeer).executeQuery(\"...\", ...)"
          - patterns:
              - pattern: "($O: org.apache.torque.util.BasePeer).executeQuery(<...$ARG...>, ...)"
              - pattern-not: "($O: org.apache.torque.util.BasePeer).executeQuery(\"...\", ...)"
          - patterns:
              - pattern: "($O: javax.persistence.EntityManager).createQuery(<...$ARG...>, ...)"
              - pattern-not: "($O: javax.persistence.EntityManager).createQuery(\"...\", ...)"
          - patterns:
              - pattern: "($O: javax.persistence.EntityManager).createNativeQuery(<...$ARG...>, ...)"
              - pattern-not: "($O: javax.persistence.EntityManager).createNativeQuery(\"...\", ...)"
          - patterns:
              - pattern: "anorm.SQL(<...$ARG...>)"
              - pattern-not: "anorm.SQL(\"...\")"
          - patterns:
              - pattern-inside: |
                  import anorm._
                  ...
              - pattern: "SQL(<...$ARG...>)"
              - pattern-not: "SQL(\"...\")"
    severity: "ERROR"
  - id: "scala_ldap_rule-AnonymousLDAP"
    languages:
      - "scala"
    message: |
      Without proper access control, executing an LDAP statement that contains a
      user-controlled value can allow an attacker to abuse poorly configured LDAP
      context
    metadata:
      category: "security"
      cwe: "CWE-358"
      shortDescription: "Improperly implemented security check for standard"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.springframework.ldap/spring-ldap-core']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          import javax.naming.Context;
          ...
      - pattern: "$ENV.put(Context.SECURITY_AUTHENTICATION, \"none\");"
    severity: "WARNING"
  - id: "scala_ldap_rule-EntryPoisoning"
    languages:
      - "scala"
    message: |
      Without proper access control, executing an LDAP statement that contains a
      user-controlled value can allow an attacker to abuse poorly configured LDAP
      context
    metadata:
      category: "security"
      cwe: "CWE-358"
      shortDescription: "Improperly implemented security check for standard"
      security-severity: "High"
      related-packages: ['pkg:maven/org.springframework.ldap/spring-ldap-core']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: "new javax.naming.directory.SearchControls($SCOPE, $CLIMIT, $TLIMIT, $ATTR, true, $DEREF)"
    severity: "ERROR"
  - id: "scala_password_rule-ConstantDBPassword"
    patterns:
      - pattern: "java.sql.DriverManager.getConnection($URI, $USR, \"...\");"
    message: |
      A potential hard-coded password was identified in a database connection string.
      Passwords should not be stored directly in code
      but loaded from secure locations such as a Key Management System (KMS).

      The purpose of using a Key Management System is so access can be audited and keys easily
      rotated
      in the event of a breach. By hardcoding passwords, it will be extremely difficult to determine
      when or if, a key is compromised.

      The recommendation on which KMS to use depends on the environment the application is running
      in:

      - For Google Cloud Platform consider [Cloud Key Management](https://cloud.google.com/kms/docs)
      - For Amazon Web Services consider [AWS Key Management](https://aws.amazon.com/kms/)
      - For on premise or other alternatives to cloud providers, consider [Hashicorp's
      Vault](https://www.vaultproject.io/)
      - For other cloud providers, please see their documentation
    metadata:
      category: "security"
      cwe: "CWE-259"
      shortDescription: "Use of Hard-coded Password"
      technology:
        - "scala"
      security-severity: "Critical"
      related-packages: ['pkg:maven/org.springframework/spring-jdbc']
      license: MIT (c) GitLab Inc.
    severity: "ERROR"
    languages:
      - "scala"
  - id: "scala_password_rule-EmptyDBPassword"
    patterns:
      - pattern: "java.sql.DriverManager.getConnection($URI, $USR, \"\");"
    message: |
      The application does not provide authentication when communicating a database
      server. It is strongly recommended that the database server be configured with
      authentication and restrict what queries users can execute.

      Please see your database server's documentation on how to configure a password.

      Additionally, passwords should not be stored directly in code
      but loaded from secure locations such as a Key Management System (KMS).

      The purpose of using a Key Management System is so access can be audited and keys easily
      rotated
      in the event of a breach. By hardcoding passwords, it will be extremely difficult to determine
      when or if, a key is compromised.

      The recommendation on which KMS to use depends on the environment the application is running
      in:

      - For Google Cloud Platform consider [Cloud Key Management](https://cloud.google.com/kms/docs)
      - For Amazon Web Services consider [AWS Key Management](https://aws.amazon.com/kms/)
      - For on premise or other alternatives to cloud providers, consider [Hashicorp's
      Vault](https://www.vaultproject.io/)
      - For other cloud providers, please see their documentation
    metadata:
      category: "security"
      cwe: "CWE-259"
      shortDescription: "Use of Hard-coded Password"
      technology:
        - "scala"
      security-severity: "Critical"
      related-packages: ['pkg:maven/org.springframework/spring-jdbc']
      license: MIT (c) GitLab Inc.
    languages:
      - "scala"
    severity: "ERROR"
  - id: "scala_password_rule-HardcodePassword"
    languages:
      - "scala"
    message: |
      A potential hard-coded password was identified in the source code.
      Passwords should not be stored directly in code
      but loaded from secure locations such as a Key Management System (KMS).

      The purpose of using a Key Management System is so access can be audited and keys easily
      rotated
      in the event of a breach. By hardcoding passwords, it will be extremely difficult to determine
      when or if, a key is compromised.

      The recommendation on which KMS to use depends on the environment the application is running
      in:

      - For Google Cloud Platform consider [Cloud Key Management](https://cloud.google.com/kms/docs)
      - For Amazon Web Services consider [AWS Key Management](https://aws.amazon.com/kms/)
      - For on premise or other alternatives to cloud providers, consider [Hashicorp's
      Vault](https://www.vaultproject.io/)
      - For other cloud providers, please see their documentation
    metadata:
      category: "security"
      cwe: "CWE-259"
      shortDescription: "Use of Hard-coded Password"
      technology:
        - "scala"
      security-severity: "High"
      related-packages: ['pkg:maven/org.springframework/spring-jdbc']
      license: MIT (c) GitLab Inc.
    pattern-either:
      - pattern: "java.security.KeyStore.PasswordProtection(\"...\".toCharArray())"
      - pattern: "java.security.KeyStore.getInstance(...).load(..., \"...\".toCharArray())"
      - pattern: "($KS: java.security.KeyStore).load(..., \"...\".toCharArray())"
      - pattern: "KeyManagerFactory.getInstance(...).init(..., \"...\".toCharArray())"
      - pattern: "($KMF: KeyManagerFactory).init(..., \"...\".toCharArray())"
      - pattern: "PBEKeySpec(\"...\", ...)"
      - pattern: "PasswordAuthentication(\"...\", \"...\")"
      - pattern: "($CB: PasswordCallback).setPassword(\"...\")"
      - pattern: "KerberosKey(...,\"...\",...)"
      - pattern: "java.sql.DriverManager.getConnection(..., \"...\")"
      - pattern: "io.vertx.ext.web.handler.CSRFHandler.create(..., \"...\")"
      - pattern: "$S.setPassword(\"...\")"
    severity: "ERROR"
  - id: "scala_perm_rule-DangerousPermissions"
    pattern-either:
      - pattern: |
          $RUNVAR = new RuntimePermission("createClassLoader");
          ...
          ($PC: PermissionCollection).add($RUNVAR);
      - pattern: |
          $REFVAR = new ReflectPermission("suppressAccessChecks");
          ...
          ($PC: PermissionCollection).add($REFVAR);
      - pattern: "($PC: PermissionCollection).add(new ReflectPermission (\"suppressAccessChecks\"))"
      - pattern: "($PC: PermissionCollection).add(new RuntimePermission(\"createClassLoader\"))"
    languages:
      - "scala"
    message: |
      Do not grant dangerous combinations of permissions.
    metadata:
      shortDescription: "Insecure inherited permissions"
      category: "security"
      cwe: "CWE-277"
      confidence: "HIGH"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.springframework.ldap/spring-ldap-core']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "scala_perm_rule-OverlyPermissiveFilePermissionInline"
    languages:
      - "scala"
    message: |
      Overly permissive file permission
    metadata:
      category: "security"
      confidence: "HIGH"
      cwe: "CWE-732"
      shortDescription: "Incorrect Permission Assignment for Critical Resource"
      security-severity: "High"
      related-packages: ['pkg:maven/org.apache.commons/commons-io']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern: "java.nio.file.Files.setPosixFilePermissions(..., java.nio.file.attribute.PosixFilePermissions.fromString(\"\
              $PERM_STRING\"));"
          - pattern: |
              $PERMISSIONS = java.nio.file.attribute.PosixFilePermissions.fromString("$PERM_STRING");
              ...
              java.nio.file.Files.setPosixFilePermissions(..., $PERMISSIONS);
      - metavariable-regex:
          metavariable: "$PERM_STRING"
          regex: "[rwx-]{6}[rwx]{1,}"
    severity: "WARNING"
  - id: "scala_perm_rule-OverlyPermissiveFilePermissionObj"
    languages:
      - "scala"
    message: |
      Overly permissive file permission
    metadata:
      category: "security"
      confidence: "HIGH"
      cwe: "CWE-732"
      shortDescription: "Incorrect Permission Assignment for Critical Resource"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.commons/commons-io']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          ...
          java.nio.file.Files.setPosixFilePermissions(..., $PERMS);
      - pattern-either:
          - pattern: "$PERMS.add($P);"
          - pattern: "$A = $B + $P;"
      - metavariable-regex:
          metavariable: "$P"
          regex: "(PosixFilePermission.){0,1}(OTHERS_)"
    severity: "WARNING"
  - id: "scala_script_rule-ScriptInjection"
    languages:
      - "scala"
    message: |
      The software constructs all or part of a code segment using externally-influenced
      input from an upstream component, but it does not neutralize or incorrectly
      neutralizes special elements that could modify the syntax or behavior of the
      intended code segment.
    metadata:
      cwe: "CWE-94"
      shortDescription: "Improper Control of Generation of Code ('Code Injection')"
      security-severity: "Info"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: "($ENGINE: javax.script.ScriptEngine).eval($ARG)"
      - pattern-not: "($ENGINE: javax.script.ScriptEngine).eval(\"...\")"
    severity: "ERROR"
  - id: "scala_script_rule-SpelView"
    languages:
      - "scala"
    message: |
      The software constructs all or part of a code segment using externally-influenced
      input from an upstream component, but it does not neutralize or incorrectly
      neutralizes special elements that could modify the syntax or behavior of the
      intended code segment.
    metadata:
      cwe: "CWE-94"
      shortDescription: "Improper Control of Generation of Code ('Code Injection')"
      security-severity: "Medium"
      category: "security"
      owasp:
        - "A1:2017-Injection"
        - "A03:2021-Injection"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: "($P: org.springframework.expression.spel.standard.SpelExpressionParser).parseExpression($ARG);"
      - pattern-not: "($P: org.springframework.expression.spel.standard.SpelExpressionParser ).parseExpression(\"...\");"
    severity: "ERROR"
  - id: "scala_smtp_rule-InsecureSmtp"
    languages:
      - "scala"
    message: |
      Server identity verification is disabled when making SSL connections.
    metadata:
      cwe: "CWE-297"
      shortDescription: "Improper Validation of Certificate with Host Mismatch"
      security-severity: "High"
      category: "security"
      owasp:
        - "A2:2017-Broken Authentication"
        - "A07:2021-Identification and Authentication Failures"
      related-packages: ['pkg:maven/org.apache.commons/commons-email']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - pattern-inside: |
              $E = new org.apache.commons.mail.SimpleEmail(...);
              ...
          - pattern-inside: |
              $E = new org.apache.commons.mail.Email(...);
              ...
          - pattern-inside: |
              $E = new org.apache.commons.mail.MultiPartEmail(...);
              ...
          - pattern-inside: |
              $E = new org.apache.commons.mail.HtmlEmail(...);
              ...
          - pattern-inside: |
              $E = new org.apache.commons.mail.ImageHtmlEmail(...);
              ...
      - pattern-not: |
          $E.setSSLOnConnect(true);
          ...
          $E.setSSLCheckServerIdentity(true);
    severity: "ERROR"
  - id: "scala_smtp_rule-SmtpClient"
    languages:
      - "scala"
    message: |
      Simple Mail Transfer Protocol (SMTP) is a the text based protocol used for
      email delivery. Like with HTTP, headers are separate by new line separator. If
      kuser input is place in a header line, the application should remove or replace
      new line characters (CR / LF). You should use a safe wrapper such as Apache
      Common Email and Simple Java Mail which filter special characters that can lead
      to header injection.
    metadata:
      category: "security"
      cwe: "CWE-77"
      shortDescription: "Improper Neutralization of Special Elements used in a Command"
      security-severity: "High"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  $M = new MimeMessage(...);
                  ...
              - pattern: "$M.setSubject($ARG);"
              - pattern-not: "$M.setSubject(\"...\")"
          - patterns:
              - pattern-inside: |
                  $M = new MimeMessage(...);
                  ...
              - pattern: "$M.addHeader($ARG1, $ARG2)"
              - pattern-not: "$M.addHeader(\"...\", \"...\")"
          - patterns:
              - pattern-inside: |
                  $M = new MimeMessage(...);
                  ...
              - pattern: "$M.setDescription($ARG)"
              - pattern-not: "$M.setDescription(\"...\")"
          - patterns:
              - pattern-inside: |
                  $M = new MimeMessage(...);
                  ...
              - pattern: "$M.setDisposition($ARG)"
              - pattern-not: "$M.setDisposition(\"...\")"
    severity: "ERROR"
  - id: "scala_ssrf_rule-PlaySSRF"
    languages:
      - "scala"
    message: |
      Server-Side Request Forgery occur when a web server executes a request to a user supplied
      destination parameter that is not validated. Such vulnerabilities could allow an attacker to
      access internal services or to launch attacks from your web server.
    metadata:
      category: "security"
      cwe: "CWE-918"
      shortDescription: "Server-Side Request Forgery (SSRF)"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-not-inside: |
          object $CLAZZ {
            ...
            $ARG = ...
            ...
          }
      - pattern-not-inside: |
          class $CLAZZ {
            ...
            $ARG = ...
            ...
          }
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  import play.api.libs.ws._
                  ...
              - pattern-not: "($W:WSClient).url(\"...\")"
              - pattern-not: "($W:WSClient).url(\"...\" + \"...\")"
              - pattern: "($W:WSClient).url(<...$ARG...>)"
          - patterns:
              - pattern: "($W:play.api.libs.ws.WSClient).url(<...$ARG...>)"
              - pattern-not: "($W:play.api.libs.ws.WSClient).url(\"...\")"
              - pattern-not: "($W:play.api.libs.ws.WSClient).url(\"...\" + \"...\")"
    severity: "ERROR"
  - id: "scala_ssrf_rule-SSRF"
    languages:
      - "scala"
    message: |
      Server-Side Request Forgery occur when a web server executes a request to a user supplied
      destination parameter that is not validated. Such vulnerabilities could allow an attacker to
      access internal services or to launch attacks from your web server.
    metadata:
      category: "security"
      cwe: "CWE-918"
      shortDescription: "Server-Side Request Forgery (SSRF)"
      security-severity: "Low"
      license: MIT (c) GitLab Inc.
    pattern-either:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  import java.net._
                  ...
              - pattern-inside: |
                  import java.net.URL
                  ...
              - pattern-inside: |
                  import java.net.URI
                  ...
          - pattern: "new $TYPE(...). ... .$FUNC"
          - pattern-not: "new $TYPE(\"...\"). ... .$FUNC"
          - metavariable-pattern:
              metavariable: "$FUNC"
              pattern-either:
                - pattern: "connect"
                - pattern: "GetContent"
                - pattern: "openConnection"
                - pattern: "openStream"
                - pattern: "getContent"
          - metavariable-pattern:
              metavariable: "$TYPE"
              pattern-either:
                - pattern: "URL"
                - pattern: "java.net.URL"
                - pattern: "URI"
                - pattern: "java.net.URI"
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  import java.net.*;
                  ...
              - pattern-inside: |
                  import java.net.InetSocketAddress;
                  ...
          - pattern: |
              new InetSocketAddress(..., $PORT)
          - pattern-not: |
              new InetSocketAddress("...", $PORT)
    severity: "ERROR"
  - id: "scala_strings_rule-BadHexConversion"
    languages:
      - "scala"
    message: |
      When converting a byte array containing a hash signature to a human readable string, a
      conversion mistake can be made if the array is read byte by byte.
    metadata:
      category: "security"
      confidence: "HIGH"
      cwe: "CWE-704"
      shortDescription: "Incorrect Type Conversion or Cast"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    pattern-either:
      - pattern: |
          $B_ARR = ($MD: java.security.MessageDigest).digest(...);
          ...
          for(...) {
            ...
            Integer.toHexString(...);
          }
      - pattern: |
          $B_ARR = ($MD: java.security.MessageDigest).digest(...);
          ...
          while(...) {
            ...
            Integer.toHexString(...);
          }
    severity: "WARNING"
  - id: "scala_strings_rule-FormatStringManipulation"
    languages:
      - "scala"
    message: |
      Allowing user input to control format parameters could enable an attacker to cause exceptions
      to be thrown or leak information.Attackers may be able  to modify the format string argument,
      such that an exception is thrown. If this exception is left uncaught, it may crash the
      application. Alternatively, if sensitive information is used within the unused arguments,
      attackers may change the format string to reveal this information.
    metadata:
      category: "security"
      confidence: "HIGH"
      cwe: "CWE-134"
      shortDescription: "Use of Externally-Controlled Format String"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  $INPUT = ($REQ: javax.servlet.http.HttpServletRequest).getParameter(...);
                  ...
              - pattern-inside: |
                  $FORMAT_STR = <... $INPUT ...>;
          - patterns:
              - pattern-inside: |
                  val $INPUT = ($REQ: javax.servlet.http.HttpServletRequest).getParameter(...);
                  ...
              - pattern-inside: |
                  val $FORMAT_STR = <... $INPUT ...>;
                  ...
          - pattern-inside: |
              val $FORMAT_STR = ... + ($REQ: javax.servlet.http.HttpServletRequest).getParameter(...) + ...; ...
          - pattern-inside: |
              val $FORMAT_STR = ... + ($REQ: javax.servlet.http.HttpServletRequest).getParameter(...); ...
      - pattern-either:
          - pattern: "$VAL = <... $INPUT ...>"
          - pattern: "String.format($FORMAT_STR, ...);"
          - pattern: "String.format(java.util.Locale.$LOCALE, $FORMAT_STR, ...);"
          - pattern: "($F: java.util.Formatter).format($FORMAT_STR, ...);"
          - pattern: "($F: java.util.Formatter).format(java.util.Locale.$LOCALE, $FORMAT_STR, ...);"
          - pattern: "($F: java.io.PrintStream).printf($FORMAT_STR, ...);"
          - pattern: "($F: java.io.PrintStream).printf(java.util.Locale.$LOCALE, $FORMAT_STR, ...);"
          - pattern: "($F: java.io.PrintStream).format($FORMAT_STR, ...);"
          - pattern: "($F: java.io.PrintStream).format(java.util.Locale.$LOCALE, $FORMAT_STR, ...);"
          - pattern: "System.out.printf($FORMAT_STR, ...);"
          - pattern: "System.out.printf(java.util.Locale.$LOCALE, $FORMAT_STR, ...);"
          - pattern: "System.out.format($FORMAT_STR, ...);"
          - pattern: "System.out.format(java.util.Locale.$LOCALE, $FORMAT_STR, ...);"
    severity: "ERROR"
  - id: "scala_strings_rule-ImproperUnicode"
    languages:
      - "scala"
    message: |
      Improper Handling of Unicode Encoding
    metadata:
      category: "security"
      confidence: "HIGH"
      cwe: "CWE-176"
      shortDescription: "Improper Handling of Unicode Encoding"
      security-severity: "Medium"
      license: MIT (c) GitLab Inc.
    pattern-either:
      - patterns:
          - pattern-either:
              - pattern: |
                  $S = ($INPUT: String).$TRANSFORM(...);
                  ...
                  $S.$METHOD(...);
              - pattern: "($INPUT: String).$TRANSFORM().$METHOD(...);"
          - metavariable-regex:
              metavariable: "$METHOD"
              regex: "(equals|equalsIgnoreCase|indexOf)"
          - metavariable-regex:
              metavariable: "$TRANSFORM"
              regex: "(toLowerCase|toUpperCase)"
      - pattern: "java.text.Normalizer.normalize(...);"
      - pattern: "java.net.IDN.toASCII(...);"
      - pattern: "($U: URI).toASCIIString()"
    severity: "ERROR"
  - id: "scala_strings_rule-ModifyAfterValidation"
    patterns:
      - pattern: |
          $Y.matcher($VAR);
          ...
          $VAR.$METHOD(...);
      - metavariable-regex:
          metavariable: "$METHOD"
          regex: "(replace)"
    languages:
      - "scala"
    message: |
      CERT: IDS11-J. Perform any string modifications before validation
    metadata:
      shortDescription: "Collapse of data into unsafe value"
      category: "security"
      cwe: "CWE-182"
      confidence: "HIGH"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "scala_strings_rule-NormalizeAfterValidation"
    patterns:
      - pattern: |
          $Y = java.util.regex.Pattern.compile("[<>]");
          ...
          $Y.matcher($VAR);
          ...
          java.text.Normalizer.normalize($VAR, ...);
    languages:
      - "scala"
    message: |
      IDS01-J. Normalize strings before validating them
    metadata:
      shortDescription: "Collapse of data into unsafe value"
      category: "security"
      cwe: "CWE-182"
      confidence: "HIGH"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "scala_templateinjection_rule-TemplateInjection"
    languages:
      - "scala"
    message: |
      A malicious user in control of a template can run malicious code on the
      server-side. Velocity templates should be seen as scripts.
    metadata:
      category: "security"
      cwe: "CWE-94"
      shortDescription: "Improper Control of Generation of Code ('Code Injection')"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.apache.velocity/velocity']
      license: MIT (c) GitLab Inc.
    pattern-either:
      - patterns:
          - pattern: "org.apache.velocity.app.Velocity.evaluate(..., $VAR)"
          - pattern-not: "org.apache.velocity.app.Velocity.evaluate(..., \"...\")"
      - patterns:
          - pattern-not-inside: |
              $C = ($CFG: freemarker.template.Configuration).getTemplate("...");
              ...
          - pattern-inside: |
              $C = ($CFG: freemarker.template.Configuration).getTemplate($IN);
              ...
          - pattern: "$C.process(...)"
      - patterns:
          - pattern-inside: |
              import com.mitchellbosecke.pebble.PebbleEngine;
              ...
          - pattern-inside: |
              $C = $T.getTemplate($IN);
              ...
          - pattern-not-inside: |
              $C = $T.getTemplate("...");
              ...
          - pattern: "$C.evaluate(...)"
    severity: "ERROR"
  - id: "scala_unsafe_rule-ExternalConfigControl"
    languages:
      - "scala"
    message: |
      Allowing external control of system settings can disrupt service or cause an application to
      behave in unexpected, and potentially malicious ways. An attacker could cause an error by
      providing a nonexistent catalog name or connect to an unauthorized portion of the database.
    metadata:
      category: "security"
      cwe: "CWE-15"
      shortDescription: "External Control of System or Configuration Setting"
      technology:
        - "scala"
      security-severity: "High"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: |
          $TAINTED = ($REQ: HttpServletRequest).getParameter(...);
          ...
          ($CONN: java.sql.Connection).setCatalog($TAINTED);
    severity: "WARNING"
  - id: "scala_unsafe_rule-InformationExposure"
    languages:
      - "scala"
    message: |
      The sensitive information may be valuable information on its own (such as a password), or it
      may be useful for launching other, more deadly attacks. If an attack fails, an attacker may use
      error information provided by the server to launch another more focused attack. For example, an
      attempt to exploit a path traversal weakness (CWE-22) might yield the full pathname of the
      installed application.
    metadata:
      category: "security"
      cwe: "CWE-209"
      shortDescription: "Information Exposure Through an Error Message"
      technology:
        - "scala"
      security-severity: "Low"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: "$E.printStackTrace(...)"
    severity: "WARNING"
  - id: "scala_unsafe_rule-SensitiveDataExposure"
    languages:
      - "scala"
    message: |
      Applications can unintentionally leak information about their configuration, internal
      workings, or violate privacy through a variety of application problems. Pages that provide
      different responses based on the validity of the data can lead to Information Leakage;
      specifically when data deemed confidential is being revealed as a result of the web
      application's design.
    metadata:
      category: "security"
      cwe: "CWE-497"
      shortDescription: "Exposure of sensitive system information to an unauthorized control sphere"
      technology:
        - "scala"
        - "play"
      security-severity: "Info"
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          def $FUNC(..., $ARG: String, ...) = $TYPE {
            ...
          }
      - pattern-inside: |
          $VAL = ($C: play.api.Configuration).underlying.getString($ARG)
          ...
      - pattern: "Ok(<...$VAL...>)"
    severity: "WARNING"
  - id: "scala_xml_rule-ApacheXmlRpc"
    languages:
      - "scala"
    message: |
      Enabling extensions in Apache XML RPC server or client can lead to deserialization
      vulnerability which would allow an attacker to execute arbitrary code.
    metadata:
      category: "security"
      cwe: "CWE-502"
      shortDescription: "Deserialization of Untrusted Data"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.apache.xmlrpc/xmlrpc-client']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-either:
          - patterns:
              - pattern-inside: |
                  val $VAR = new XmlRpcServerConfigImpl();
                  ...
              - pattern: "$VAR.setEnabledForExtensions(true);"
          - patterns:
              - pattern-inside: |
                  val $VAR = new org.apache.xmlrpc.client.XmlRpcClientConfigImpl();
                  ...
              - pattern: "$VAR.setEnabledForExtensions(true);"
    severity: "WARNING"
  - id: "scala_xml_rule-SAMLIgnoreComments"
    languages:
      - "scala"
    message: |
      Ignoring XML comments in SAML may lead to authentication bypass
    metadata:
      category: "security"
      shortDescription: "Weak authentication"
      cwe: "CWE-1390"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.xmlrpc/xmlrpc-client']
      license: MIT (c) GitLab Inc.
    pattern: "($POOL: BasicParserPool).setIgnoreComments(false);"
    severity: "WARNING"
  - id: "scala_xml_rule-XmlDecoder"
    languages:
      - "scala"
    message: |
      Avoid using XMLDecoder to parse content from an untrusted source.
    metadata:
      category: "security"
      cwe: "CWE-502"
      shortDescription: "Deserialization of Untrusted Data"
      security-severity: "High"
      related-packages: ['pkg:maven/java/beans']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          $D = new java.beans.XMLDecoder($IN);
          ...
      - pattern-not-inside: |
          $DX = new java.beans.XMLDecoder("...");
          ...
      - pattern: "$D.readObject"
    severity: "WARNING"
  - id: "scala_xml_rule-XsltTransform"
    mode: "taint"
    pattern-sources:
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern-inside: |
                      $FUNC(...,String $VAR, ...) {
                        ...
                      }
                  - pattern-either:
                      - pattern: "new FileInputStream(<... $VAR ...>);"
                      - pattern: "getClass.getResourceAsStream(<... $VAR ...>)"
              - patterns:
                  - pattern-inside: |
                      class $CLZ {
                        String $X = "...";
                        ...
                      }
                  - pattern-inside: |
                      $FUNC(...,String $Y, ...) {
                        ...
                      }
                  - pattern-either:
                      - pattern: "new FileInputStream($X + $Y);"
                      - pattern: "getClass.getResourceAsStream($X + $Y)"
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern-inside: |-
                  (javax.xml.transform.TransformerFactory $T).newTransformer($SRC, ...)
              - pattern-inside: |-
                  (javax.xml.transform.Transformer $T).transform($SRC, ...)
          - pattern: "$SRC"
    languages:
      - "java"
    message: |
      It is possible to attach malicious behavior to those style sheets. Therefore, if an attacker
      can control the content or the source of the style sheet, he might be able to trigger remote
      code execution.
    metadata:
      shortDescription: "XML injection (aka Blind XPath injection)"
      category: "security"
      cwe: "CWE-91"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.xmlrpc/xmlrpc-client']
      license: MIT (c) GitLab Inc.
    severity: "WARNING"
  - id: "scala_xpathi_rule-XpathInjection"
    languages:
      - "scala"
    message: |
      The input values included in SQL queries need to be passed in safely. Bind
      variables in prepared statements can be used to easily mitigate the risk of
      SQL injection.
    metadata:
      category: "security"
      cwe: "CWE-611"
      shortDescription: "Improper Restriction of XML External Entity Reference ('XXE')"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.xmlrpc/xmlrpc-client']
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern-inside: |-
                  import javax.xml.xpath._
                  ...
              - pattern-inside: |-
                  import javax.xml.xpath.XPath
                  ...
          - pattern-either:
              - pattern: "$Y.compile(...)"
              - pattern: "$X.evaluate(..., $ARG2)"
    pattern-sources:
      - patterns:
          - pattern-inside: |
              def $FUNC(..., $ARG: $TYPE,...): $RET = {
                ...
              }
          - pattern: "$ARG"
    severity: "ERROR"
  - id: "scala_xss_rule-MVCApi"
    languages:
      - "scala"
    message: |
      Disabling HTML escaping put the application at risk for Cross-Site Scripting (XSS).
    metadata:
      category: "security"
      cwe: "CWE-79"
      shortDescription: "Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sanitizers:
      - pattern: "org.owasp.encoder.Encode.forHtml(...)"
    pattern-sinks:
      - pattern: "Ok(...)"
    pattern-sources:
      - patterns:
          - pattern-inside: |
              def $FUNC(..., $ARG: String, ...) = Action {
                ...
              }
          - focus-metavariable: "$ARG"
    severity: "WARNING"
  - id: "scala_xss_rule-RequestWrapper"
    languages:
      - "scala"
    message: |
      Avoid using custom XSS filtering. Please use standard sanitization functions.
    metadata:
      category: "security"
      cwe: "CWE-79"
      shortDescription: "Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          class $CLASS(...) extends HttpServletRequestWrapper(...) {
          ...
          }
      - pattern: "def stripXSS(...) = { ... }"
    severity: "INFO"
  - id: "scala_xss_rule-WicketXSS"
    languages:
      - "scala"
    message: |
      Disabling HTML escaping put the application at risk for Cross-Site Scripting (XSS).
    metadata:
      category: "security"
      cwe: "CWE-79"
      shortDescription: "Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.wicket/wicket']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern: "($X: Label).setEscapeModelStrings(false);"
    severity: "WARNING"
  - id: "scala_xss_rule-XSSReqParamToServletWriter"
    languages:
      - "scala"
    message: |
      Servlet reflected cross site scripting vulnerability
    metadata:
      category: "security"
      cwe: "CWE-79"
      shortDescription: "Improper Neutralization of Input During Web Page Generation"
      technology:
        - "scala"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sanitizers:
      - pattern: "Encode.forHtml(...)"
      - pattern: "org.owasp.esapi.Encoder.encodeForSQL(...)"
    pattern-sinks:
      - patterns:
          - pattern-inside: |-
              def $FUNC(..., $RES: HttpServletResponse, ...): $TYPE = {...}
          - pattern-inside: |
              $WRITER = $RES.getWriter
              ...
          - pattern: "$WRITER.write(...)"
      - patterns:
          - pattern-inside: |-
              def $FUNC(..., $RES: HttpServletResponse, ...): $TYPE = {...}
          - pattern: "$RES.getWriter.write(...)"
      - patterns:
          - pattern-inside: |-
              def $FUNC(..., $RES: HttpServletResponse, ...): $TYPE = {...}
          - pattern: "$RES.getWriter.print(...)"
    pattern-sources:
      - patterns:
          - pattern-inside: |-
              def $FUNC(..., $REQ: HttpServletRequest, ...): $TYPE = {...}
          - pattern-either:
              - pattern: "$REQ.getParameter(...)"
              - pattern: "$REQ.getQueryString"
    severity: "WARNING"
  - id: "scala_xss_rule-XSSServlet"
    languages:
      - "scala"
    message: |
      A potential XSS was found. It could be used to execute unwanted JavaScript in a
      client's browser.
    metadata:
      category: "security"
      cwe: "CWE-79"
      shortDescription: "Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.springframework.boot/spring-boot-starter-web']
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sanitizers:
      - patterns:
          - pattern-inside: |-
              org.owasp.encoder.Encode.forHtml($TAINTED);
          - pattern: "$TAINTED"
    pattern-sinks:
      - patterns:
          - pattern-inside: |-
              def $FUNC(..., $RES: HttpServletResponse, ...): $TYPE = {...}
          - pattern-inside: |
              $WRITER = $RES.getWriter;
              ...
          - pattern: "$WRITER.write($DATA,...);"
          - pattern: "$DATA"
      - patterns:
          - pattern-inside: |-
              def $FUNC(..., $RES: HttpServletResponse, ...): $TYPE = {...}
          - pattern: "$RES.getWriter.write($DATA,...);"
          - pattern: "$DATA"
    pattern-sources:
      - patterns:
          - pattern-inside: |-
              def $FUNC(..., $REQ: HttpServletRequest, ...): $TYPE = {...}
          - pattern: "$REQ.getParameter(...);"
    severity: "WARNING"
  - id: "scala_xxe_rule-Document"
    languages:
      - "scala"
    message: |
      XML External Entity (XXE) attacks can occur when an XML parser supports XML
      entities while processing XML received from an untrusted source.
    metadata:
      category: "security"
      cwe: "CWE-611"
      shortDescription: "Improper Restriction of XML External Entity Reference ('XXE')"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.xmlrpc/xmlrpc-client']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          $DF = DocumentBuilderFactory.newInstance
          ...
      - pattern-not-inside: |
          $DF.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)
          ...
      - pattern-not-inside: |
          $DF.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
          ...
      - pattern: "$DB.parse(...)"
    severity: "ERROR"
  - id: "scala_xxe_rule-SaxParserXXE"
    languages:
      - "scala"
    message: |
      XML External Entity (XXE) attacks can occur when an XML parser supports XML
      entities while processing XML received from an untrusted source.
    metadata:
      category: "security"
      cwe: "CWE-611"
      shortDescription: "Improper Restriction of XML External Entity Reference ('XXE')"
      security-severity: "Info"
      related-packages: ['pkg:maven/org.apache.xmlrpc/xmlrpc-client']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          val $SF = SAXParserFactory.newInstance
          ...
      - pattern-not-inside: |
          $SF.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
          ...
      - pattern-not-inside: |
          $SF.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)
          ...
      - pattern-inside: |
          val $P = $SFP.newSAXParser
          ...
      - pattern: "$P.parse(...);"
    severity: "ERROR"
  - id: "scala_xxe_rule-Trans"
    languages:
      - "scala"
    message: |
      XML External Entity (XXE) attacks can occur when an XML parser supports XML
      entities while processing XML received from an untrusted source.
    metadata:
      category: "security"
      cwe: "CWE-611"
      shortDescription: "Improper Restriction of XML External Entity Reference ('XXE')"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.xmlrpc/xmlrpc-client']
      license: MIT (c) GitLab Inc.
    mode: "taint"
    pattern-sanitizers:
      - pattern: "$T.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, \"\");"
      - pattern: "$T.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, \"\");"
      - pattern: "$T.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);"
    pattern-sinks:
      - pattern: "$T.transform(...)"
    pattern-sources:
      - patterns:
          - pattern-either:
              - pattern-inside: |-
                  import javax.xml.transform._
                  ...
              - pattern-inside: |-
                  import javax.xml.transform.Transformer
                  ...
          - pattern: "$FACT.newTransformer"
    severity: "ERROR"
  - id: "scala_xxe_rule-XMLRdr"
    languages:
      - "scala"
    message: |
      XML External Entity (XXE) attacks can occur when an XML parser supports XML
      entities while processing XML received from an untrusted source.
    metadata:
      category: "security"
      cwe: "CWE-611"
      shortDescription: "Improper Restriction of XML External Entity Reference ('XXE')"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.xmlrpc/xmlrpc-client']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          val $R = XMLReaderFactory.createXMLReader
          ...
      - pattern-not-inside: |
          $R.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
          ...
      - pattern: "$R.parse(...)"
    severity: "ERROR"
  - id: "scala_xxe_rule-XMLStreamRdr"
    languages:
      - "scala"
    message: |
      XML External Entity (XXE) attacks can occur when an XML parser supports XML
      entities while processing XML received from an untrusted source.
    metadata:
      category: "security"
      cwe: "CWE-611"
      shortDescription: "Improper Restriction of XML External Entity Reference ('XXE')"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.xmlrpc/xmlrpc-client']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          $SF = XMLInputFactory.newFactory
          ...
      - pattern-not-inside: |
          $SF.setProperty(XMLInputFactory.SUPPORT_DTD, false)
          ...
      - pattern-not-inside: |
          $SF.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false)
          ...
      - pattern: "$SF.createXMLStreamReader(...)"
    severity: "ERROR"
  - id: "scala_xxe_rule-XPathXXE"
    languages:
      - "scala"
    message: |
      XML External Entity (XXE) attacks can occur when an XML parser supports XML
      entities while processing XML received from an untrusted source.
    metadata:
      category: "security"
      cwe: "CWE-611"
      shortDescription: "Improper Restriction of XML External Entity Reference ('XXE')"
      security-severity: "Medium"
      related-packages: ['pkg:maven/org.apache.xmlrpc/xmlrpc-client']
      license: MIT (c) GitLab Inc.
    patterns:
      - pattern-inside: |
          val $DF = DocumentBuilderFactory.newInstance
          ...
      - pattern-not-inside: |
          $DF.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "")
          ...
      - pattern-not-inside: |
          $DF.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "")
          ...
      - pattern-not-inside: |
          $DF.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
          ...
      - pattern-not-inside: |
          $DF.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)
          ...
      - pattern-inside: |
          $B = $DF.newDocumentBuilder
          ...
      - pattern: "$XPATH.evaluate(...)"
    severity: "ERROR"
