diff --git a/rules/go/gosec/LICENSE b/rules/go/gosec/LICENSE new file mode 100644 index 000000000..f49a4e16e --- /dev/null +++ b/rules/go/gosec/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/rules/go/gosec/README.md b/rules/go/gosec/README.md new file mode 100644 index 000000000..80e4738cf --- /dev/null +++ b/rules/go/gosec/README.md @@ -0,0 +1,3 @@ +## License + +The `gosec` folder and its subfolder are under Apache 2 License diff --git a/rules/go/gosec/blocklist/cgi.yml b/rules/go/gosec/blocklist/cgi.yml new file mode 100644 index 000000000..72fba4668 --- /dev/null +++ b/rules/go/gosec/blocklist/cgi.yml @@ -0,0 +1,59 @@ +patterns: + - pattern: | + import "net/http/cgi" + - pattern: | + import ( + "net/http/cgi" + ) +languages: + - go +metadata: + description: "Use of a Broken or Risky Cryptographic Algorithm" + remediation_message: | + ## Description + + Using the `net/http/cgi` package in Go, especially with versions prior to 1.6.3, exposes the application to the Httpoxy attack, a vulnerability identified as CVE-2016-5386. This vulnerability arises from the way CGI and FastCGI protocols handle certain environment variables, which can be manipulated to intercept and redirect outgoing HTTP requests made by the web application. + + ## Remediations + + ✅ Update Go Version + + Ensure you are using a version of Go that is 1.6.3 or later, where this vulnerability is patched. + + ```sh + # Check Go version and update if necessary + go version + # Follow Go's update instructions if your version is < 1.6.3 + ``` + + ✅ Use Alternative Packages + + Refrain from using CGI where possible. Utilize alternative packages and methods to handle HTTP requests which do not rely on the CGI protocol. + + ```go + // Use the standard net/http package instead + import "net/http" + ``` + + ❌ Don't Use `net/http/cgi` in Older Versions + + Do not use the `net/http/cgi` package if you are operating on Go versions older than 1.6.3 as they are susceptible to the Httpoxy vulnerability. + + ```go + // This import is vulnerable to Httpoxy in Go < 1.6.3 + import "net/http/cgi" + ``` + + ❌ Avoid Exposing Environment Variables + + Ensure that the environment variables such as `HTTP_PROXY` are not being exposed unintentionally, as this can be leveraged for Httpoxy attacks. + + ## Resources + + - [CVE-2016-5386 Detail](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5386) + - [Httpoxy.org](https://httpoxy.org/) + + cwe_id: + - 327 + id: go_gosec_blocklist_cgi + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_blocklist_cgi diff --git a/rules/go/gosec/blocklist/des.yml b/rules/go/gosec/blocklist/des.yml new file mode 100644 index 000000000..c9508e8d6 --- /dev/null +++ b/rules/go/gosec/blocklist/des.yml @@ -0,0 +1,77 @@ +patterns: + - pattern: | + import "crypto/des" + - pattern: | + import ( + "crypto/des" + ) +languages: + - go +metadata: + description: "Use of a Broken or Risky Cryptographic Algorithm" + remediation_message: | + ## Description + + The Data Encryption Standard (DES) is an outdated symmetric-key algorithm for encryption. Officially deemed insecure and no longer recommended for use, DES was withdrawn by the National Institute of Standards and Technology (NIST) as a standard in 2005, primarily because of its 56-bit key size, which is vulnerable to brute-force attacks. + + ## Remediation + + To ensure the confidentiality and integrity of sensitive data, it is crucial to utilize a modern and secure encryption algorithm. The use of Advanced Encryption Standard (AES) with a key size of 256 bits (AES-256) is recommended for its strong security properties and widespread acceptance as a replacement for DES. + + ✅ Implement AES-256 for Strong Encryption + + ```go + // Use AES-256 for secure encryption by initializing a 32-byte key for AES-256 + key := make([]byte, 32) + if _, err := io.ReadFull(rand.Reader, key); err != nil { + log.Fatal(err) + } + + // Create a new cipher block from the key + blockCipher, err := aes.NewCipher(key) + if err != nil { + log.Fatal(err) + } + + // Use Galois/Counter Mode (GCM) for both encryption and decryption + aead, err := cipher.NewGCM(blockCipher) + if err != nil { + log.Fatal(err) + } + + var encrypted = []byte{} + var nonce = []byte{} + + // Encrypt a message with AES-256 using GCM + { + msg := []byte("Some secret message") + // Ensure nonces are unique for each encryption to maintain security + nonce = make([]byte, 12) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + log.Fatal(err) + } + encrypted = aead.Seal(nil, nonce, msg, nil) + } + + // Decrypt the message securely + { + msg, err := aead.Open(nil, nonce, encrypted, nil) + if err != nil { + log.Fatal(err) + } + fmt.Printf("Decrypted: %s\n", msg) + } + ``` + + ❌ Do Not Use Deprecated Algorithms + Avoid using deprecated cryptographic algorithms such as DES, as they do not provide adequate security against modern threats and attacks. + + ## Resources + + - [NIST Recommendations](https://csrc.nist.gov/publications/detail/sp/800-131a/rev-2/final) + - [AES-256 Encryption](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) + + cwe_id: + - 327 + id: go_gosec_blocklist_des + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_blocklist_des diff --git a/rules/go/gosec/blocklist/md5.yml b/rules/go/gosec/blocklist/md5.yml new file mode 100644 index 000000000..f4c565bea --- /dev/null +++ b/rules/go/gosec/blocklist/md5.yml @@ -0,0 +1,47 @@ +patterns: + - pattern: | + import "crypto/md5" + - pattern: | + import ( + "crypto/md5" + ) +languages: + - go +metadata: + description: "Use of a Broken or Risky Cryptographic Algorithm" + remediation_message: | + ## Description + + MD5 is a widely used cryptographic hash function that produces a 128-bit (16-byte) hash value. It's commonly used to check the integrity of files. However, MD5 is not collision-resistant; this means that different inputs may produce the same output hash. MD5's vulnerabilities and the feasibility of collision attacks have rendered it obsolete for security-related purposes, particularly digital signatures, SSL certificates, and cryptographic message authentication. + + ## Remediation + + Given the vulnerabilities of MD5, it is highly recommended to switch to more secure hashing algorithms. For hashing purposes that do not involve passwords, such as verifying file integrity or generating unique identifiers, SHA-3 or BLAKE2 can be used due to their stronger cryptographic properties. + + ✅ Use SHA-3 or BLAKE2 for General Hashing Needs + + ```go + // BLAKE2 is a cryptographic hash function faster than MD5, SHA-1, and SHA-2, and as secure as the latest standard SHA-3 + 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)) + ``` + + For password hashing, where the hash functions need to be slow to combat brute-force attacks, bcrypt or Argon2id should be used. These algorithms are designed to be computationally intensive to hash and verify, which helps protect against password cracking attempts. + + ✅ Adopt bcrypt or Argon2id for Password Hashing + + The bcrypt algorithm is a good choice for password hashing as it allows you to adjust the cost (computational complexity) and is widely supported. Argon2id is the winner of the Password Hashing Competition and offers a good balance between resistance to GPU cracking attacks and usability. + + ## Resources + + - [OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html) + + cwe_id: + - 327 + id: go_gosec_blocklist_md5 + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_blocklist_md5 diff --git a/rules/go/gosec/blocklist/rc4.yml b/rules/go/gosec/blocklist/rc4.yml new file mode 100644 index 000000000..a717f2224 --- /dev/null +++ b/rules/go/gosec/blocklist/rc4.yml @@ -0,0 +1,71 @@ +patterns: + - pattern: | + import "crypto/rc4" + - pattern: | + import ( + "crypto/rc4" + ) +languages: + - go +metadata: + description: "Use of a Broken or Risky Cryptographic Algorithm" + remediation_message: | + ## Description + + RC4 is a stream cipher that was once popular for its simplicity and speed in operation. However, extensive research over the years has revealed multiple vulnerabilities, rendering RC4 insecure in most contexts. Its weaknesses in key scheduling and the generation of non-random bytes have led to successful cryptanalysis and practical attacks, making it unsuitable for securing data. + + ## Remediation + + With the known vulnerabilities of RC4, it's essential to move to a more secure cipher. AES (Advanced Encryption Standard) is the recommended replacement because it has undergone extensive scrutiny and is considered secure against cryptanalysis. + + ✅ Switch to AES-256 for Robust Encryption + + ```go + // 32 byte keys will set up AES-256, which is a secure block cipher that has become the industry standard for encryption. + 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 after every 2^32 uses of a single nonce-value to avoid cipher text repetition. + 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) + } + ``` + + Using AES-256 ensures that your encryption mechanism meets current security standards and is robust against known attacks. AES has been widely adopted across various industries and has proven its reliability over time. + + ## Resources + + - [NIST Guidelines on Cryptography](https://csrc.nist.gov/publications/detail/sp/800-38a/final) + cwe_id: + - 327 + id: go_gosec_blocklist_rc4 + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_blocklist_rc4 diff --git a/rules/go/gosec/blocklist/sha1.yml b/rules/go/gosec/blocklist/sha1.yml new file mode 100644 index 000000000..467dba6ad --- /dev/null +++ b/rules/go/gosec/blocklist/sha1.yml @@ -0,0 +1,43 @@ +patterns: + - pattern: | + import "crypto/sha1" + - pattern: | + import ( + "crypto/sha1" + ) +languages: + - go +metadata: + description: "Use of a Broken or Risky Cryptographic Algorithm" + remediation_message: | + ## Description + + The SHA-1 hashing algorithm is no longer considered secure against well-funded attackers. It is vulnerable to collision attacks, which means it's possible to generate two different inputs that result in the same SHA-1 hash, undermining the hash's uniqueness and security. Due to these vulnerabilities, it is advised to discontinue using SHA-1 for cryptographic security. + + ## Remediation + + When choosing a hashing algorithm for cryptographic purposes, it's important to select one that is resistant to collisions and other attack vectors. SHA-3 and BLAKE2 are both excellent choices for non-password-based hashing requirements due to their strong cryptographic properties. + + ✅ For General Hashing Needs, Use SHA-3 or BLAKE2 + + Choose SHA-3 or BLAKE2 for their resistance to known hash attack vectors, ensuring the integrity and uniqueness of your data fingerprints. + + ✅ For Password Hashing, Prefer bcrypt or Argon2id + + For password hashing specifically, bcrypt or Argon2id are recommended. These algorithms are designed to be computationally intensive, which helps protect against brute-force attacks. + + ❌ Discontinue Using SHA-1 for Security Purposes + + Given its vulnerabilities, avoid using SHA-1 in any security context to prevent potential collision attacks. + + The code snippet provided is unrelated to the hashing algorithms and seems to be a continuation of the previous examples for encryption with AES-256. Ensure your hashing and encryption strategies are correctly implemented as per their intended use-cases. + + ## Resources + + - [OWASP Cryptographic Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html) + - [NIST Policy on Hash Functions](https://csrc.nist.gov/projects/hash-functions) + + cwe_id: + - 327 + id: go_gosec_blocklist_sha1 + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_blocklist_sha1 diff --git a/rules/go/gosec/crypto/bad_tls_settings.yml b/rules/go/gosec/crypto/bad_tls_settings.yml new file mode 100644 index 000000000..72b097a17 --- /dev/null +++ b/rules/go/gosec/crypto/bad_tls_settings.yml @@ -0,0 +1,78 @@ +patterns: + - pattern: | + tls.Config{$<...>CipherSuites: []$<_>{$<...>$$<...>}$<...>} + filters: + - variable: CIPHERS + values: + - 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 + - pattern: | + tls.CipherSuite{$<...>ID: $$<...>} + filters: + - variable: CIPHERS + values: + - 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 +languages: + - go +metadata: + description: "Use of a broken or risky cryptographic algorithm" + remediation_message: | + ## Description + + A security concern arises when a cryptographically insecure cipher suite is used in an application. Such cipher suites may be vulnerable to various types of attacks, reducing the security of the communication channel. + + ## Remediation + + To enhance the security of TLS connections, it is crucial to use up-to-date and secure cipher suites and protocols. Here are the recommended steps to ensure the use of secure ciphers: + + ✅ Use Modern, Secure Cipher Suites + + Select cipher suites that are known to be secure and have properties such as Perfect Forward Secrecy (PFS), which protects past communications even if future private keys are compromised. + + ✅ Adopt TLS 1.3 Where Possible + + TLS 1.3 should be the preferred protocol as it includes improvements over previous versions, making it more secure against various attacks. Go's standard library will automatically prefer the most secure protocol and cipher suite available during the TLS handshake. + + ✅ Configure TLS Properly If Using TLS 1.0-1.2 + + In cases where TLS 1.3 is not an option and you must use TLS 1.0-1.2, ensure to configure the cipher suites to use those that support PFS, as listed below. + + ❌ Avoid Using Obsolete or Insecure Cipher Suites + + Avoid any cipher suites that do not support modern security standards, including those without PFS or with known vulnerabilities. + + The provided Go code examples demonstrate how to configure the `tls.Config` struct for a Go server to use TLS 1.3 or to specify a list of secure cipher suites when using TLS 1.0-1.2. + + ## Resources + + - [Mozilla's SSL Configuration Generator](https://ssl-config.mozilla.org/) + - [OWASP TLS Cipher String Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/TLS_Cipher_String_Cheat_Sheet.html) + - [RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3](https://tools.ietf.org/html/rfc8446) + + cwe_id: + - 327 + id: go_gosec_crypto_bad_tls_settings + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_crypto_bad_tls_settings diff --git a/rules/go/gosec/crypto/insecure_ignore_host_key.yml b/rules/go/gosec/crypto/insecure_ignore_host_key.yml new file mode 100644 index 000000000..34d4277a7 --- /dev/null +++ b/rules/go/gosec/crypto/insecure_ignore_host_key.yml @@ -0,0 +1,38 @@ +patterns: + - ssh.InsecureIgnoreHostKey() +languages: + - go +metadata: + description: "Key exchange without entity authentication" + remediation_message: | + ## Description + + The security vulnerability identified pertains to the application neglecting the verification of host keys during SSH connections. Host keys are crucial for confirming the server's identity, preventing Man-in-the-Middle (MitM) attacks where an attacker could impersonate the server. When these keys are ignored, the client cannot guarantee the authenticity of the server it connects to. + + ## Remediation + + To mitigate this risk, it is essential to implement proper host key checking: + + ✅ Implement Host Key Verification + + Use the `knownhosts` package from Go's `x/crypto/ssh` to validate server keys against known hosts. This mirrors the functionality found in OpenSSH. + + ✅ Avoid Disabling Host Key Checking + + Never disable host key checking in production code. While it might be convenient for testing, it opens up security vulnerabilities. + + ❌ Do Not Use `InsecureIgnoreHostKey` + + Although available, using `ssh.InsecureIgnoreHostKey` as a `HostKeyCallback` function should be strictly avoided as it does not offer any form of host validation. + + Below is a code snippet showing how to set up an SSH `ClientConfig` in Go to use the `knownhosts` callback for server verification: + + ## Resources + + - [GoDoc for x/crypto/ssh](https://pkg.go.dev/golang.org/x/crypto/ssh) + - [Secure use of SSH - OpenSSH](https://www.openssh.com/) + + cwe_id: + - 322 + id: go_gosec_crypto_insecure_ignore_host_key + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_crypto_insecure_ignore_host_key diff --git a/rules/go/gosec/crypto/weak_crypto.yml b/rules/go/gosec/crypto/weak_crypto.yml new file mode 100644 index 000000000..7a3d221ec --- /dev/null +++ b/rules/go/gosec/crypto/weak_crypto.yml @@ -0,0 +1,46 @@ +patterns: + - des.NewCipher() + - des.NewTripleDESCipher() + - md5.New() + - md5.Sum() + - rc4.NewCipher() + - sha1.New() + - sha1.Sum() +languages: + - go +metadata: + description: "Use of a Broken or Risky Cryptographic Algorithm" + remediation_message: | + ## Description + + The issue identified indicates the use of a cryptographic algorithm that is no longer considered secure by current standards. Such algorithms can compromise data confidentiality and integrity, making it vulnerable to decryption and tampering by unauthorized parties. + + ## Remediation + + To ensure the security of the data, adhere to the following guidelines: + + ✅ Employ Strong Cryptographic Algorithms + + Replace deprecated or weak algorithms with strong, modern alternatives such as AES (Advanced Encryption Standard) for encryption, and SHA-256 or higher for hashing. + + ✅ Keep Libraries Updated + + Use the latest versions of cryptographic libraries, as they are more likely to default to secure algorithms and settings. + + ❌ Avoid Deprecated Algorithms + + Do not use cryptographic algorithms that have been deprecated due to vulnerabilities, such as MD5, SHA-1, or DES. + + ❌ Do Not Reinvent Cryptography + + Avoid custom cryptographic implementations as they are more susceptible to errors. Instead, rely on well-reviewed and tested standard cryptographic libraries. + + ## Resources + + - [NIST Cryptographic Standards and Guidelines](https://csrc.nist.gov/publications/sp) + - [Cryptography Coding Standard](https://cryptocoding.net/index.php/Coding_rules) + + cwe_id: + - 327 + id: go_gosec_crypto_weak_crypto + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_crypto diff --git a/rules/go/gosec/crypto/weak_key_strength.yml b/rules/go/gosec/crypto/weak_key_strength.yml new file mode 100644 index 000000000..43cac029a --- /dev/null +++ b/rules/go/gosec/crypto/weak_key_strength.yml @@ -0,0 +1,55 @@ +patterns: + - pattern: rsa.GenerateKey($<...>$) + filters: + - variable: ARG + less_than: 2048 +languages: + - go +metadata: + description: "Inadequate encryption strength" + remediation_message: | + ## Description + + The application generates an RSA key with a bit length that is shorter than the current recommended minimum of 2048 bits. Keys shorter than 2048 bits are considered insecure due to advancements in computational power which could potentially allow them to be factored, thereby breaking the encryption. + + ## Remediation + + To ensure the security of RSA keys, follow these guidelines: + + ✅ Use Sufficient Key Length + + Generate RSA keys with a minimum length of 2048 bits to align with NIST recommendations and safeguard against future advancements in computing power that could compromise keys of shorter lengths. + + ```go + // Example of generating a secure RSA key with 2048 bits + import ( + "crypto/rand" + "crypto/rsa" + "log" + ) + + func generateSecureKey() { + // Use at least 2048 bits for secure RSA keys + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + log.Fatalf("Error generating RSA key: %v", err) + } + // privateKey can now be used for secure cryptographic operations + } + ``` + + ❌ Avoid Short Keys + + Do not use RSA keys that are less than 2048 bits in length, as they do not offer sufficient protection against brute-force attacks. + + ❌ Don't Ignore Industry Standards + + Always follow industry standards and guidelines for cryptographic practices to maintain the integrity and confidentiality of data. + + ## Resources + + - [NIST Special Publication 800-57 Part 1](https://csrc.nist.gov/publications/detail/sp/800-57-part-1/rev-5/final) + cwe_id: + - 326 + id: go_gosec_crypto_weak_key_strength + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_key_strength diff --git a/rules/go/gosec/crypto/weak_random.yml b/rules/go/gosec/crypto/weak_random.yml new file mode 100644 index 000000000..d21ed1110 --- /dev/null +++ b/rules/go/gosec/crypto/weak_random.yml @@ -0,0 +1,92 @@ +patterns: + - pattern: $.$() + filters: + - variable: METHOD + values: + - Float32 + - Float64 + - Int31 + - Int + - Intn + - Int31n + - Int63 + - Int63n + - NormalFloat64 + - Uint32 + - Uint64 + - either: + - variable: RAND + detection: go_gosec_crypto_weak_random_init + scope: cursor + - variable: RAND + detection: go_gosec_crypto_weak_random_import + scope: cursor +auxiliary: + - id: go_gosec_crypto_weak_random_import + patterns: + - pattern: import $"math/rand" + - pattern: | + import ( + $"math/rand" + ) + - id: go_gosec_crypto_weak_random_init + patterns: + - pattern: $.New() + filters: + - variable: RAND + detection: go_gosec_crypto_weak_random_import + scope: cursor +languages: + - go +metadata: + description: "Use of cryptographically weak Pseudo-Random Number Generator (PRNG)" + remediation_message: | + ## Description + + The `math/rand` package in Go is designed for generating pseudorandom numbers, which are not secure for cryptographic purposes. These numbers are predictable if the seed is known, which could compromise the security of applications using them for secrets, tokens, or other security-sensitive features. + + ## Remediations + + To securely generate random numbers in a security-sensitive context, implement the following measures: + + ✅ Use Cryptographically Secure Randomness + + Replace the use of `math/rand` with `crypto/rand` to ensure that the random numbers generated are suitable for cryptographic use and are not predictable. + + ```go + import ( + "crypto/rand" + "log" + "math/big" + ) + + func generateSecureRandomNumber() *big.Int { + // Generate a cryptographically secure random number + randomNumber, err := rand.Int(rand.Reader, big.NewInt(1<<62)) + if err != nil { + log.Fatalf("Failed to generate a secure random number: %v", err) + } + return randomNumber + } + ``` + + ✅ Audit Existing Code + + Review your codebase for instances where `math/rand` is used in security-sensitive contexts and update them to use `crypto/rand`. + + ❌ Do Not Use Predictable Seeds + + Avoid initializing `math/rand` with predictable seeds, such as timestamps or other easily guessable values, especially in a security context. + + ❌ Don't Use for Security Purposes + + Never rely on `math/rand` for generating random numbers in cryptographic applications, like key generation, authentication tokens, or any form of security challenge. + + ## Resources + + - [crypto/rand package documentation](https://pkg.go.dev/crypto/rand) + + cwe_id: + - 338 + id: go_gosec_crypto_weak_random + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_random diff --git a/rules/go/gosec/crypto/weak_tls_version.yml b/rules/go/gosec/crypto/weak_tls_version.yml new file mode 100644 index 000000000..a646a6f47 --- /dev/null +++ b/rules/go/gosec/crypto/weak_tls_version.yml @@ -0,0 +1,77 @@ +patterns: + - tls.VersionTLS11 + - tls.VersionTLS10 +languages: + - go +metadata: + description: "Use of deprecated TLS version" + remediation_message: | + ## Description + + TLS (Transport Layer Security) versions 1.1 and 1.0 have been deprecated due to known security vulnerabilities that can expose sensitive data to interception and attacks. Using these versions can put data transmissions at risk. + + ## Remediations + + To ensure secure data transmission, you should enforce the use of TLS 1.3, which includes security enhancements over its predecessors. The following steps can be taken: + + ✅ Enforce TLS 1.3 + + Update your server configuration to support and prefer TLS 1.3, which includes modern security features and mitigates known vulnerabilities found in older versions. + + ✅ Configure Go’s TLS Library + + Set `MinVersion` in the `tls.Config` struct to `tls.VersionTLS13` to ensure that the server only accepts TLS 1.3 connections. + + ```go + import ( + "crypto/tls" + "log" + "net/http" + "time" + ) + + func main() { + cert, err := tls.LoadX509KeyPair("server.crt", "server.key") + if err != nil { + log.Fatalf("failed to load key pair: %s", err) + } + + cfg := &tls.Config{ + Certificates: []tls.Certificate{cert}, + MinVersion: tls.VersionTLS13, // Enforce TLS 1.3 + } + + srv := &http.Server{ + Addr: ":8999", // Listen on port 8999 + TLSConfig: cfg, + ReadTimeout: time.Minute, + WriteTimeout: time.Minute, + } + + log.Printf("Server is starting...") + log.Fatal(srv.ListenAndServeTLS("", "")) // TLS cert and key are already provided in the TLSConfig + } + ``` + + ✅ Perfect Forward Secrecy (PFS) + + TLS 1.3 configurations ensure PFS by default, which protects past communications even if future session keys are compromised. + + ✅ Regularly Update Dependencies + + Keep your Go version and dependencies up-to-date to benefit from the latest security fixes and improvements. + + ❌ Do Not Use Deprecated TLS Versions + + Avoid configuring your server to accept TLS 1.0 or 1.1. Remove these options from your TLS configuration to prevent downgrade attacks. + + ## Resources + + - [IETF's Deprecation of TLS 1.0 and 1.1](https://tools.ietf.org/html/rfc8996) + - [OWASP TLS Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/TLS_Security_Cheat_Sheet.html) + - [Go `crypto/tls` package documentation](https://pkg.go.dev/crypto/tls) + + cwe_id: + - 310 + id: go_gosec_crypto_weak_tls_version + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_tls_version diff --git a/rules/go/gosec/file_permissions/file_perm.yml b/rules/go/gosec/file_permissions/file_perm.yml new file mode 100644 index 000000000..f93e0a2a3 --- /dev/null +++ b/rules/go/gosec/file_permissions/file_perm.yml @@ -0,0 +1,85 @@ +patterns: + - pattern: | + os.$($<...>$) + filters: + - variable: METHOD + values: + - Chmod + - OpenFile + - WriteFile + - variable: MASK + detection: go_gosec_file_permissions_file_perm_mask + scope: result +auxiliary: + - id: go_gosec_file_permissions_file_perm_mask + patterns: + - pattern: $ + filters: + - either: + - variable: MASK + regex: \A07 + - variable: MASK + regex: \A0\d[1-7] + - variable: MASK + regex: \A0\d\d[1-7] +languages: + - go +metadata: + description: "Incorrect permission assignment for critical resource" + remediation_message: | + ## Description + + When creating or updating files, ensuring proper file permissions is crucial to maintain security. Overly permissive file settings can allow unauthorized users to read, modify, or execute files, which could lead to information disclosure, data tampering, or compromise of the system. + + ## Remediations + + To prevent unauthorized access, it's important to set restrictive file permissions, particularly when sensitive data is involved. Here's how to manage file permissions in Go: + + ✅ Restrict File Permissions + + Set file permissions to allow only the necessary access level for the application. Avoid using permissions like `0777`, which allows read, write, and execute permissions for all users. + + ✅ Use Go’s `os` Package + + Utilize the `os.OpenFile` function with the appropriate file permission flags. + + ✅ Recommended File Permissions + + - `0400` grants read-only access to the file's owner. + - `0200` grants write-only access to the file's owner. + - `0600` grants read and write access to the file's owner and is commonly used for files that need to be both read from and written to by the application. + + ```go + import ( + "log" + "os" + ) + + func main() { + // Use os.OpenFile to create a file with restricted permissions + // 0600 permission: Read and write for the owner, no permissions for others + f, err := os.OpenFile("file.txt", os.O_CREATE|os.O_WRONLY, 0600) + if err != nil { + log.Fatalf("failed to create file: %s", err) + } + defer f.Close() + // Continue to work with the file here + } + ``` + + ✅ Verify File Permissions + + After file creation, check the file permissions to ensure they are set correctly. + + ✅ Secure Default Permissions + + If you are developing an application that creates multiple files, consider setting umask in your application to a secure default. + + ✅ Review File Permission Settings + + Regularly audit file permissions to ensure they adhere to the principle of least privilege. + + cwe_id: + - 732 + id: go_gosec_file_permissions_file_perm + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_file_permissions_file_perm diff --git a/rules/go/gosec/file_permissions/mkdir.yml b/rules/go/gosec/file_permissions/mkdir.yml new file mode 100644 index 000000000..fb420f55b --- /dev/null +++ b/rules/go/gosec/file_permissions/mkdir.yml @@ -0,0 +1,77 @@ +patterns: + - pattern: | + os.$($<...>$) + filters: + - variable: METHOD + values: + - Mkdir + - MkdirAll + - variable: MASK + detection: go_gosec_file_permissions_mkdir_mask + scope: result +auxiliary: + - id: go_gosec_file_permissions_mkdir_mask + patterns: + - pattern: $ + filters: + - variable: MASK + regex: \A077 +languages: + - go +metadata: + description: "Incorrect permission assignment for critical resource" + remediation_message: | + ## Description + + Setting correct directory permissions is critical to maintaining the security of a system. Directories with overly permissive access rights can become a vector for security breaches, allowing unauthorized users to add, remove, or change files, potentially leading to the execution of malicious code, data leaks, or system compromise. + + ## Remediations + + When creating directories, apply the principle of least privilege to ensure users have only the permissions necessary for their role: + + ✅ Restrict Directory Permissions + + Use permissions that restrict access to what is strictly necessary for the operation of the application. Avoid overly permissive settings such as `0777`, which allow all users to read, write, and execute. + + ✅ Use Go’s `os` Package + + Leverage the `os.Mkdir` or `os.MkdirAll` function with appropriate permission flags to create directories. + + ✅ Recommended Directory Permissions + + - `0700` gives the owner read, write, and execute permissions, with no access for group and others, suitable for private user data. + - `0750` gives the owner full permissions, the group read and execute permissions, and no permissions for others, which is commonly used for directories that need to be shared within a group. + + ```go + import ( + "log" + "os" + ) + + func main() { + // Use os.Mkdir to create a directory with restricted permissions + // 0700 permission: Full control for the owner, no permissions for group and others + err := os.Mkdir("secure_directory", 0700) + if err != nil { + log.Fatalf("failed to create directory: %s", err) + } + // Continue setting up the directory here + } + ``` + + ✅ Verify Directory Permissions + + After creating a directory, confirm the permissions to ensure they have been set correctly. + + ✅ Set Secure Umask + + Consider setting a secure umask in your application or user profile to ensure that all newly created files and directories have restrictive permissions by default. + + ✅ Regular Auditing + + Implement regular checks of directory permissions as part of your security auditing procedures to identify and correct any permissions that are too broad. + + cwe_id: + - 732 + id: go_gosec_file_permissions_mkdir + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_file_permissions_mkdir diff --git a/rules/go/gosec/filesystem/decompression_bomb.yml b/rules/go/gosec/filesystem/decompression_bomb.yml new file mode 100644 index 000000000..622d93af1 --- /dev/null +++ b/rules/go/gosec/filesystem/decompression_bomb.yml @@ -0,0 +1,119 @@ +patterns: + - pattern: | + io.$($<...>$$<...>) + filters: + - variable: METHOD + values: + - Copy + - CopyBuffer + - either: + - variable: READER + detection: go_gosec_filesystem_decompression_bomb_reader + scope: result + - variable: READER + detection: go_gosec_filesystem_decompression_bomb_reader_file_open + scope: result +auxiliary: + - id: go_gosec_filesystem_decompression_bomb_reader_file_open + patterns: + - pattern: $.Open() + filters: + - variable: FILE + detection: go_gosec_filesystem_decompression_bomb_reader_file + scope: result + - id: go_gosec_filesystem_decompression_bomb_reader_file + patterns: + - pattern: $.File + filters: + - variable: READER + detection: go_gosec_filesystem_decompression_bomb_reader + scope: result + - id: go_gosec_filesystem_decompression_bomb_reader + patterns: + - gzip.NewReader() + - zlib.NewReader() + - bzip2.NewReader() + - flate.NewReader() + - lzw.Reader() + - tar.NewReader() + - zip.NewReader() + - zlib.NewReaderDict() + - flate.NewReaderDict() + - zip.OpenReader() +languages: + - go +metadata: + description: "Use of a Broken or Risky Cryptographic Algorithm" + remediation_message: | + ## Description + + Decompression bombs are a form of attack against an application or service that processes compressed files. The attacker crafts a compressed file that is small in size, but when decompressed, expands to a much larger size that is disproportionate to the original. This can exhaust system resources like CPU, memory, or disk space, leading to a Denial of Service (DoS). + + ## Remediations + + Implement measures to mitigate the impact of decompression bombs: + + ✅ Limit Decompression Size + + Use `io.LimitReader` to restrict the amount of data that a reader will decompress. This prevents the decompression of large files that could fill up memory or disk space. + + ✅ Monitor Resource Usage + + Implement resource monitoring to watch for unexpected spikes in CPU, memory, or disk usage, which could indicate an attempted decompression bomb attack. + + ✅ Input Validation + + Validate the size and type of the input before decompressing. If possible, reject files that do not meet expected criteria. + + ✅ Fail Safely + + Ensure that your application can handle errors from the decompression process safely, without crashing or becoming unresponsive. + + ✅ Regular Updates + + Keep compression libraries up to date with the latest security patches to protect against known vulnerabilities. + + ✅ User Education + + Educate users about the risks of decompression bombs if they are able to upload compressed files. + + ```go + import ( + "compress/gzip" + "io" + "log" + "os" + ) + + func main() { + // Open the gzip file + f, err := os.Open("example.gz") + if err != nil { + log.Fatal(err) + } + defer f.Close() + + // Create a gzip reader on the file + r, err := gzip.NewReader(f) + if err != nil { + log.Fatal(err) + } + defer r.Close() + + // Define a limit for decompression + const maxDecompressSize = 10 * 1024 * 1024 // 10 MB + + // Limit the size of the reader + limitedReader := io.LimitReader(r, maxDecompressSize) + + // Use the limited reader to decompress, preventing the decompression bomb from expanding fully + if _, err := io.Copy(os.Stdout, limitedReader); err != nil { + log.Fatal(err) + } + } + ``` + + cwe_id: + - 327 + id: go_gosec_filesystem_decompression_bomb + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_filesystem_decompression_bomb diff --git a/rules/go/gosec/filesystem/dirtraversal.yml b/rules/go/gosec/filesystem/dirtraversal.yml new file mode 100644 index 000000000..a983f8c54 --- /dev/null +++ b/rules/go/gosec/filesystem/dirtraversal.yml @@ -0,0 +1,81 @@ +patterns: + - pattern: | + $.Dir("/") + filters: + - variable: NET + detection: go_gosec_filesystem_dirtraversal_init + scope: cursor +auxiliary: + - id: go_gosec_filesystem_dirtraversal_init + patterns: + - pattern: import $"net/http" + - pattern: | + import ( + $"net/http" + ) +languages: + - go +metadata: + description: "Relative path traversal" + remediation_message: | + ## Description + + Mounting the root directory (`/`) in an HTTP server is a significant security risk. It potentially allows anyone with access to the HTTP service to browse and access system files, which can lead to information disclosure, data breaches, or further exploitation of the system. + + ## Remediations + + Implement the following measures to prevent exposing the entire filesystem through your web server: + + ✅ Serve Specific Directory + + Change the `http.Dir` to serve files from a specific, safe directory intended for public access rather than the root directory. Ensure this directory contains only the files that are meant to be publicly accessible. + + ✅ Access Control + + Apply appropriate permissions to the directory being served to ensure that the server process can only access the files that it's supposed to serve. + + ✅ Use of Configuration Files + + If supported, use configuration files like `.htaccess` (for Apache HTTP Server) or equivalent server configuration to control access to directories. + + ✅ Isolate Environment + + Consider running your server in a containerized or virtualized environment with strict access controls to limit the potential damage in case of a security breach. + + ✅ Regular Audits + + Perform regular audits of the filesystem and the files being served to ensure that no sensitive information is being unintentionally exposed. + + ```go + import ( + "net/http" + "log" + ) + + func main() { + // Define the specific path to a directory to be served + const safePath = "/var/www/html/public" + + // Create a new file server handler that serves files from the safePath + fs := http.FileServer(http.Dir(safePath)) + + // Configure the server to handle requests to the root with the file server handler + http.Handle("/", http.StripPrefix("/", fs)) + + // Start the server + log.Fatal(http.ListenAndServe(":9000", nil)) + } + ``` + + ## Resources + + - [Go Documentation: http package](https://pkg.go.dev/net/http) + - [OWASP: Securing File Uploads](https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload) + - [NIST Guidelines on Securing Public Web Servers](https://csrc.nist.gov/publications/detail/sp/800-44/version-2/final) + - [Docker Documentation: Use containers for isolation](https://docs.docker.com/get-started/overview/#use-containers-for-isolation) + - [Linux man page for chmod (file permissions)](https://linux.die.net/man/1/chmod) + + cwe_id: + - 327 + id: go_gosec_filesystem_dirtraversal + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_filesystem_dirtraversal diff --git a/rules/go/gosec/filesystem/filereadtaint.yml b/rules/go/gosec/filesystem/filereadtaint.yml new file mode 100644 index 000000000..614a348de --- /dev/null +++ b/rules/go/gosec/filesystem/filereadtaint.yml @@ -0,0 +1,129 @@ +imports: + - go_shared_lang_dynamic_request_input +patterns: + - pattern: | + os.OpenFile($$<...>) + filters: + - variable: INPUT + detection: go_gosec_filesystem_filereadtaint_input + scope: result + - pattern: | + os.Open($$<...>) + filters: + - variable: INPUT + detection: go_gosec_filesystem_filereadtaint_input + scope: result + - pattern: | + ioutil.ReadFile($$<...>) + filters: + - variable: INPUT + detection: go_gosec_filesystem_filereadtaint_input + scope: result + - pattern: | + os.ReadFile($$<...>) + filters: + - variable: INPUT + detection: go_gosec_filesystem_filereadtaint_input + scope: result +auxiliary: + - id: go_gosec_filesystem_filereadtaint_input + sanitizer: go_gosec_filesystem_filereadtaint_input_sanitizer + patterns: + - pattern: $ + filters: + - variable: INIT + detection: go_shared_lang_dynamic_request_input + scope: cursor + - id: go_gosec_filesystem_filereadtaint_input_sanitizer + patterns: + - pattern: filepath.Clean() + - pattern: filepath.Rel() + - pattern: strconv.Itoa() +languages: + - go +metadata: + description: "Improper limitation of a pathname to a restricted directory ('Path Traversal')" + remediation_message: | + ## Description + + Constructing file or path information dynamically, especially from user input, poses a significant security risk. If not handled correctly, attackers could manipulate these paths to access or manipulate sensitive files, leading to data breaches or system compromise. It is crucial to ensure that user input is not used directly to interact with the file system, as this can be exploited to perform path traversal attacks. + + ## Remediations + + To mitigate the risks associated with dynamic file path construction from user inputs, apply the following best practices: + + ✅ Hash or Replace User Input + + When dealing with user input for file operations, hash the input or replace it with a system-generated unique identifier to prevent path manipulation. + + ✅ Use `filepath.Base` + + Extract only the filename from the path, ignoring any directory information, to avoid directory traversal vulnerabilities. + + ✅ Validate Paths Before Use + + Always perform validation on the resolved paths before accessing files to ensure they are within the expected directory. + + ```go + import ( + "crypto/rand" + "encoding/hex" + "io" + "log" + "path/filepath" + "strings" + ) + + // userData struct holds user-related data with a unique ID for file operations + type userData struct { + id string // Unique identifier for the filename + userFilename string // Original filename from the user, kept for reference + } + + // newUserData constructs a new userData instance with a random file ID + func newUserData(userFilename string) userData { + return userData{ + id: randomFileID(), // Use a random ID instead of user-provided filename + userFilename: userFilename, + } + } + + // randomFileID generates a secure 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() { + // Simulated user input, which may be malicious + data := newUserData("../../possibly/malicious") + + // Define a safe base path for file operations + const basePath = "/tmp/" + + // Resolve the full path using the safe base path and the random ID + resolvedPath, err := filepath.Join(basePath, filepath.Base(data.id)) + if err != nil { + log.Fatal(err) + } + + // Ensure the resolved path is within our designated base path + if !strings.HasPrefix(resolvedPath, basePath) { + log.Fatal("The resolved path does not start with the expected base path") + } + + // The file can now be safely accessed using resolvedPath + // Further file processing code would go here + } + ``` + + ## Resources + + - [OWASP Guide to Preventing Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal) + cwe_id: + - 327 + id: go_gosec_filesystem_filereadtaint + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_filesystem_filereadtaint diff --git a/rules/go/gosec/filesystem/poor_write_permissions.yml b/rules/go/gosec/filesystem/poor_write_permissions.yml new file mode 100644 index 000000000..4e7d9787f --- /dev/null +++ b/rules/go/gosec/filesystem/poor_write_permissions.yml @@ -0,0 +1,75 @@ +patterns: + - pattern: | + ioutil.WriteFile($<...>$) + filters: + - variable: MASK + detection: go_gosec_filesystem_poor_write_permissions_mask + scope: result +auxiliary: + - id: go_gosec_filesystem_poor_write_permissions_mask + patterns: + - pattern: $ + filters: + - either: + - variable: MASK + regex: \A07 + - variable: MASK + regex: \A0\d[1-7] + - variable: MASK + regex: \A0\d\d[1-7] +languages: + - go +metadata: + description: "Incorrect default permissions" + remediation_message: | + ## Description + + The application has been detected setting file permissions that are too permissive. This configuration could allow unauthorized users to read, write, or execute files, potentially leading to information disclosure or other security vulnerabilities. + + ## Remediations + + To enhance security, file permissions should be set to more restrictive values, especially when the files contain sensitive information: + + ✅ Use Restrictive File Permissions + + Assign file permissions to limit access appropriately based on the application's requirements. + + - `0400`: Grants read-only access to the file for the owner. + - `0200`: Grants write-only access to the file for the owner. + - `0600`: Grants read and write access to the file for the owner. + + ✅ Apply Permissions During File Creation + + When creating or modifying files, set the appropriate permissions to prevent unauthorized access. + + ```go + import ( + "os" + "log" + ) + + func main() { + // Data to be written to the file + dat := []byte("sensitive data") + + // Write the data to 'file.txt' with read and write permissions for the owner only + if err := os.WriteFile("file.txt", dat, 0600); err != nil { + log.Fatalf("failed to write file: %s", err) + } + // File is now safely written with restricted permissions + } + ``` + + ✅ Review File Permission Settings + + Regularly audit the permissions of files to ensure they conform to the principle of least privilege. + + ## Resources + + - [Go Documentation for os Package](https://pkg.go.dev/os) + - [Linux 'chmod' Command](https://linux.die.net/man/1/chmod) + - [OWASP File Handling Best Practices](https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html) + cwe_id: + - 276 + id: go_gosec_filesystem_poor_write_permissions + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_filesystem_poor_write_permissions diff --git a/rules/go/gosec/filesystem/tempfile.yml b/rules/go/gosec/filesystem/tempfile.yml new file mode 100644 index 000000000..e0dcee613 --- /dev/null +++ b/rules/go/gosec/filesystem/tempfile.yml @@ -0,0 +1,83 @@ +patterns: + - pattern: | + os.Create($) + filters: + - variable: PATH + detection: go_gosec_filesystem_tempfile_path + scope: result + - pattern: | + $.WriteFile($$<...>) + filters: + - variable: PACKAGE + values: + - ioutil + - os + - variable: PATH + detection: go_gosec_filesystem_tempfile_path + scope: result +auxiliary: + - id: go_gosec_filesystem_tempfile_path + patterns: + - pattern: $ + filters: + - variable: PATH + regex: (/tmp/.*|/var/tmp/.*) +languages: + - go +metadata: + description: "Incorrect default permissions" + remediation_message: | + ## Description + + The application has been observed creating files in shared system temporary directories, such as `/tmp` or `/var/tmp`, without the use of secure functions like `os.CreateTemp`. This practice is insecure because it opens up the possibility of symlink attacks, where an attacker could anticipate the temporary file name and create a symlink to a target file, leading to unauthorized file creation or overwriting when the application writes to what it believes is a temporary file. + + ## Remediations + + To prevent symlink attacks and other vulnerabilities associated with the use of shared temporary directories: + + ✅ Use Secure Temporary File Creation + + Implement `os.CreateTemp` to safely create temporary files within a directory that's restricted to the application. This reduces the risk of symlink attacks and ensures that temporary files are handled securely. + + ```go + import ( + "os" + "log" + ) + + func main() { + // Ensure the application-restricted directory exists with appropriate permissions + restrictedDir := "/opt/appdir/restricted" + if err := os.MkdirAll(restrictedDir, 0700); err != nil { + log.Fatalf("failed to create restricted directory: %s", err) + } + + // Securely create a temporary file within the restricted directory + f, err := os.CreateTemp(restrictedDir, "temp-*.txt") + if err != nil { + log.Fatalf("failed to create temporary file: %s", err) + } + defer f.Close() // Ensure the file is closed when no longer needed + + defer os.Remove(f.Name()) // Clean up the file upon exit + // Continue working with the temporary file + } + ``` + + ✅ Avoid Shared Temporary Directories for Sensitive Operations + + Do not use common temporary directories for storing sensitive information or for operations that require secure handling of files. + + ✅ Clean Up After Use + + Always remove temporary files after their use to prevent accumulation and potential misuse. + + ## Resources + + - [Go Documentation: os.CreateTemp](https://pkg.go.dev/os#CreateTemp) + - [Secure Coding Guidelines for File I/O](https://wiki.sei.cmu.edu/confluence/display/seccode/TOCTOU+Race+Conditions) + - [OWASP Guide to File System Security](https://owasp.org/www-community/controls/Path_Traversal) + cwe_id: + - 378 + id: go_gosec_filesystem_tempfile + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_filesystem_tempfile diff --git a/rules/go/gosec/filesystem/ziparchive.yml b/rules/go/gosec/filesystem/ziparchive.yml new file mode 100644 index 000000000..22494b690 --- /dev/null +++ b/rules/go/gosec/filesystem/ziparchive.yml @@ -0,0 +1,144 @@ +patterns: + - pattern: | + filepath.Join($<...>$$<...>) + filters: + - either: + - variable: READER + detection: go_gosec_filesystem_ziparchive_reader + scope: result + - variable: READER + detection: go_gosec_filesystem_ziparchive_file_name + scope: result +auxiliary: + - id: go_gosec_filesystem_ziparchive_file_name + patterns: + - pattern: $.Name + filters: + - either: + - variable: FILE + detection: go_gosec_filesystem_ziparchive_reader_file + scope: result + - variable: FILE + detection: go_gosec_filesystem_ziparchive_zip_file + scope: result + - id: go_gosec_filesystem_ziparchive_reader_file + patterns: + - pattern: $.File + filters: + - variable: READER + detection: go_gosec_filesystem_ziparchive_reader + scope: result + - id: go_gosec_filesystem_ziparchive_reader + patterns: + - zip.OpenReader() + - id: go_gosec_filesystem_ziparchive_zip_file + patterns: + - pattern: func $<_>($<...>$$<_> *zip.File$<...>) {} +languages: + - go +metadata: + description: "Improper limitation of a pathname to a restricted directory ('Path Traversal')" + remediation_message: | + ## Description + + The application is at risk of a path traversal vulnerability, commonly known as 'Zip Slip', when extracting files from untrusted archives. Maliciously crafted archive files can contain relative path specifications that lead to writing files outside the intended directory when extracted, potentially overwriting system files or placing unauthorized files in critical paths. + + ## Remediations + + To safeguard against 'Zip Slip' and related exploitation techniques, follow these security practices: + + ✅ Limit Archive Size + + Implement checks to ensure the zip archive's size does not exceed a maximum threshold, preventing 'Zip Bombs'—archives that decompress to disproportionately large sizes. + + ✅ Generate Unique Filenames + + Avoid using the original filenames from the archive. If necessary, use only the base name after sanitizing or, better yet, generate a unique name to prevent intentional overwrites. + + ✅ Validate Extraction Paths + + Confirm that extracted files are written to a specified, trusted directory and do not traverse outside of this directory. + + ✅ Disallow Symbolic Links + + Only process regular files. Exclude symbolic links to prevent indirect file read/write vulnerabilities. + + ```go + import ( + "archive/zip" + "io" + "log" + "os" + "path/filepath" + "strings" + ) + + func main() { + // Open the zip file for reading + r, err := zip.OpenReader("trusted.zip") + if err != nil { + log.Fatal(err) + } + defer r.Close() + + // Set up restrictions and base path + const ( + expectedFileCount = 10 + totalAllowedSize = 10 * 1024 * 1024 // 10MB + maxFileSize = 1024 * 1024 // 1MB + basePath = "/var/restricted/" + ) + + // Calculate total size of uncompressed files + var totalSize uint64 + for _, f := range r.File { + totalSize += f.UncompressedSize64 + } + + // Check if total size exceeds the limit + if totalSize > totalAllowedSize { + log.Fatalf("archive exceeds total allowed size: %d\n", totalSize) + } + + // Process files in the archive + for _, f := range r.File { + // Skip overlarge files + if f.UncompressedSize64 > maxFileSize { + log.Printf("skipping file as it exceeds maxFileSize: %s\n", f.Name) + continue + } + + // Skip if not a regular file + if !f.Mode().IsRegular() { + log.Printf("skipping non-regular file: %s\n", f.Name) + continue + } + + // Securely resolve the file path + name := filepath.Base(f.Name) + resolvedPath, err := filepath.Join(basePath, name) + if err != nil { + log.Fatal(err) + } + + // Ensure the file does not traverse outside the base path + if !strings.HasPrefix(resolvedPath, basePath) { + log.Fatal("path does not start with basePath") + } + + // Extract and process the file (omitted for brevity) + } + } + ``` + + For processing directories within the zip archive, ensure to clean the path and validate it strictly against the base path. + + ## Resources + + - [OWASP Cheat Sheet: Zip Slip Vulnerability](https://cheatsheetseries.owasp.org/cheatsheets/Path_Traversal_Cheat_Sheet.html) + - [Go Documentation: archive/zip package](https://pkg.go.dev/archive/zip) + + cwe_id: + - 22 + id: go_gosec_filesystem_ziparchive + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_filesystem_ziparchive diff --git a/rules/go/gosec/http/http_serve.yml b/rules/go/gosec/http/http_serve.yml new file mode 100644 index 000000000..79dd2c14b --- /dev/null +++ b/rules/go/gosec/http/http_serve.yml @@ -0,0 +1,82 @@ +patterns: + - pattern: $.$() + filters: + - variable: METHOD + values: + - ListenAndServe + - ListenAndServeTLS + - Serve + - ServeTLS + - variable: HTTP_INIT + detection: go_gosec_http_http_serve_http_init + scope: result +auxiliary: + - id: go_gosec_http_http_serve_http_init + patterns: + - import $"net" + - import $"net/http" + - pattern: | + import ( + $"net" + ) + - pattern: | + import ( + $"net/http" + ) +languages: + - go +metadata: + description: "Uncontrolled resource consumption" + remediation_message: | + ## Description + + The `net/http` serve functions in Go, when used with default settings, are vulnerable to resource consumption attacks. Attackers can exploit this by creating numerous connections to the server, intentionally not completing data transfers or leaving connections open, which can exhaust the server's resources and prevent it from accepting new legitimate connections. + + ## Remediations + + To mitigate such attacks, specific server configurations are necessary: + + ❌ Avoid Default Serve Functions for Production + + Functions like `http.ListenAndServe` and `http.Serve` should not be used in a production setting as they do not allow for timeout configurations. + + ✅ Configure Timeouts on Custom `http.Server` Object + + Create a custom `http.Server` object and set appropriate timeouts to prevent resource exhaustion. + + ```go + import ( + "net/http" + "time" + "log" + ) + + func main() { + srv := &http.Server{ + Addr: "localhost:8000", + ReadHeaderTimeout: 15 * time.Second, + ReadTimeout: 15 * time.Second, + WriteTimeout: 10 * time.Second, + IdleTimeout: 30 * time.Second, + } + + if err := srv.ListenAndServe(); err != nil { + log.Fatal(err) + } + } + ``` + + ✅ Use `http.TimeoutHandler` for Per Request Timeouts + + To set timeouts for individual requests, use the `http.TimeoutHandler` wrapper on your handlers. This ensures that the server does not wait indefinitely for a request to complete. + + ## Resources + + - [http.Server Timeouts Documentation](https://pkg.go.dev/net/http#Server) + - [Guide to Setting Request-Based Timeouts](https://pkg.go.dev/net/http#TimeoutHandler) + - [Understanding the Slowloris Attack](https://en.wikipedia.org/wiki/Slowloris_(computer_security)) + + cwe_id: + - 400 + id: go_gosec_http_http_serve + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_http_http_serve diff --git a/rules/go/gosec/http/http_slowloris.yml b/rules/go/gosec/http/http_slowloris.yml new file mode 100644 index 000000000..2abcb7f66 --- /dev/null +++ b/rules/go/gosec/http/http_slowloris.yml @@ -0,0 +1,83 @@ +patterns: + - pattern: $.ListenAndServe() + filters: + - variable: HTTP_INIT + detection: go_gosec_http_http_slowloris_http_init + scope: cursor + - not: + either: + - variable: HTTP_INIT + detection: go_gosec_http_http_slowloris_http_slowloris_read_timeout + scope: cursor + - variable: HTTP_INIT + detection: go_gosec_http_http_slowloris_http_slowloris_read_header_timeout + scope: cursor +auxiliary: + - id: go_gosec_http_http_slowloris_http_slowloris_read_timeout + patterns: + - pattern: | + &http.Server{$<...>$ReadTimeout: $<_>$<...>} + - id: go_gosec_http_http_slowloris_http_slowloris_read_header_timeout + patterns: + - pattern: | + &http.Server{$<...>$ReadHeaderTimeout: $<_>$<...>} + - id: go_gosec_http_http_slowloris_http_init + patterns: + - pattern: | + &http.Server{} +languages: + - go +metadata: + description: "Uncontrolled resource consumption (Slowloris)" + remediation_message: | + ## Description + + The server configuration lacks a `ReadHeaderTimeout`, making it vulnerable to a Slowloris attack. This type of attack occurs when an attacker opens multiple connections to the server but sends only partial requests. The server keeps each connection open, waiting for the headers to be completed, ultimately leading to resource exhaustion. + + ## Remediations + + To protect against such attacks, the following steps should be taken: + + ❌ Avoid Default Serve Functions for Production + + Do not use `http.ListenAndServe` and `http.Serve` in a production environment, as they do not support timeout settings. + + ✅ Configure `http.Server` with Timeouts + + Establish a custom `http.Server` instance with appropriate timeouts to prevent attackers from exploiting the lack of `ReadHeaderTimeout`. + + ```go + import ( + "net/http" + "time" + "log" + ) + + func main() { + srv := &http.Server{ + Addr: "localhost:8000", + ReadHeaderTimeout: 15 * time.Second, + ReadTimeout: 15 * time.Second, + WriteTimeout: 10 * time.Second, + IdleTimeout: 30 * time.Second, + } + + if err := srv.ListenAndServe(); err != nil { + log.Fatal(err) + } + } + ``` + + ✅ Enforce Request Timeouts + + Implement `http.TimeoutHandler` to apply timeouts to individual HTTP handlers, which starts counting down only after the headers have been read. + + ## Resources + + - [Configuring Timeouts in http.Server](https://pkg.go.dev/net/http#Server) + - [How to Set Request-Based Timeouts](https://pkg.go.dev/net/http#TimeoutHandler) + - [Understanding Slowloris Attacks](https://en.wikipedia.org/wiki/Slowloris_(computer_security)) + cwe_id: + - 400 + id: go_gosec_http_http_slowloris + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_http_http_slowloris diff --git a/rules/go/gosec/injection/ssrf_injection.yml b/rules/go/gosec/injection/ssrf_injection.yml new file mode 100644 index 000000000..38684eab3 --- /dev/null +++ b/rules/go/gosec/injection/ssrf_injection.yml @@ -0,0 +1,128 @@ +imports: + - go_shared_lang_dynamic_input +patterns: + - pattern: http.Get($) + filters: + - variable: URL + detection: go_shared_lang_dynamic_input + - pattern: http.Head($) + filters: + - variable: URL + detection: go_shared_lang_dynamic_input + - pattern: http.Post($, $<...>) + filters: + - variable: URL + detection: go_shared_lang_dynamic_input + - pattern: http.PostForm($, $<...>) + filters: + - variable: URL + detection: go_shared_lang_dynamic_input + - pattern: http.NewRequest($<...>$$<...>) + filters: + - variable: URL + detection: go_shared_lang_dynamic_input +languages: + - go +metadata: + description: "Server Side Request Forgery (SSRF)" + remediation_message: | + ## Description + + Server-Side Request Forgery (SSRF) is a security vulnerability that occurs when a server-side application makes HTTP requests to arbitrary URLs controlled by the user. SSRF can be exploited by attackers to target internal systems behind firewalls that are otherwise inaccessible from the external network, by tricking the server into making requests to these systems. + + ## Remediations + + To mitigate SSRF vulnerabilities, follow these guidelines: + + ✅ Validate User Input + + Avoid using direct user input to construct URLs for backend requests. If you must use user input, validate or sanitize it rigorously. + + ✅ Restrict URLs to Known Safe Domains + + Where possible, limit requests to a predefined set of safe URLs or domains. This can be done using server-side mapping from user-supplied keys to URLs. + + ✅ Implement IP Safelists and Blocklists + + Use an HTTP client that allows customizing and blocking specific IP ranges, such as private network addresses and other non-routable IP ranges. + + ✅ Use Network-Level Security + + If the HTTP client doesn't support IP range blocking, consider running it with restricted system permissions, or within a secure network where firewall rules can block dangerous addresses. + + ✅ Leverage a Secure HTTP Proxy + + As a last resort, route all backend HTTP requests through a secure proxy that can filter out and block requests to potentially harmful addresses. + + ```go + import ( + "context" + "crypto/tls" + "errors" + "net" + "net/http" + "time" + ) + + // IsDisallowedIP checks if an IP address falls within a range of disallowed IPs. + func IsDisallowedIP(hostIP string) bool { + ip := net.ParseIP(hostIP) + // Add more checks as necessary + return ip.IsMulticast() || ip.IsUnspecified() || ip.IsLoopback() || ip.IsPrivate() + } + + // SafeTransport defines a custom transport that filters out disallowed IP addresses. + 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) { + c.Close() + 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) { + c.Close() + return nil, errors.New("ip address is not allowed") + } + return c, c.Handshake() + }, + TLSHandshakeTimeout: timeout, + } + } + + // httpRequest performs a secure HTTP request, filtering out disallowed IPs. + 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() + // Process response + } + ``` + + ## Resources + + - [OWASP SSRF Prevention Cheat Sheet](https://owasp.org/www-community/attacks/Server_Side_Request_Forgery) + + cwe_id: + - 918 + id: go_gosec_injection_ssrf_injection + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_injection_ssrf_injection diff --git a/rules/go/gosec/injection/subproc_injection.yml b/rules/go/gosec/injection/subproc_injection.yml new file mode 100644 index 000000000..04fde540f --- /dev/null +++ b/rules/go/gosec/injection/subproc_injection.yml @@ -0,0 +1,95 @@ +imports: + - go_shared_lang_dynamic_input +patterns: + - pattern: exec.CommandContext($<_>, $$<...>) + filters: + - variable: EXE + detection: go_shared_lang_dynamic_input + - pattern: exec.Command($$<...>) + filters: + - variable: EXE + detection: go_shared_lang_dynamic_input + - pattern: syscall.ForkExec($$<...>) + filters: + - variable: EXE + detection: go_shared_lang_dynamic_input + - pattern: syscall.StartProcess($$<...>) + filters: + - variable: EXE + detection: go_shared_lang_dynamic_input +languages: + - go +metadata: + description: "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')" + remediation_message: | + ## Description + + OS command injection is a severe security vulnerability that occurs when an application incorrectly processes user input. This flaw can allow attackers to execute arbitrary commands on the host operating system, potentially leading to a full system compromise. + + ## Remediations + + Prevent OS command injection by adhering to the following practices: + + ❌ Avoid Direct User Input + + Do not use user-supplied information for constructing OS commands or command-line arguments, as this can lead to command injection vulnerabilities. + + ✅ Implement Input Validation + + Ensure that any user input is validated against a set of strict rules to ensure it does not contain malicious characters or patterns. + + ✅ Use Hardcoded Arguments + + When invoking OS commands, use a hardcoded set of arguments to ensure that user input cannot alter the command's behavior. + + ✅ Utilize Temporary Files Securely + + When dealing with files, create temporary files in a restricted directory, avoiding the use of user-supplied filenames. + + ✅ Employ Native Libraries + + Where possible, use native libraries or features of the programming language instead of invoking shell commands, which can be safer and more efficient. + + ```go + import ( + "io/ioutil" + "os/exec" + "log" + ) + + func main() { + userData := []byte("user data") + + // Create a temporary file in a secure, application-specific directory + f, err := ioutil.TempFile("/var/app/restricted", "temp-*.dat") + if err != nil { + log.Fatal(err) + } + + // Write user data to the temporary file + if _, err := f.Write(userData); err != nil { + f.Close() + log.Fatal(err) + } + + // Close the file handle + if err := f.Close(); err != nil { + log.Fatal(err) + } + + // Execute a command using the temporary file, avoiding direct user input for filenames + out, err := exec.Command("/bin/cat", f.Name()).Output() + if err != nil { + log.Fatal(err) + } + // Output can be used for further processing + } + ``` + + ## Resources + + - [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html) + cwe_id: + - 95 + id: go_gosec_injection_subproc_injection + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_injection_subproc_injection diff --git a/rules/go/gosec/injection/template_injection.yml b/rules/go/gosec/injection/template_injection.yml new file mode 100644 index 000000000..a5215516b --- /dev/null +++ b/rules/go/gosec/injection/template_injection.yml @@ -0,0 +1,95 @@ +imports: + - go_shared_lang_dynamic_input +patterns: + - pattern: template.HTML($) + filters: + - variable: IN + detection: go_shared_lang_dynamic_input + - pattern: template.JS($) + filters: + - variable: IN + detection: go_shared_lang_dynamic_input + - pattern: template.URL($) + filters: + - variable: IN + detection: go_shared_lang_dynamic_input + - pattern: template.HTMLAttr($) + filters: + - variable: IN + detection: go_shared_lang_dynamic_input +languages: + - go +metadata: + description: "Improper neutralization of input during web page generation ('Cross-site Scripting')" + remediation_message: | + ## Description + + Cross-Site Scripting (XSS) is a vulnerability that allows attackers to run malicious scripts in the context of a trusted web application. This can happen when an application includes untrusted data without proper validation or escaping. There are several contexts where XSS can occur, each requiring specific encoding strategies to mitigate the risk. + + ## Remediations + + To defend against XSS attacks, consider the following measures: + + ✅ Encode Based on Context + + When user input is reflected back in HTML, ensure it is encoded based on the context in which it is used (e.g., HTML content, HTML attributes, JavaScript context, CSS context, etc.). + + ✅ Template Safely + + Utilize templating engines that automatically encode data based on context, and be cautious not to override these safeguards. + + ✅ Sanitize Data + + Use libraries or functions designed to sanitize user input, particularly when inserting content into a web page. + + ✅ Separate Data from Code + + Avoid inline scripting and event handlers, and instead use separate JavaScript files to handle events. This reduces the risk of script injection through event attributes. + + ✅ Avoid Mixing Templating Systems + + Do not mix server-side and client-side templating systems, as server-side systems may not escape output in a way that is safe for client-side use. + + ❌ Do Not Encode Before Storing + + Avoid encoding user input before storing it in a database. The encoding should be applied when the data is output, not before storage, to ensure that it is encoded appropriately for its context. + + Here's an example of using Go’s `html/template` package to safely render HTML content: + + ```go + import ( + "html/template" + "os" + "log" + ) + + func main() { + // Define a template with a function to safely render HTML + testTemplate, err := template.New("testTemplate").Funcs(template.FuncMap{ + "SafeHTML": func() template.HTML { + const safeHTML = "
hardcoded, safe html
" + return template.HTML(safeHTML) + }, + }).Parse(`{{ SafeHTML }}`) + + if err != nil { + log.Fatal(err) + } + + // Execute the template and ensure proper encoding + if err := testTemplate.Execute(os.Stdout, nil); err != nil { + log.Fatal(err) + } + } + ``` + + ## Resources + + - [OWASP XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html) + - [Go html/template Documentation](https://pkg.go.dev/html/template) + - [CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')](https://cwe.mitre.org/data/definitions/79.html) + + cwe_id: + - 79 + id: go_gosec_injection_template_injection + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_injection_template_injection diff --git a/rules/go/gosec/leak/pprof_endpoint.yml b/rules/go/gosec/leak/pprof_endpoint.yml new file mode 100644 index 000000000..a3979bd8d --- /dev/null +++ b/rules/go/gosec/leak/pprof_endpoint.yml @@ -0,0 +1,62 @@ +patterns: + - import "net/http/pprof" + - pattern: | + import ( + $<_>"net/http/pprof" + ) +languages: + - go +metadata: + description: "Active debug code (pprof enabled)" + remediation_message: | + ## Description + + Go's standard library includes a profiling tool that can be enabled by importing `net/http/pprof`. This tool provides a `/debug/pprof` endpoint that exposes runtime profiling data over HTTP. When enabled in a production environment, it can present a significant security risk as it lacks authentication controls and can potentially leak sensitive information about the application's runtime state and environment. + + ## Remediations + + To prevent unintended exposure of profiling information: + + ✅ Remove `net/http/pprof` in Production + + Before deploying your application to a production environment, remove any import statements for `net/http/pprof` from your codebase. Ensure that the profiling endpoint is not available in the live environment. + + ```go + // +build !production + + package main + + import ( + _ "net/http/pprof" // Ensure this line is not present in your production builds + "net/http" + ) + + func main() { + // ... your application code ... + + // Start the server (omit the pprof import and handler in production) + log.Println(http.ListenAndServe("localhost:6060", nil)) + } + ``` + + ✅ Conditional Compilation + + Use build tags to include profiling only in non-production builds. + + ✅ Use Environment Configurations + + Configure environment-specific settings to conditionally enable or disable the profiling endpoints. + + ✅ Implement Authentication + + If profiling is necessary in a controlled production scenario, secure the endpoint with strong authentication mechanisms. + + ## Resources + + - [Go net/http/pprof Package Documentation](https://pkg.go.dev/net/http/pprof) + - [Go Build Constraints Documentation](https://pkg.go.dev/go/build#hdr-Build_Constraints) + - [OWASP Security by Design Principles](https://owasp.org/www-project-security-by-design/) + cwe_id: + - 918 + id: go_gosec_leak_pprof_endpoint + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_leak_pprof_endpoint diff --git a/rules/go/gosec/memory/integer_overflow.yml b/rules/go/gosec/memory/integer_overflow.yml new file mode 100644 index 000000000..6f81fd210 --- /dev/null +++ b/rules/go/gosec/memory/integer_overflow.yml @@ -0,0 +1,71 @@ +patterns: + - pattern: $($) + filters: + - variable: INT + values: + - int16 + - int32 + - variable: X + detection: go_gosec_memory_integer_overflow_source +auxiliary: + - id: go_gosec_memory_integer_overflow_source + patterns: + - $$<_>$<...> := strconv.Atoi($<...>) +languages: + - go +metadata: + description: "Integer overflow or wraparound" + remediation_message: | + ## Description + + In Go, the size of the `int` type varies with the system architecture: it's 32 bits on a 32-bit system and 64 bits on a 64-bit system. This variability can lead to integer overflow issues when a value returned from `strconv.Atoi` is cast to a smaller integer type, such as `int32` or `int16`, and the original number exceeds the maximum value that can be stored in the smaller type. Integer overflow can cause erratic behavior and potentially serious bugs. + + ## Remediations + + To prevent integer overflow and ensure safe type conversion: + + ✅ Check Values Before Conversion + + Before casting an `int` to a smaller type, compare it against the maximum values that the target type can hold. + + ```go + import ( + "strconv" + "fmt" + "log" + "math" + ) + + func main() { + // Convert the string to an int + bigValue, err := strconv.Atoi("32768") + if err != nil { + log.Fatal(err) + } + + // Ensure the value does not exceed int16's maximum limit + if bigValue > math.MaxInt16 { + log.Fatal("value too large to fit in int16") + } + + // Safely convert to int16 + value := int16(bigValue) + fmt.Println(value) + } + ``` + + ✅ Use Appropriate Types + + Where possible, use fixed-size types like `int32` or `int64` to avoid overflow issues related to architecture-dependent sizes. + + ✅ Handle Errors + + Always handle errors returned from conversion functions like `strconv.Atoi` to detect and manage conversion issues immediately. + + ## Resources + + - [Go math package for integer limits](https://pkg.go.dev/math#pkg-constants) + cwe_id: + - 190 + id: go_gosec_memory_integer_overflow + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_memory_integer_overflow diff --git a/rules/go/gosec/memory/math_big_rat.yml b/rules/go/gosec/memory/math_big_rat.yml new file mode 100644 index 000000000..0d94f91f1 --- /dev/null +++ b/rules/go/gosec/memory/math_big_rat.yml @@ -0,0 +1,72 @@ +patterns: + - pattern: $.SetString() + filters: + - variable: RAT + detection: go_gosec_memory_math_big_rat_init + scope: cursor +auxiliary: + - id: go_gosec_memory_math_big_rat_init + patterns: + - pattern: $.Rat{} + filters: + - variable: BIG_INIT + detection: go_gosec_memory_math_big_init + scope: result + - id: go_gosec_memory_math_big_init + patterns: + - import $"math/big" + - | + import ( + $"math/big" + ) +languages: + - go +metadata: + description: "Integer Overflow or Wraparound" + remediation_message: | + ## Description + + When converting strings to integers using `strconv.Atoi` in Go, there's a risk of integer overflow if the result is assigned to a smaller integer type such as `int16` or `int32`. The size of the default `int` type in Go is platform-dependent—64 bits on a 64-bit system and 32 bits on a 32-bit system. Overflow can occur when the value returned from `strconv.Atoi` exceeds the range of the target integer type. + + ## Remediations + + ✅ Check Before Conversion + + Always verify that the value returned from `strconv.Atoi` is within the range of the target type before conversion. + + ```go + if intValue, err := strconv.Atoi(stringValue); err == nil { + if intValue >= math.MinInt16 && intValue <= math.MaxInt16 { + int16Value := int16(intValue) + // Use int16Value safely + } + } + ``` + + ✅ Use Specific Type Conversion Functions + + Use type-specific parsing functions such as `strconv.ParseInt` with the appropriate bit size to directly obtain the desired type. + + ```go + if int64Value, err := strconv.ParseInt(stringValue, 10, 16); err == nil { + int16Value := int16(int64Value) + // Use int16Value safely + } + ``` + + ❌ Avoid Blind Type Casting + + Do not cast the result of `strconv.Atoi` to a smaller integer type without validating that the value fits within the smaller type's range. + + ❌ Don't Ignore Errors + + Never ignore the error returned by `strconv.Atoi`. Always handle it to catch conversion issues, including potential overflows. + + ## Resources + + - [Go strconv package](https://pkg.go.dev/strconv) + - [Go math package for min/max constants](https://pkg.go.dev/math#pkg-constants) + cwe_id: + - 190 + id: go_gosec_memory_math_big_rat + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_memory_math_big_rat diff --git a/rules/go/gosec/memory/memory_aliasing.yml b/rules/go/gosec/memory/memory_aliasing.yml new file mode 100644 index 000000000..3a93007d4 --- /dev/null +++ b/rules/go/gosec/memory/memory_aliasing.yml @@ -0,0 +1,65 @@ +patterns: + - pattern: "&$" + filters: + - variable: ARG + detection: go_gosec_memory_memory_aliasing_range + scope: cursor + - not: + variable: ARG + detection: go_gosec_memory_memory_aliasing_range_return + scope: cursor +auxiliary: + - id: go_gosec_memory_memory_aliasing_range_return + patterns: + - return $<...>&$$<_>$<...> + - id: go_gosec_memory_memory_aliasing_range + patterns: + - pattern: | + for $<...>$$$<...> := range $<_> {} +languages: + - go +metadata: + description: "Incorrect access of indexable resource ('Range Error')" + remediation_message: | + ## Description + + Go's `for ... range` constructs allocate a single iteration variable for the loop's duration, which can cause confusion when addresses of this variable are stored or used beyond a single iteration. Since the iteration variable's address remains constant, subsequent iterations overwrite the previously referenced values, leading to unexpected results, particularly when using go routines or deferred functions within the loop. + + ## Remediations + + ✅ Create a New Variable Inside the Loop + + Declare a new local variable within the loop's scope to hold the iteration value. This ensures a unique address is used for each iteration. + + ```go + for _, n := range []someStruct{{1}, {2}, {3}, {4}} { + localVar := n + // Use localVar instead of n + } + ``` + + ✅ Use Indexed Addressing + + Instead of the iteration variable, directly reference the indexed element within the array or slice. + + ```go + for i := range mySlice { + // Use &mySlice[i] to obtain a stable address + } + ``` + + ❌ Do Not Store the Address of the Iteration Variable + + Avoid taking the address of the iteration variable and storing it, as it leads to all references pointing to the same memory location. + + ❌ Avoid Using the Iteration Variable's Address in Goroutines + + Using the iteration variable's address directly in goroutines can cause race conditions or logical errors, as the variable's value may change before the goroutine accesses it. + + ## Resources + + - [Go For Statements](https://go.dev/ref/spec#For_statements) + cwe_id: + - 118 + id: go_gosec_memory_memory_aliasing + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_memory_memory_aliasing diff --git a/rules/go/gosec/network/bind_to_all_interfaces.yml b/rules/go/gosec/network/bind_to_all_interfaces.yml new file mode 100644 index 000000000..1717bbd3b --- /dev/null +++ b/rules/go/gosec/network/bind_to_all_interfaces.yml @@ -0,0 +1,61 @@ +patterns: + - pattern: $.Listen($<...>$) + filters: + - variable: PACKAGE + values: + - net + - tls + - variable: ADDR + regex: 0\.0\.0\.0:[0-9]+ +languages: + - go +metadata: + description: "Exposure of sensitive information to an unauthorized actor" + remediation_message: | + ## Description + + Binding to "0.0.0.0" allows a service to accept connections on all network interfaces. While this can be useful for services meant to be widely accessible, it can also unintentionally expose the service on network interfaces that are not secure or intended for such traffic, potentially leading to security vulnerabilities. + + ## Remediations + + To mitigate the risks associated with binding to all network interfaces: + + ✅ Bind to a Specific Interface + + Configure your service to listen on a specific IP address or network interface. This can be controlled through: + + - **Environment Variable**: Use an environment variable to specify the IP address, making the configuration more flexible and secure. + - **Configuration File**: Define the IP address in a configuration file which the application reads at startup. + - **Programmatic Identification**: Programmatically determine the appropriate network interface and bind the service to its IP address. + + ```go + import ( + "net" + "os" + "log" + ) + + func main() { + // Retrieve the IP address from an environment variable + addr := os.Getenv("IP_ADDRESS") + + // Listen on the specified interface + listener, err := net.Listen("tcp", addr) + if err != nil { + log.Fatalf("Failed to listen on %s: %v", addr, err) + } + + // Continue to set up your server (e.g., http.Serve(listener, handler)) + } + ``` + + ✅ **Security Best Practices**: Always follow security best practices when configuring network services, such as using firewalls to restrict access and encrypting traffic with TLS. + + ## Resources + + - [Go net package](https://pkg.go.dev/net) + - [Go os package for environment variables](https://pkg.go.dev/os) + cwe_id: + - 200 + id: go_gosec_network_bind_to_all_interfaces + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_network_bind_to_all_interfaces diff --git a/rules/go/gosec/secrets/secrets.yml b/rules/go/gosec/secrets/secrets.yml new file mode 100644 index 000000000..24c2ab50b --- /dev/null +++ b/rules/go/gosec/secrets/secrets.yml @@ -0,0 +1,93 @@ +patterns: + - pattern: | + $ := $ + filters: + - variable: NAME + detection: go_gosec_secrets_secrets_name + scope: cursor_strict + - variable: STRING_LITERAL + detection: go_gosec_secrets_secrets_literal + scope: cursor + - pattern: | + var $ = $ + filters: + - variable: NAME + detection: go_gosec_secrets_secrets_name + scope: cursor_strict + - variable: STRING_LITERAL + detection: go_gosec_secrets_secrets_literal + scope: cursor + - pattern: | + $ = $ + filters: + - variable: NAME + detection: go_gosec_secrets_secrets_name + scope: cursor_strict + - variable: STRING_LITERAL + detection: go_gosec_secrets_secrets_literal + scope: cursor +auxiliary: + - id: go_gosec_secrets_secrets_name + patterns: + - pattern: $ + filters: + - variable: NAME + regex: (?i)(password|api_?key|secret)\b + - id: go_gosec_secrets_secrets_literal + patterns: + - pattern: $ + filters: + - variable: STRING + detection: string_literal + scope: cursor_strict + - not: + variable: STRING + string_regex: \A[*]+\z + - variable: STRING + entropy_greater_than: 3.5 +languages: + - go +severity: high +metadata: + description: "Use of hard-coded password" + remediation_message: | + ## Description + + Storing sensitive information such as secret keys, passwords, or API tokens directly in source code can lead to security vulnerabilities. This practice makes it easy for malicious actors to access these secrets if the codebase is exposed or improperly accessed. + + ## Remediations + + To protect sensitive information: + + ✅ Dynamic Secret Retrieval + + Implement mechanisms to retrieve secrets dynamically at runtime from a secure source rather than hardcoding them in the source files. + + ✅ Environment Variables + + Use environment variables to inject secrets into the application at runtime, keeping them out of the codebase. + + ✅ Secrets Management Systems + + Utilize dedicated secrets management tools and services that securely store and manage sensitive information. + + ✅ Encrypted Configuration Files + + Store secrets in configuration files that are encrypted and decrypt them at runtime within the application. + + ✅ Access Control + + Ensure that the storage location for secrets has strict access controls to prevent unauthorized access. + + ✅ Audit and Rotate Secrets + + Regularly audit access to secrets and rotate them to minimize the risk if they are compromised. + + ## Resources + + - [OWASP: Use of Hard-coded Passwords](https://owasp.org/www-community/vulnerabilities/Use_of_hard-coded_password) + - [OWASP: Secrets Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html#21-high-availability) + cwe_id: + - 798 + id: go_gosec_secrets_secrets + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_secrets_secrets diff --git a/rules/go/gosec/sql/concat_sqli.yml b/rules/go/gosec/sql/concat_sqli.yml new file mode 100644 index 000000000..415b58163 --- /dev/null +++ b/rules/go/gosec/sql/concat_sqli.yml @@ -0,0 +1,109 @@ +imports: + - go_shared_lang_dynamic_request_input +patterns: + - pattern: | + $.Query($) + filters: + - variable: INPUT + detection: go_gosec_sql_concat_sqli_sanitized_input + - not: + variable: INPUT + detection: go_gosec_sql_concat_sqli_input_sprintf_sanitizer + - either: + - variable: DB + detection: go_gosec_sql_concat_sqli_sql_open + scope: cursor + - variable: DB + detection: go_gosec_sql_concat_sqli_sql_db_begin + scope: cursor + - pattern: | + $.QueryContext($<...>$) + filters: + - variable: INPUT + detection: go_gosec_sql_concat_sqli_sanitized_input + - either: + - variable: DB + detection: go_gosec_sql_concat_sqli_sql_open + scope: cursor + - variable: DB + detection: go_gosec_sql_concat_sqli_sql_db_begin + scope: cursor +auxiliary: + - id: go_gosec_sql_concat_sqli_input_sprintf_sanitizer + patterns: + - pattern: fmt.Sprintf($$<...>) + filters: + - variable: STRING + string_regex: (SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE).*%[bdoxXfFp].* + - id: go_gosec_sql_concat_sqli_input_sanitizer + patterns: + - pattern: $<_>.QuoteIdentifier($$<_>) + - id: go_gosec_sql_concat_sqli_sanitized_input + sanitizer: go_gosec_sql_concat_sqli_input_sanitizer + patterns: + - pattern: $ + filters: + - variable: INPUT + detection: go_shared_lang_dynamic_request_input + scope: cursor + - id: go_gosec_sql_concat_sqli_sql_db_begin + patterns: + - pattern: $.Begin() + filters: + - variable: SQL + detection: go_gosec_sql_concat_sqli_sql_open + scope: cursor + - id: go_gosec_sql_concat_sqli_sql_open + patterns: + - pattern: $.Open() + filters: + - variable: SQL + detection: go_gosec_sql_concat_sqli_sql_init + scope: cursor + - id: go_gosec_sql_concat_sqli_sql_init + patterns: + - pattern: import $"database/sql" + - pattern: | + import ( + $"database/sql" + ) +languages: + - go +severity: high +metadata: + description: "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')" + remediation_message: | + ## Description + + SQL Injection represents a severe vulnerability that can culminate in the compromise of data or entire systems. When SQL query strings are crafted dynamically based on user inputs, there's potential for malicious users to manipulate the logic of the SQL statement. Such tampering could provide adversaries unauthorized access to sensitive data or even allow them to execute system-level operations or code. + + ## Remediations + + ✅ Use Parameterized Queries + + Always opt for parameterized queries over dynamically generated SQL queries to prevent SQL injection. + + ```go + rows, err := db.Query("SELECT * FROM users WHERE userName = ?", userName) + if err != nil { + return nil, err + } + defer rows.Close() + for rows.Next() { + // ... process rows + } + ``` + + ✅ Avoid Direct User Input in Dynamic Queries + + If there's an absolute need to formulate dynamic queries, ensure that direct user input is never utilized. Instead, leverage a map or dictionary containing valid values and determine them through a user-provided key. + + For instance, certain database drivers do not support parameterized queries for operators like `>` or `<`. Instead of directly using user-input values, allow users to provide substitutes like `gt` for `>` and `lt` for `<`. Subsequently, use these alphabetical inputs to retrieve the actual operators for your query. Implement a similar approach for queries requiring non-parameterizable column or table names. + + ## Resources + + - [OWASP SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html) + cwe_id: + - 89 + id: go_gosec_sql_concat_sqli + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_sql_concat_sqli diff --git a/rules/go/gosec/subproc/subproc.yml b/rules/go/gosec/subproc/subproc.yml new file mode 100644 index 000000000..d198b8017 --- /dev/null +++ b/rules/go/gosec/subproc/subproc.yml @@ -0,0 +1,82 @@ +imports: + - go_shared_lang_dynamic_request_input +patterns: + - pattern: | + exec.CommandContext($, $$<...>) + filters: + - variable: INPUT + detection: go_shared_lang_dynamic_request_input + - pattern: | + exec.Command($$<...>) + filters: + - variable: INPUT + detection: go_shared_lang_dynamic_request_input + - pattern: | + syscall.ForkExec($$<...>) + filters: + - variable: INPUT + detection: go_shared_lang_dynamic_request_input + - pattern: | + syscall.StartProcess($$<...>) + filters: + - variable: INPUT + detection: go_shared_lang_dynamic_request_input +languages: + - go +severity: high +metadata: + description: "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')" + remediation_message: | + ## Description + + OS command injection is a perilous vulnerability that has the potential to lead to full system compromise. Adversaries may exploit this flaw by feeding arbitrary commands or arguments intended for execution. This opens the door for unchecked operations, which could wreak havoc on the system or reveal sensitive information. + + ## Remediations + + ✅ Avoid User Input in OS Commands + + Always steer clear of incorporating user input when formulating commands or their arguments, especially for functions responsible for OS command execution. This includes, but is not limited to, filenames provided during user uploads/downloads. + + ✅ Hardcoded Argument Set + + Ensure your application exclusively uses a hardcoded set of arguments for OS command executions. If filenames are being passed to such functions, consider adopting a hash of the filename or another distinctive identifier. + + ✅ Opt for Native Libraries + + Due to the inherent risks associated with third-party commands and the possibility of undisclosed attack vectors, prefer using native libraries that offer the same capabilities as opposed to resorting to OS system commands. + + ✅ Specify Full Path in Windows + + If the environment is Windows-based, always provide the complete path information when denoting the OS command. This circumvents potential vulnerabilities stemming from untrusted search paths (CWE-426). + + ```go + 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) + } + + // use the absolute path to the binary and the name of the temporary file + // steering clear of any user-provided filenames + out, err := exec.Command("/bin/cat", f.Name()).Output() + if err != nil { + log.Fatal(err) + } + ``` + + ## Resources + + - [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html) + cwe_id: + - 95 + id: go_gosec_subproc_subproc + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_subproc_subproc diff --git a/rules/go/gosec/unsafe/unsafe.yml b/rules/go/gosec/unsafe/unsafe.yml new file mode 100644 index 000000000..d6fdbbe94 --- /dev/null +++ b/rules/go/gosec/unsafe/unsafe.yml @@ -0,0 +1,43 @@ +imports: + - go_shared_lang_dynamic_request_input +patterns: + - unsafe.Alignof() + - unsafe.Offsetof() + - unsafe.Sizeof() + - unsafe.Pointer() +languages: + - go +metadata: + description: "Use of inherently dangerous function (unsafe package)" + remediation_message: | + ## Description + + The Go programming language features the `unsafe` package which grants low-level memory management capabilities, inclusive of direct memory access and pointer manipulation. Though the `unsafe` package can be quite potent, its usage sidesteps the Go compiler's type safety checks. This can lead to an array of security vulnerabilities and unpredictable system behavior. + + ## Remediations + + ✅ Avoid `unsafe` Unless Absolutely Necessary + + The overarching guidance here is to steer clear of the `unsafe` package unless there's an absolute necessity for its functions. When opting for low-level memory operations, ensure that their implications are well-understood and that their deployment is preceded by rigorous testing. + + ✅ Be Wary of Buffer Overflows + + Direct manipulation of memory can lead to buffer overflows, potentially enabling unauthorized code execution. Ensure buffer boundaries are always respected. + + ✅ Avoid Use After Free + + Accessing memory that has already been freed can result in unintended code execution or unpredictable behaviors. Ensure that once memory has been freed, it isn't accessed further. + + ✅ Prevent Information/Memory Leaks + + Unintended memory retention or unintended disclosure of information in memory can occur when using unsafe functions. This can compromise other security defenses or lead to system failures due to exhausted memory. Regularly review and audit your code to check for such leaks. + + ## Resources + + - [Buffer Overflows - OWASP](https://owasp.org/www-community/vulnerabilities/Buffer_Overflow) + - [Using Freed Memory - OWASP](https://owasp.org/www-community/vulnerabilities/Using_freed_memory) + - [Memory Leaks - OWASP](https://owasp.org/www-community/vulnerabilities/Memory_leak) + cwe_id: + - 242 + id: go_gosec_unsafe_unsafe + documentation_url: https://docs.bearer.com/reference/rules/go_gosec_unsafe_unsafe diff --git a/rules/go/shared/lang/dynamic_input.yml b/rules/go/shared/lang/dynamic_input.yml new file mode 100644 index 000000000..333d7ae73 --- /dev/null +++ b/rules/go/shared/lang/dynamic_input.yml @@ -0,0 +1,31 @@ +type: shared +languages: + - go +patterns: + - pattern: $ + filters: + - variable: INPUT + detection: go_shared_lang_dymamic_input_input + scope: cursor_strict + - pattern: $.ReadString() + filters: + - variable: READER + detection: go_shared_lang_dynamic_input_reader + scope: cursor +auxiliary: + - id: go_shared_lang_dynamic_input_reader + patterns: + - pattern: bufio.NewReader($) + filters: + - variable: INPUT + detection: go_shared_lang_dymamic_input_input + scope: result + - id: go_shared_lang_dymamic_input_input + patterns: + - os.Stdin + - os.Args + - os.Getenv() + - func $<_>($<...>$$ $<_>$<...>) +metadata: + description: "Go dynamic input." + id: go_shared_lang_dynamic_input diff --git a/rules/go/shared/lang/dynamic_request_input.yml b/rules/go/shared/lang/dynamic_request_input.yml new file mode 100644 index 000000000..b44dbda8b --- /dev/null +++ b/rules/go/shared/lang/dynamic_request_input.yml @@ -0,0 +1,61 @@ +type: shared +imports: + - go_shared_lang_dynamic_input +languages: + - go +patterns: + - pattern: $ + filters: + - variable: INPUT + detection: go_shared_lang_dynamic_input + scope: cursor_strict + - pattern: $ + filters: + - variable: INPUT + detection: go_shared_lang_dymamic_request_input_input + scope: cursor_strict + - pattern: $.ReadString() + filters: + - variable: READER + detection: go_shared_lang_dynamic_request_input_reader + scope: cursor +auxiliary: + - id: go_shared_lang_dymamic_request_input_input + patterns: + - pattern: $.$<_>() + filters: + - variable: QUERY + detection: go_shared_lang_dynamic_request_input_query + scope: cursor + - id: go_shared_lang_dynamic_request_input_reader + patterns: + - pattern: bufio.NewReader($) + filters: + - variable: INPUT + detection: go_shared_lang_dymamic_request_input_input + scope: result + - id: go_shared_lang_dynamic_request_input_query + patterns: + - pattern: $.Query() + filters: + - variable: REQUEST_URL + detection: go_shared_lang_dynamic_request_input_request_url + scope: cursor + - id: go_shared_lang_dynamic_request_input_request_url + patterns: + - pattern: $.URL + filters: + - variable: REQUEST + detection: go_shared_lang_dynamic_request_input_request + scope: cursor + - id: go_shared_lang_dynamic_request_input_request + patterns: + - func($<...>$$<_> *http.Request$<...>) {} + - func ($<...>$$<_> http.Request$<...>) {} + - func $<_>($<...>$$<_> *http.Request$<...>) {} + - func $<_>($<...>$$<_> http.Request$<...>) {} + - var $$<_> *http.Request + - var $$<_> http.Request +metadata: + description: "Go dynamic request input." + id: go_shared_lang_dynamic_request_input diff --git a/tests/go/gosec/LICENSE b/tests/go/gosec/LICENSE new file mode 100644 index 000000000..f49a4e16e --- /dev/null +++ b/tests/go/gosec/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/tests/go/gosec/README.md b/tests/go/gosec/README.md new file mode 100644 index 000000000..80e4738cf --- /dev/null +++ b/tests/go/gosec/README.md @@ -0,0 +1,3 @@ +## License + +The `gosec` folder and its subfolder are under Apache 2 License diff --git a/tests/go/gosec/blocklist/cgi/__snapshots__/test.js.snap b/tests/go/gosec/blocklist/cgi/__snapshots__/test.js.snap new file mode 100644 index 000000000..53cbcdd5c --- /dev/null +++ b/tests/go/gosec/blocklist/cgi/__snapshots__/test.js.snap @@ -0,0 +1,83 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_blocklist_cgi test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_blocklist_cgi", + "title": "Use of a Broken or Risky Cryptographic Algorithm", + "description": "## Description\\n\\nUsing the \`net/http/cgi\` package in Go, especially with versions prior to 1.6.3, exposes the application to the Httpoxy attack, a vulnerability identified as CVE-2016-5386. This vulnerability arises from the way CGI and FastCGI protocols handle certain environment variables, which can be manipulated to intercept and redirect outgoing HTTP requests made by the web application.\\n\\n## Remediations\\n\\n✅ Update Go Version\\n\\nEnsure you are using a version of Go that is 1.6.3 or later, where this vulnerability is patched.\\n\\n\`\`\`sh\\n# Check Go version and update if necessary\\ngo version\\n# Follow Go's update instructions if your version is < 1.6.3\\n\`\`\`\\n\\n✅ Use Alternative Packages\\n\\nRefrain from using CGI where possible. Utilize alternative packages and methods to handle HTTP requests which do not rely on the CGI protocol.\\n\\n\`\`\`go\\n// Use the standard net/http package instead\\nimport \\"net/http\\"\\n\`\`\`\\n\\n❌ Don't Use \`net/http/cgi\` in Older Versions\\n\\nDo not use the \`net/http/cgi\` package if you are operating on Go versions older than 1.6.3 as they are susceptible to the Httpoxy vulnerability.\\n\\n\`\`\`go\\n// This import is vulnerable to Httpoxy in Go < 1.6.3\\nimport \\"net/http/cgi\\"\\n\`\`\`\\n\\n❌ Avoid Exposing Environment Variables\\n\\nEnsure that the environment variables such as \`HTTP_PROXY\` are not being exposed unintentionally, as this can be leveraged for Httpoxy attacks.\\n\\n## Resources\\n\\n- [CVE-2016-5386 Detail](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5386)\\n- [Httpoxy.org](https://httpoxy.org/)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_blocklist_cgi", + "line_number": 3, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 3, + "end": 3, + "column": { + "start": 1, + "end": 22 + } + }, + "sink": { + "start": 3, + "end": 3, + "column": { + "start": 1, + "end": 22 + }, + "content": "import \\"net/http/cgi\\"" + }, + "parent_line_number": 3, + "snippet": "import \\"net/http/cgi\\"", + "fingerprint": "d44ba4fd1dac6349b308c573d7831ecc_0", + "old_fingerprint": "f4afdcd85f5f01a0d80ee3f3337e2888_0", + "code_extract": "import \\"net/http/cgi\\"" + } + ] +}" +`; + +exports[`go_gosec_blocklist_cgi test 2 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_blocklist_cgi", + "title": "Use of a Broken or Risky Cryptographic Algorithm", + "description": "## Description\\n\\nUsing the \`net/http/cgi\` package in Go, especially with versions prior to 1.6.3, exposes the application to the Httpoxy attack, a vulnerability identified as CVE-2016-5386. This vulnerability arises from the way CGI and FastCGI protocols handle certain environment variables, which can be manipulated to intercept and redirect outgoing HTTP requests made by the web application.\\n\\n## Remediations\\n\\n✅ Update Go Version\\n\\nEnsure you are using a version of Go that is 1.6.3 or later, where this vulnerability is patched.\\n\\n\`\`\`sh\\n# Check Go version and update if necessary\\ngo version\\n# Follow Go's update instructions if your version is < 1.6.3\\n\`\`\`\\n\\n✅ Use Alternative Packages\\n\\nRefrain from using CGI where possible. Utilize alternative packages and methods to handle HTTP requests which do not rely on the CGI protocol.\\n\\n\`\`\`go\\n// Use the standard net/http package instead\\nimport \\"net/http\\"\\n\`\`\`\\n\\n❌ Don't Use \`net/http/cgi\` in Older Versions\\n\\nDo not use the \`net/http/cgi\` package if you are operating on Go versions older than 1.6.3 as they are susceptible to the Httpoxy vulnerability.\\n\\n\`\`\`go\\n// This import is vulnerable to Httpoxy in Go < 1.6.3\\nimport \\"net/http/cgi\\"\\n\`\`\`\\n\\n❌ Avoid Exposing Environment Variables\\n\\nEnsure that the environment variables such as \`HTTP_PROXY\` are not being exposed unintentionally, as this can be leveraged for Httpoxy attacks.\\n\\n## Resources\\n\\n- [CVE-2016-5386 Detail](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5386)\\n- [Httpoxy.org](https://httpoxy.org/)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_blocklist_cgi", + "line_number": 3, + "full_filename": "/tmp/bearer-scan/main2.go", + "filename": ".", + "source": { + "start": 3, + "end": 5, + "column": { + "start": 1, + "end": 2 + } + }, + "sink": { + "start": 3, + "end": 5, + "column": { + "start": 1, + "end": 2 + }, + "content": "import (\\n\\tcgi2 \\"net/http/cgi\\"\\n)" + }, + "parent_line_number": 3, + "snippet": "import (\\n\\tcgi2 \\"net/http/cgi\\"\\n)", + "fingerprint": "d44ba4fd1dac6349b308c573d7831ecc_0", + "old_fingerprint": "08ab0d9c254c63555f4b1118aaa6548a_0", + "code_extract": "import (\\n\\tcgi2 \\"net/http/cgi\\"\\n)" + } + ] +}" +`; diff --git a/tests/go/gosec/blocklist/cgi/test.js b/tests/go/gosec/blocklist/cgi/test.js new file mode 100644 index 000000000..feb638403 --- /dev/null +++ b/tests/go/gosec/blocklist/cgi/test.js @@ -0,0 +1,16 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) + + test("test 2", () => { + const testCase = "main2.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/blocklist/cgi/testdata/main.go b/tests/go/gosec/blocklist/cgi/testdata/main.go new file mode 100644 index 000000000..81de73fc0 --- /dev/null +++ b/tests/go/gosec/blocklist/cgi/testdata/main.go @@ -0,0 +1,9 @@ +package main + +import "net/http/cgi" + +func maincgi() { + if err := cgi.Serve(nil); err != nil { + return + } +} diff --git a/tests/go/gosec/blocklist/cgi/testdata/main2.go b/tests/go/gosec/blocklist/cgi/testdata/main2.go new file mode 100644 index 000000000..22de59e9e --- /dev/null +++ b/tests/go/gosec/blocklist/cgi/testdata/main2.go @@ -0,0 +1,11 @@ +package main + +import ( + cgi2 "net/http/cgi" +) + +func maincgi2() { + if err := cgi2.Serve(nil); err != nil { + return + } +} diff --git a/tests/go/gosec/blocklist/des/__snapshots__/test.js.snap b/tests/go/gosec/blocklist/des/__snapshots__/test.js.snap new file mode 100644 index 000000000..ec4604197 --- /dev/null +++ b/tests/go/gosec/blocklist/des/__snapshots__/test.js.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_blocklist_des test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_blocklist_des", + "title": "Use of a Broken or Risky Cryptographic Algorithm", + "description": "## Description\\n\\nThe Data Encryption Standard (DES) is an outdated symmetric-key algorithm for encryption. Officially deemed insecure and no longer recommended for use, DES was withdrawn by the National Institute of Standards and Technology (NIST) as a standard in 2005, primarily because of its 56-bit key size, which is vulnerable to brute-force attacks.\\n\\n## Remediation\\n\\nTo ensure the confidentiality and integrity of sensitive data, it is crucial to utilize a modern and secure encryption algorithm. The use of Advanced Encryption Standard (AES) with a key size of 256 bits (AES-256) is recommended for its strong security properties and widespread acceptance as a replacement for DES.\\n\\n✅ Implement AES-256 for Strong Encryption\\n\\n\`\`\`go\\n// Use AES-256 for secure encryption by initializing a 32-byte key for AES-256\\nkey := make([]byte, 32)\\nif _, err := io.ReadFull(rand.Reader, key); err != nil {\\n log.Fatal(err)\\n}\\n\\n// Create a new cipher block from the key\\nblockCipher, err := aes.NewCipher(key)\\nif err != nil {\\n log.Fatal(err)\\n}\\n\\n// Use Galois/Counter Mode (GCM) for both encryption and decryption\\naead, err := cipher.NewGCM(blockCipher)\\nif err != nil {\\n log.Fatal(err)\\n}\\n\\nvar encrypted = []byte{}\\nvar nonce = []byte{}\\n\\n// Encrypt a message with AES-256 using GCM\\n{\\n msg := []byte(\\"Some secret message\\")\\n // Ensure nonces are unique for each encryption to maintain security\\n nonce = make([]byte, 12)\\n if _, err := io.ReadFull(rand.Reader, nonce); err != nil {\\n log.Fatal(err)\\n }\\n encrypted = aead.Seal(nil, nonce, msg, nil)\\n}\\n\\n// Decrypt the message securely\\n{\\n msg, err := aead.Open(nil, nonce, encrypted, nil)\\n if err != nil {\\n log.Fatal(err)\\n }\\n fmt.Printf(\\"Decrypted: %s\\\\n\\", msg)\\n}\\n\`\`\`\\n\\n❌ Do Not Use Deprecated Algorithms\\nAvoid using deprecated cryptographic algorithms such as DES, as they do not provide adequate security against modern threats and attacks.\\n\\n## Resources\\n\\n- [NIST Recommendations](https://csrc.nist.gov/publications/detail/sp/800-131a/rev-2/final)\\n- [AES-256 Encryption](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_blocklist_des", + "line_number": 3, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 3, + "end": 6, + "column": { + "start": 1, + "end": 2 + } + }, + "sink": { + "start": 3, + "end": 6, + "column": { + "start": 1, + "end": 2 + }, + "content": "import (\\n\\t\\"crypto/des\\"\\n\\t\\"fmt\\"\\n)" + }, + "parent_line_number": 3, + "snippet": "import (\\n\\t\\"crypto/des\\"\\n\\t\\"fmt\\"\\n)", + "fingerprint": "5701c2af30f4ca2aaf2cb0bff61be1be_0", + "old_fingerprint": "c8850e419b899cc93c57917fbacacbf4_0", + "code_extract": "import (\\n\\t\\"crypto/des\\"\\n\\t\\"fmt\\"\\n)" + } + ] +}" +`; diff --git a/tests/go/gosec/blocklist/des/test.js b/tests/go/gosec/blocklist/des/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/blocklist/des/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/blocklist/des/testdata/main.go b/tests/go/gosec/blocklist/des/testdata/main.go new file mode 100644 index 000000000..5ee6576ef --- /dev/null +++ b/tests/go/gosec/blocklist/des/testdata/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "crypto/des" + "fmt" +) + +func maindes() { + k := []byte("super secret key") + block, err := des.NewCipher(k) + if err != nil { + return + } + out := make([]byte, 0) + in := []byte("cleartext") + block.Encrypt(out, in) + decrypted := make([]byte, 0) + block.Decrypt(decrypted, out) + fmt.Printf("Doing something with: %s\n", string(decrypted)) +} + +func main3des() { + k := []byte("super secret key") + block, err := des.NewTripleDESCipher(k) + if err != nil { + return + } + out := make([]byte, 0) + in := []byte("cleartext") + block.Encrypt(out, in) + decrypted := make([]byte, 0) + block.Decrypt(decrypted, out) + fmt.Printf("Doing something with: %s\n", string(decrypted)) +} diff --git a/tests/go/gosec/blocklist/md5/__snapshots__/test.js.snap b/tests/go/gosec/blocklist/md5/__snapshots__/test.js.snap new file mode 100644 index 000000000..108cc1241 --- /dev/null +++ b/tests/go/gosec/blocklist/md5/__snapshots__/test.js.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_blocklist_md5 test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_blocklist_md5", + "title": "Use of a Broken or Risky Cryptographic Algorithm", + "description": "## Description\\n\\nMD5 is a widely used cryptographic hash function that produces a 128-bit (16-byte) hash value. It's commonly used to check the integrity of files. However, MD5 is not collision-resistant; this means that different inputs may produce the same output hash. MD5's vulnerabilities and the feasibility of collision attacks have rendered it obsolete for security-related purposes, particularly digital signatures, SSL certificates, and cryptographic message authentication.\\n\\n## Remediation\\n\\nGiven the vulnerabilities of MD5, it is highly recommended to switch to more secure hashing algorithms. For hashing purposes that do not involve passwords, such as verifying file integrity or generating unique identifiers, SHA-3 or BLAKE2 can be used due to their stronger cryptographic properties.\\n\\n✅ Use SHA-3 or BLAKE2 for General Hashing Needs\\n\\n\`\`\`go\\n// BLAKE2 is a cryptographic hash function faster than MD5, SHA-1, and SHA-2, and as secure as the latest standard SHA-3\\nfileContents := []byte(\\"some file contents to create hash for\\")\\nblake2bHasher, err := blake2b.New512(nil)\\nif err != nil {\\n log.Fatal(err)\\n}\\nhashedValue := blake2bHasher.Sum(fileContents)\\nfmt.Printf(\\"%s\\\\n\\", hex.EncodeToString(hashedValue))\\n\`\`\`\\n\\nFor password hashing, where the hash functions need to be slow to combat brute-force attacks, bcrypt or Argon2id should be used. These algorithms are designed to be computationally intensive to hash and verify, which helps protect against password cracking attempts.\\n\\n✅ Adopt bcrypt or Argon2id for Password Hashing\\n\\nThe bcrypt algorithm is a good choice for password hashing as it allows you to adjust the cost (computational complexity) and is widely supported. Argon2id is the winner of the Password Hashing Competition and offers a good balance between resistance to GPU cracking attacks and usability.\\n\\n## Resources\\n\\n- [OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_blocklist_md5", + "line_number": 3, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 3, + "end": 6, + "column": { + "start": 1, + "end": 2 + } + }, + "sink": { + "start": 3, + "end": 6, + "column": { + "start": 1, + "end": 2 + }, + "content": "import (\\n\\t\\"crypto/md5\\"\\n\\t\\"fmt\\"\\n)" + }, + "parent_line_number": 3, + "snippet": "import (\\n\\t\\"crypto/md5\\"\\n\\t\\"fmt\\"\\n)", + "fingerprint": "eb186a281342cc12d763fe3c841dfc09_0", + "old_fingerprint": "389be4fd1aecab117b4e71d6dc81e0fe_0", + "code_extract": "import (\\n\\t\\"crypto/md5\\"\\n\\t\\"fmt\\"\\n)" + } + ] +}" +`; diff --git a/tests/go/gosec/blocklist/md5/test.js b/tests/go/gosec/blocklist/md5/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/blocklist/md5/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/blocklist/md5/testdata/main.go b/tests/go/gosec/blocklist/md5/testdata/main.go new file mode 100644 index 000000000..9da531607 --- /dev/null +++ b/tests/go/gosec/blocklist/md5/testdata/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "crypto/md5" + "fmt" +) + +func mainMD55() { + h := md5.New() + h.Write([]byte("stuff")) +} + +func mainMD5Sum() { + out := md5.Sum([]byte("stuff")) + fmt.Println(out) +} diff --git a/tests/go/gosec/blocklist/rc4/__snapshots__/test.js.snap b/tests/go/gosec/blocklist/rc4/__snapshots__/test.js.snap new file mode 100644 index 000000000..77ee87a53 --- /dev/null +++ b/tests/go/gosec/blocklist/rc4/__snapshots__/test.js.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_blocklist_rc4 test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_blocklist_rc4", + "title": "Use of a Broken or Risky Cryptographic Algorithm", + "description": "## Description\\n\\nRC4 is a stream cipher that was once popular for its simplicity and speed in operation. However, extensive research over the years has revealed multiple vulnerabilities, rendering RC4 insecure in most contexts. Its weaknesses in key scheduling and the generation of non-random bytes have led to successful cryptanalysis and practical attacks, making it unsuitable for securing data.\\n\\n## Remediation\\n\\nWith the known vulnerabilities of RC4, it's essential to move to a more secure cipher. AES (Advanced Encryption Standard) is the recommended replacement because it has undergone extensive scrutiny and is considered secure against cryptanalysis.\\n\\n✅ Switch to AES-256 for Robust Encryption\\n\\n\`\`\`go\\n// 32 byte keys will set up AES-256, which is a secure block cipher that has become the industry standard for encryption.\\nkey := make([]byte, 32)\\nif _, err := io.ReadFull(rand.Reader, key); err != nil {\\n log.Fatal(err)\\n}\\n\\nblockCipher, err := aes.NewCipher(key)\\nif err != nil {\\n log.Fatal(err)\\n}\\n\\naead, err := cipher.NewGCM(blockCipher)\\nif err != nil {\\n log.Fatal(err)\\n}\\n\\nvar encrypted = []byte{}\\nvar nonce = []byte{}\\n// Encryption routine\\n{\\n msg := []byte(\\"Some secret message\\")\\n // Note that the key must be rotated after every 2^32 uses of a single nonce-value to avoid cipher text repetition.\\n nonce = make([]byte, 12)\\n if _, err := io.ReadFull(rand.Reader, nonce); err != nil {\\n log.Fatal(err)\\n }\\n encrypted = aead.Seal(nil, nonce, msg, nil)\\n}\\n\\n// Decryption routine\\n{\\n msg, err := aead.Open(nil, nonce, encrypted, nil)\\n if err != nil {\\n log.Fatal(err)\\n }\\n fmt.Printf(\\"Decrypted: %s\\\\n\\", msg)\\n}\\n\`\`\`\\n\\nUsing AES-256 ensures that your encryption mechanism meets current security standards and is robust against known attacks. AES has been widely adopted across various industries and has proven its reliability over time.\\n\\n## Resources\\n\\n- [NIST Guidelines on Cryptography](https://csrc.nist.gov/publications/detail/sp/800-38a/final)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_blocklist_rc4", + "line_number": 3, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 3, + "end": 6, + "column": { + "start": 1, + "end": 2 + } + }, + "sink": { + "start": 3, + "end": 6, + "column": { + "start": 1, + "end": 2 + }, + "content": "import (\\n\\t\\"crypto/rc4\\"\\n\\t\\"fmt\\"\\n)" + }, + "parent_line_number": 3, + "snippet": "import (\\n\\t\\"crypto/rc4\\"\\n\\t\\"fmt\\"\\n)", + "fingerprint": "0fec54dd8724cf655550856999eb17fb_0", + "old_fingerprint": "67002f96f906da879da606e636c13b8c_0", + "code_extract": "import (\\n\\t\\"crypto/rc4\\"\\n\\t\\"fmt\\"\\n)" + } + ] +}" +`; diff --git a/tests/go/gosec/blocklist/rc4/test.js b/tests/go/gosec/blocklist/rc4/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/blocklist/rc4/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/blocklist/rc4/testdata/main.go b/tests/go/gosec/blocklist/rc4/testdata/main.go new file mode 100644 index 000000000..d223fa505 --- /dev/null +++ b/tests/go/gosec/blocklist/rc4/testdata/main.go @@ -0,0 +1,18 @@ +package main + +import ( + "crypto/rc4" + "fmt" +) + +func mainrc4() { + k := []byte("super secret key") + c, err := rc4.NewCipher(k) + if err != nil { + return + } + out := make([]byte, 0) + in := []byte("some cleartext") + c.XORKeyStream(out, in) + fmt.Printf("Doing something with our data: %v", out) +} diff --git a/tests/go/gosec/blocklist/sha1/__snapshots__/test.js.snap b/tests/go/gosec/blocklist/sha1/__snapshots__/test.js.snap new file mode 100644 index 000000000..d368e578f --- /dev/null +++ b/tests/go/gosec/blocklist/sha1/__snapshots__/test.js.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_blocklist_sha1 test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_blocklist_sha1", + "title": "Use of a Broken or Risky Cryptographic Algorithm", + "description": "## Description\\n\\nThe SHA-1 hashing algorithm is no longer considered secure against well-funded attackers. It is vulnerable to collision attacks, which means it's possible to generate two different inputs that result in the same SHA-1 hash, undermining the hash's uniqueness and security. Due to these vulnerabilities, it is advised to discontinue using SHA-1 for cryptographic security.\\n\\n## Remediation\\n\\nWhen choosing a hashing algorithm for cryptographic purposes, it's important to select one that is resistant to collisions and other attack vectors. SHA-3 and BLAKE2 are both excellent choices for non-password-based hashing requirements due to their strong cryptographic properties.\\n\\n✅ For General Hashing Needs, Use SHA-3 or BLAKE2\\n\\nChoose SHA-3 or BLAKE2 for their resistance to known hash attack vectors, ensuring the integrity and uniqueness of your data fingerprints.\\n\\n✅ For Password Hashing, Prefer bcrypt or Argon2id\\n\\nFor password hashing specifically, bcrypt or Argon2id are recommended. These algorithms are designed to be computationally intensive, which helps protect against brute-force attacks.\\n\\n❌ Discontinue Using SHA-1 for Security Purposes\\n\\nGiven its vulnerabilities, avoid using SHA-1 in any security context to prevent potential collision attacks.\\n\\nThe code snippet provided is unrelated to the hashing algorithms and seems to be a continuation of the previous examples for encryption with AES-256. Ensure your hashing and encryption strategies are correctly implemented as per their intended use-cases.\\n\\n## Resources\\n\\n- [OWASP Cryptographic Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html)\\n- [NIST Policy on Hash Functions](https://csrc.nist.gov/projects/hash-functions)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_blocklist_sha1", + "line_number": 3, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 3, + "end": 6, + "column": { + "start": 1, + "end": 2 + } + }, + "sink": { + "start": 3, + "end": 6, + "column": { + "start": 1, + "end": 2 + }, + "content": "import (\\n\\t\\"crypto/sha1\\"\\n\\t\\"fmt\\"\\n)" + }, + "parent_line_number": 3, + "snippet": "import (\\n\\t\\"crypto/sha1\\"\\n\\t\\"fmt\\"\\n)", + "fingerprint": "c2e8f4129be45dc51a2f05ecb2a2c021_0", + "old_fingerprint": "be80d15eca84c5e2caf79b59e5541bc9_0", + "code_extract": "import (\\n\\t\\"crypto/sha1\\"\\n\\t\\"fmt\\"\\n)" + } + ] +}" +`; diff --git a/tests/go/gosec/blocklist/sha1/test.js b/tests/go/gosec/blocklist/sha1/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/blocklist/sha1/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/blocklist/sha1/testdata/main.go b/tests/go/gosec/blocklist/sha1/testdata/main.go new file mode 100644 index 000000000..3a6af7bee --- /dev/null +++ b/tests/go/gosec/blocklist/sha1/testdata/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "crypto/sha1" + "fmt" +) + +func mainSHA1() { + h := sha1.New() + h.Write([]byte("stuff")) +} + +func mainSHA1Sum() { + out := sha1.Sum([]byte("stuff")) + fmt.Println(out) +} diff --git a/tests/go/gosec/crypto/bad_tls_settings/__snapshots__/test.js.snap b/tests/go/gosec/crypto/bad_tls_settings/__snapshots__/test.js.snap new file mode 100644 index 000000000..532724c22 --- /dev/null +++ b/tests/go/gosec/crypto/bad_tls_settings/__snapshots__/test.js.snap @@ -0,0 +1,76 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_crypto_bad_tls_settings test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_crypto_bad_tls_settings", + "title": "Use of a broken or risky cryptographic algorithm", + "description": "## Description\\n\\nA security concern arises when a cryptographically insecure cipher suite is used in an application. Such cipher suites may be vulnerable to various types of attacks, reducing the security of the communication channel.\\n\\n## Remediation\\n\\nTo enhance the security of TLS connections, it is crucial to use up-to-date and secure cipher suites and protocols. Here are the recommended steps to ensure the use of secure ciphers:\\n\\n✅ Use Modern, Secure Cipher Suites\\n\\nSelect cipher suites that are known to be secure and have properties such as Perfect Forward Secrecy (PFS), which protects past communications even if future private keys are compromised.\\n\\n✅ Adopt TLS 1.3 Where Possible\\n\\nTLS 1.3 should be the preferred protocol as it includes improvements over previous versions, making it more secure against various attacks. Go's standard library will automatically prefer the most secure protocol and cipher suite available during the TLS handshake.\\n\\n✅ Configure TLS Properly If Using TLS 1.0-1.2\\n\\nIn cases where TLS 1.3 is not an option and you must use TLS 1.0-1.2, ensure to configure the cipher suites to use those that support PFS, as listed below.\\n\\n❌ Avoid Using Obsolete or Insecure Cipher Suites\\n\\nAvoid any cipher suites that do not support modern security standards, including those without PFS or with known vulnerabilities.\\n\\nThe provided Go code examples demonstrate how to configure the \`tls.Config\` struct for a Go server to use TLS 1.3 or to specify a list of secure cipher suites when using TLS 1.0-1.2.\\n\\n## Resources\\n\\n- [Mozilla's SSL Configuration Generator](https://ssl-config.mozilla.org/)\\n- [OWASP TLS Cipher String Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/TLS_Cipher_String_Cheat_Sheet.html)\\n- [RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3](https://tools.ietf.org/html/rfc8446)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_bad_tls_settings", + "line_number": 11, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 11, + "end": 11, + "column": { + "start": 21, + "end": 137 + } + }, + "sink": { + "start": 11, + "end": 11, + "column": { + "start": 21, + "end": 137 + }, + "content": "tls.Config{CipherSuites: []uint16{tls.TLS_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}}" + }, + "parent_line_number": 11, + "snippet": "tls.Config{CipherSuites: []uint16{tls.TLS_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}}", + "fingerprint": "36c61f9e4fdfaf69e608c2e75308672a_0", + "old_fingerprint": "4a972d23a74fe461e2424501cdcbd9d0_0", + "code_extract": "\\t\\tTLSClientConfig: &tls.Config{CipherSuites: []uint16{tls.TLS_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}}," + }, + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_crypto_bad_tls_settings", + "title": "Use of a broken or risky cryptographic algorithm", + "description": "## Description\\n\\nA security concern arises when a cryptographically insecure cipher suite is used in an application. Such cipher suites may be vulnerable to various types of attacks, reducing the security of the communication channel.\\n\\n## Remediation\\n\\nTo enhance the security of TLS connections, it is crucial to use up-to-date and secure cipher suites and protocols. Here are the recommended steps to ensure the use of secure ciphers:\\n\\n✅ Use Modern, Secure Cipher Suites\\n\\nSelect cipher suites that are known to be secure and have properties such as Perfect Forward Secrecy (PFS), which protects past communications even if future private keys are compromised.\\n\\n✅ Adopt TLS 1.3 Where Possible\\n\\nTLS 1.3 should be the preferred protocol as it includes improvements over previous versions, making it more secure against various attacks. Go's standard library will automatically prefer the most secure protocol and cipher suite available during the TLS handshake.\\n\\n✅ Configure TLS Properly If Using TLS 1.0-1.2\\n\\nIn cases where TLS 1.3 is not an option and you must use TLS 1.0-1.2, ensure to configure the cipher suites to use those that support PFS, as listed below.\\n\\n❌ Avoid Using Obsolete or Insecure Cipher Suites\\n\\nAvoid any cipher suites that do not support modern security standards, including those without PFS or with known vulnerabilities.\\n\\nThe provided Go code examples demonstrate how to configure the \`tls.Config\` struct for a Go server to use TLS 1.3 or to specify a list of secure cipher suites when using TLS 1.0-1.2.\\n\\n## Resources\\n\\n- [Mozilla's SSL Configuration Generator](https://ssl-config.mozilla.org/)\\n- [OWASP TLS Cipher String Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/TLS_Cipher_String_Cheat_Sheet.html)\\n- [RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3](https://tools.ietf.org/html/rfc8446)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_bad_tls_settings", + "line_number": 14, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 14, + "end": 16, + "column": { + "start": 12, + "end": 3 + } + }, + "sink": { + "start": 14, + "end": 16, + "column": { + "start": 12, + "end": 3 + }, + "content": "tls.CipherSuite{\\n\\t\\tID: tls.TLS_AES_128_GCM_SHA256,\\n\\t}" + }, + "parent_line_number": 14, + "snippet": "tls.CipherSuite{\\n\\t\\tID: tls.TLS_AES_128_GCM_SHA256,\\n\\t}", + "fingerprint": "36c61f9e4fdfaf69e608c2e75308672a_1", + "old_fingerprint": "4a972d23a74fe461e2424501cdcbd9d0_1", + "code_extract": "\\tcipher := tls.CipherSuite{\\n\\t\\tID: tls.TLS_AES_128_GCM_SHA256,\\n\\t}" + } + ] +}" +`; diff --git a/tests/go/gosec/crypto/bad_tls_settings/test.js b/tests/go/gosec/crypto/bad_tls_settings/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/crypto/bad_tls_settings/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/crypto/bad_tls_settings/testdata/main.go b/tests/go/gosec/crypto/bad_tls_settings/testdata/main.go new file mode 100644 index 000000000..216d5b36f --- /dev/null +++ b/tests/go/gosec/crypto/bad_tls_settings/testdata/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "crypto/tls" + "fmt" + "net/http" +) + +func mainbadciphersuites() { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{CipherSuites: []uint16{tls.TLS_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}}, + } + + cipher := tls.CipherSuite{ + ID: tls.TLS_AES_128_GCM_SHA256, + } + + client := &http.Client{Transport: tr} + _, err := client.Get("https://golang.org/") + if err != nil { + fmt.Println(err) + } +} diff --git a/tests/go/gosec/crypto/insecure_ignore_host_key/__snapshots__/test.js.snap b/tests/go/gosec/crypto/insecure_ignore_host_key/__snapshots__/test.js.snap new file mode 100644 index 000000000..01e737c6d --- /dev/null +++ b/tests/go/gosec/crypto/insecure_ignore_host_key/__snapshots__/test.js.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_crypto_insecure_ignore_host_key test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "322" + ], + "id": "go_gosec_crypto_insecure_ignore_host_key", + "title": "Key exchange without entity authentication", + "description": "## Description\\n\\nThe security vulnerability identified pertains to the application neglecting the verification of host keys during SSH connections. Host keys are crucial for confirming the server's identity, preventing Man-in-the-Middle (MitM) attacks where an attacker could impersonate the server. When these keys are ignored, the client cannot guarantee the authenticity of the server it connects to.\\n\\n## Remediation\\n\\nTo mitigate this risk, it is essential to implement proper host key checking:\\n\\n✅ Implement Host Key Verification\\n\\nUse the \`knownhosts\` package from Go's \`x/crypto/ssh\` to validate server keys against known hosts. This mirrors the functionality found in OpenSSH.\\n\\n✅ Avoid Disabling Host Key Checking\\n\\nNever disable host key checking in production code. While it might be convenient for testing, it opens up security vulnerabilities.\\n\\n❌ Do Not Use \`InsecureIgnoreHostKey\`\\n\\nAlthough available, using \`ssh.InsecureIgnoreHostKey\` as a \`HostKeyCallback\` function should be strictly avoided as it does not offer any form of host validation.\\n\\nBelow is a code snippet showing how to set up an SSH \`ClientConfig\` in Go to use the \`knownhosts\` callback for server verification:\\n\\n## Resources\\n\\n- [GoDoc for x/crypto/ssh](https://pkg.go.dev/golang.org/x/crypto/ssh)\\n- [Secure use of SSH - OpenSSH](https://www.openssh.com/)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_insecure_ignore_host_key", + "line_number": 8, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 8, + "end": 8, + "column": { + "start": 6, + "end": 33 + } + }, + "sink": { + "start": 8, + "end": 8, + "column": { + "start": 6, + "end": 33 + }, + "content": "ssh.InsecureIgnoreHostKey()" + }, + "parent_line_number": 8, + "snippet": "ssh.InsecureIgnoreHostKey()", + "fingerprint": "dbf696ab5d93618a95e2b8124b7c4c18_0", + "old_fingerprint": "736c606334c8f895d0bfe41c1abff8ea_0", + "code_extract": "\\t_ = ssh.InsecureIgnoreHostKey()" + } + ] +}" +`; diff --git a/tests/go/gosec/crypto/insecure_ignore_host_key/test.js b/tests/go/gosec/crypto/insecure_ignore_host_key/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/crypto/insecure_ignore_host_key/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/crypto/insecure_ignore_host_key/testdata/main.go b/tests/go/gosec/crypto/insecure_ignore_host_key/testdata/main.go new file mode 100644 index 000000000..c60bd8abf --- /dev/null +++ b/tests/go/gosec/crypto/insecure_ignore_host_key/testdata/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "golang.org/x/crypto/ssh" +) + +func main() { + _ = ssh.InsecureIgnoreHostKey() +} diff --git a/tests/go/gosec/crypto/weak_crypto/__snapshots__/test.js.snap b/tests/go/gosec/crypto/weak_crypto/__snapshots__/test.js.snap new file mode 100644 index 000000000..e7ad8cda3 --- /dev/null +++ b/tests/go/gosec/crypto/weak_crypto/__snapshots__/test.js.snap @@ -0,0 +1,246 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_crypto_weak_crypto test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_crypto_weak_crypto", + "title": "Use of a Broken or Risky Cryptographic Algorithm", + "description": "## Description\\n\\nThe issue identified indicates the use of a cryptographic algorithm that is no longer considered secure by current standards. Such algorithms can compromise data confidentiality and integrity, making it vulnerable to decryption and tampering by unauthorized parties.\\n\\n## Remediation\\n\\nTo ensure the security of the data, adhere to the following guidelines:\\n\\n✅ Employ Strong Cryptographic Algorithms\\n\\nReplace deprecated or weak algorithms with strong, modern alternatives such as AES (Advanced Encryption Standard) for encryption, and SHA-256 or higher for hashing.\\n\\n✅ Keep Libraries Updated\\n\\nUse the latest versions of cryptographic libraries, as they are more likely to default to secure algorithms and settings.\\n\\n❌ Avoid Deprecated Algorithms\\n\\nDo not use cryptographic algorithms that have been deprecated due to vulnerabilities, such as MD5, SHA-1, or DES.\\n\\n❌ Do Not Reinvent Cryptography\\n\\nAvoid custom cryptographic implementations as they are more susceptible to errors. Instead, rely on well-reviewed and tested standard cryptographic libraries.\\n\\n## Resources\\n\\n- [NIST Cryptographic Standards and Guidelines](https://csrc.nist.gov/publications/sp)\\n- [Cryptography Coding Standard](https://cryptocoding.net/index.php/Coding_rules)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_crypto", + "line_number": 17, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 17, + "end": 17, + "column": { + "start": 12, + "end": 28 + } + }, + "sink": { + "start": 17, + "end": 17, + "column": { + "start": 12, + "end": 28 + }, + "content": "rc4.NewCipher(k)" + }, + "parent_line_number": 17, + "snippet": "rc4.NewCipher(k)", + "fingerprint": "45b48243a6f8f3eba502319efc975ec0_0", + "old_fingerprint": "071fb5b2a3758433e529d3c7636d7af7_0", + "code_extract": "\\tc, err := rc4.NewCipher(k)" + }, + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_crypto_weak_crypto", + "title": "Use of a Broken or Risky Cryptographic Algorithm", + "description": "## Description\\n\\nThe issue identified indicates the use of a cryptographic algorithm that is no longer considered secure by current standards. Such algorithms can compromise data confidentiality and integrity, making it vulnerable to decryption and tampering by unauthorized parties.\\n\\n## Remediation\\n\\nTo ensure the security of the data, adhere to the following guidelines:\\n\\n✅ Employ Strong Cryptographic Algorithms\\n\\nReplace deprecated or weak algorithms with strong, modern alternatives such as AES (Advanced Encryption Standard) for encryption, and SHA-256 or higher for hashing.\\n\\n✅ Keep Libraries Updated\\n\\nUse the latest versions of cryptographic libraries, as they are more likely to default to secure algorithms and settings.\\n\\n❌ Avoid Deprecated Algorithms\\n\\nDo not use cryptographic algorithms that have been deprecated due to vulnerabilities, such as MD5, SHA-1, or DES.\\n\\n❌ Do Not Reinvent Cryptography\\n\\nAvoid custom cryptographic implementations as they are more susceptible to errors. Instead, rely on well-reviewed and tested standard cryptographic libraries.\\n\\n## Resources\\n\\n- [NIST Cryptographic Standards and Guidelines](https://csrc.nist.gov/publications/sp)\\n- [Cryptography Coding Standard](https://cryptocoding.net/index.php/Coding_rules)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_crypto", + "line_number": 28, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 28, + "end": 28, + "column": { + "start": 7, + "end": 16 + } + }, + "sink": { + "start": 28, + "end": 28, + "column": { + "start": 7, + "end": 16 + }, + "content": "md5.New()" + }, + "parent_line_number": 28, + "snippet": "md5.New()", + "fingerprint": "45b48243a6f8f3eba502319efc975ec0_1", + "old_fingerprint": "071fb5b2a3758433e529d3c7636d7af7_1", + "code_extract": "\\th := md5.New()" + }, + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_crypto_weak_crypto", + "title": "Use of a Broken or Risky Cryptographic Algorithm", + "description": "## Description\\n\\nThe issue identified indicates the use of a cryptographic algorithm that is no longer considered secure by current standards. Such algorithms can compromise data confidentiality and integrity, making it vulnerable to decryption and tampering by unauthorized parties.\\n\\n## Remediation\\n\\nTo ensure the security of the data, adhere to the following guidelines:\\n\\n✅ Employ Strong Cryptographic Algorithms\\n\\nReplace deprecated or weak algorithms with strong, modern alternatives such as AES (Advanced Encryption Standard) for encryption, and SHA-256 or higher for hashing.\\n\\n✅ Keep Libraries Updated\\n\\nUse the latest versions of cryptographic libraries, as they are more likely to default to secure algorithms and settings.\\n\\n❌ Avoid Deprecated Algorithms\\n\\nDo not use cryptographic algorithms that have been deprecated due to vulnerabilities, such as MD5, SHA-1, or DES.\\n\\n❌ Do Not Reinvent Cryptography\\n\\nAvoid custom cryptographic implementations as they are more susceptible to errors. Instead, rely on well-reviewed and tested standard cryptographic libraries.\\n\\n## Resources\\n\\n- [NIST Cryptographic Standards and Guidelines](https://csrc.nist.gov/publications/sp)\\n- [Cryptography Coding Standard](https://cryptocoding.net/index.php/Coding_rules)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_crypto", + "line_number": 33, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 33, + "end": 33, + "column": { + "start": 9, + "end": 33 + } + }, + "sink": { + "start": 33, + "end": 33, + "column": { + "start": 9, + "end": 33 + }, + "content": "md5.Sum([]byte(\\"stuff\\"))" + }, + "parent_line_number": 33, + "snippet": "md5.Sum([]byte(\\"stuff\\"))", + "fingerprint": "45b48243a6f8f3eba502319efc975ec0_2", + "old_fingerprint": "071fb5b2a3758433e529d3c7636d7af7_2", + "code_extract": "\\tout := md5.Sum([]byte(\\"stuff\\"))" + }, + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_crypto_weak_crypto", + "title": "Use of a Broken or Risky Cryptographic Algorithm", + "description": "## Description\\n\\nThe issue identified indicates the use of a cryptographic algorithm that is no longer considered secure by current standards. Such algorithms can compromise data confidentiality and integrity, making it vulnerable to decryption and tampering by unauthorized parties.\\n\\n## Remediation\\n\\nTo ensure the security of the data, adhere to the following guidelines:\\n\\n✅ Employ Strong Cryptographic Algorithms\\n\\nReplace deprecated or weak algorithms with strong, modern alternatives such as AES (Advanced Encryption Standard) for encryption, and SHA-256 or higher for hashing.\\n\\n✅ Keep Libraries Updated\\n\\nUse the latest versions of cryptographic libraries, as they are more likely to default to secure algorithms and settings.\\n\\n❌ Avoid Deprecated Algorithms\\n\\nDo not use cryptographic algorithms that have been deprecated due to vulnerabilities, such as MD5, SHA-1, or DES.\\n\\n❌ Do Not Reinvent Cryptography\\n\\nAvoid custom cryptographic implementations as they are more susceptible to errors. Instead, rely on well-reviewed and tested standard cryptographic libraries.\\n\\n## Resources\\n\\n- [NIST Cryptographic Standards and Guidelines](https://csrc.nist.gov/publications/sp)\\n- [Cryptography Coding Standard](https://cryptocoding.net/index.php/Coding_rules)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_crypto", + "line_number": 38, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 38, + "end": 38, + "column": { + "start": 7, + "end": 17 + } + }, + "sink": { + "start": 38, + "end": 38, + "column": { + "start": 7, + "end": 17 + }, + "content": "sha1.New()" + }, + "parent_line_number": 38, + "snippet": "sha1.New()", + "fingerprint": "45b48243a6f8f3eba502319efc975ec0_3", + "old_fingerprint": "071fb5b2a3758433e529d3c7636d7af7_3", + "code_extract": "\\th := sha1.New()" + }, + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_crypto_weak_crypto", + "title": "Use of a Broken or Risky Cryptographic Algorithm", + "description": "## Description\\n\\nThe issue identified indicates the use of a cryptographic algorithm that is no longer considered secure by current standards. Such algorithms can compromise data confidentiality and integrity, making it vulnerable to decryption and tampering by unauthorized parties.\\n\\n## Remediation\\n\\nTo ensure the security of the data, adhere to the following guidelines:\\n\\n✅ Employ Strong Cryptographic Algorithms\\n\\nReplace deprecated or weak algorithms with strong, modern alternatives such as AES (Advanced Encryption Standard) for encryption, and SHA-256 or higher for hashing.\\n\\n✅ Keep Libraries Updated\\n\\nUse the latest versions of cryptographic libraries, as they are more likely to default to secure algorithms and settings.\\n\\n❌ Avoid Deprecated Algorithms\\n\\nDo not use cryptographic algorithms that have been deprecated due to vulnerabilities, such as MD5, SHA-1, or DES.\\n\\n❌ Do Not Reinvent Cryptography\\n\\nAvoid custom cryptographic implementations as they are more susceptible to errors. Instead, rely on well-reviewed and tested standard cryptographic libraries.\\n\\n## Resources\\n\\n- [NIST Cryptographic Standards and Guidelines](https://csrc.nist.gov/publications/sp)\\n- [Cryptography Coding Standard](https://cryptocoding.net/index.php/Coding_rules)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_crypto", + "line_number": 43, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 43, + "end": 43, + "column": { + "start": 9, + "end": 34 + } + }, + "sink": { + "start": 43, + "end": 43, + "column": { + "start": 9, + "end": 34 + }, + "content": "sha1.Sum([]byte(\\"stuff\\"))" + }, + "parent_line_number": 43, + "snippet": "sha1.Sum([]byte(\\"stuff\\"))", + "fingerprint": "45b48243a6f8f3eba502319efc975ec0_4", + "old_fingerprint": "071fb5b2a3758433e529d3c7636d7af7_4", + "code_extract": "\\tout := sha1.Sum([]byte(\\"stuff\\"))" + }, + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_crypto_weak_crypto", + "title": "Use of a Broken or Risky Cryptographic Algorithm", + "description": "## Description\\n\\nThe issue identified indicates the use of a cryptographic algorithm that is no longer considered secure by current standards. Such algorithms can compromise data confidentiality and integrity, making it vulnerable to decryption and tampering by unauthorized parties.\\n\\n## Remediation\\n\\nTo ensure the security of the data, adhere to the following guidelines:\\n\\n✅ Employ Strong Cryptographic Algorithms\\n\\nReplace deprecated or weak algorithms with strong, modern alternatives such as AES (Advanced Encryption Standard) for encryption, and SHA-256 or higher for hashing.\\n\\n✅ Keep Libraries Updated\\n\\nUse the latest versions of cryptographic libraries, as they are more likely to default to secure algorithms and settings.\\n\\n❌ Avoid Deprecated Algorithms\\n\\nDo not use cryptographic algorithms that have been deprecated due to vulnerabilities, such as MD5, SHA-1, or DES.\\n\\n❌ Do Not Reinvent Cryptography\\n\\nAvoid custom cryptographic implementations as they are more susceptible to errors. Instead, rely on well-reviewed and tested standard cryptographic libraries.\\n\\n## Resources\\n\\n- [NIST Cryptographic Standards and Guidelines](https://csrc.nist.gov/publications/sp)\\n- [Cryptography Coding Standard](https://cryptocoding.net/index.php/Coding_rules)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_crypto", + "line_number": 49, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 49, + "end": 49, + "column": { + "start": 16, + "end": 32 + } + }, + "sink": { + "start": 49, + "end": 49, + "column": { + "start": 16, + "end": 32 + }, + "content": "des.NewCipher(k)" + }, + "parent_line_number": 49, + "snippet": "des.NewCipher(k)", + "fingerprint": "45b48243a6f8f3eba502319efc975ec0_5", + "old_fingerprint": "071fb5b2a3758433e529d3c7636d7af7_5", + "code_extract": "\\tblock, err := des.NewCipher(k)" + }, + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_crypto_weak_crypto", + "title": "Use of a Broken or Risky Cryptographic Algorithm", + "description": "## Description\\n\\nThe issue identified indicates the use of a cryptographic algorithm that is no longer considered secure by current standards. Such algorithms can compromise data confidentiality and integrity, making it vulnerable to decryption and tampering by unauthorized parties.\\n\\n## Remediation\\n\\nTo ensure the security of the data, adhere to the following guidelines:\\n\\n✅ Employ Strong Cryptographic Algorithms\\n\\nReplace deprecated or weak algorithms with strong, modern alternatives such as AES (Advanced Encryption Standard) for encryption, and SHA-256 or higher for hashing.\\n\\n✅ Keep Libraries Updated\\n\\nUse the latest versions of cryptographic libraries, as they are more likely to default to secure algorithms and settings.\\n\\n❌ Avoid Deprecated Algorithms\\n\\nDo not use cryptographic algorithms that have been deprecated due to vulnerabilities, such as MD5, SHA-1, or DES.\\n\\n❌ Do Not Reinvent Cryptography\\n\\nAvoid custom cryptographic implementations as they are more susceptible to errors. Instead, rely on well-reviewed and tested standard cryptographic libraries.\\n\\n## Resources\\n\\n- [NIST Cryptographic Standards and Guidelines](https://csrc.nist.gov/publications/sp)\\n- [Cryptography Coding Standard](https://cryptocoding.net/index.php/Coding_rules)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_crypto", + "line_number": 63, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 63, + "end": 63, + "column": { + "start": 16, + "end": 41 + } + }, + "sink": { + "start": 63, + "end": 63, + "column": { + "start": 16, + "end": 41 + }, + "content": "des.NewTripleDESCipher(k)" + }, + "parent_line_number": 63, + "snippet": "des.NewTripleDESCipher(k)", + "fingerprint": "45b48243a6f8f3eba502319efc975ec0_6", + "old_fingerprint": "071fb5b2a3758433e529d3c7636d7af7_6", + "code_extract": "\\tblock, err := des.NewTripleDESCipher(k)" + } + ] +}" +`; diff --git a/tests/go/gosec/crypto/weak_crypto/test.js b/tests/go/gosec/crypto/weak_crypto/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/crypto/weak_crypto/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/crypto/weak_crypto/testdata/main.go b/tests/go/gosec/crypto/weak_crypto/testdata/main.go new file mode 100644 index 000000000..c52d7042f --- /dev/null +++ b/tests/go/gosec/crypto/weak_crypto/testdata/main.go @@ -0,0 +1,69 @@ +package main + +import ( + "crypto/des" + "crypto/md5" + "crypto/rc4" + "crypto/sha1" + "fmt" +) + +func mainrc4() { + k := []byte("super secret key") + c, err := rc4.NewCipher(k) + if err != nil { + return + } + out := make([]byte, 0) + in := []byte("some cleartext") + c.XORKeyStream(out, in) + fmt.Printf("Doing something with our data: %v", out) +} + +func mainMD55() { + h := md5.New() + h.Write([]byte("stuff")) +} + +func mainMD5Sum() { + out := md5.Sum([]byte("stuff")) + fmt.Println(out) +} + +func mainSHA1() { + h := sha1.New() + h.Write([]byte("stuff")) +} + +func mainSHA1Sum() { + out := sha1.Sum([]byte("stuff")) + fmt.Println(out) +} + +func maindes() { + k := []byte("super secret key") + block, err := des.NewCipher(k) + if err != nil { + return + } + out := make([]byte, 0) + in := []byte("cleartext") + block.Encrypt(out, in) + decrypted := make([]byte, 0) + block.Decrypt(decrypted, out) + fmt.Printf("Doing something with: %s\n", string(decrypted)) +} + +func main3des() { + k := []byte("super secret key") + block, err := des.NewTripleDESCipher(k) + if err != nil { + return + } + out := make([]byte, 0) + in := []byte("cleartext") + block.Encrypt(out, in) + decrypted := make([]byte, 0) + block.Decrypt(decrypted, out) + fmt.Printf("Doing something with: %s\n", string(decrypted)) +} diff --git a/tests/go/gosec/crypto/weak_key_strength/__snapshots__/test.js.snap b/tests/go/gosec/crypto/weak_key_strength/__snapshots__/test.js.snap new file mode 100644 index 000000000..4a7285860 --- /dev/null +++ b/tests/go/gosec/crypto/weak_key_strength/__snapshots__/test.js.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_crypto_weak_key_strength test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "326" + ], + "id": "go_gosec_crypto_weak_key_strength", + "title": "Inadequate encryption strength", + "description": "## Description\\n\\nThe application generates an RSA key with a bit length that is shorter than the current recommended minimum of 2048 bits. Keys shorter than 2048 bits are considered insecure due to advancements in computational power which could potentially allow them to be factored, thereby breaking the encryption.\\n\\n## Remediation\\n\\nTo ensure the security of RSA keys, follow these guidelines:\\n\\n✅ Use Sufficient Key Length\\n\\nGenerate RSA keys with a minimum length of 2048 bits to align with NIST recommendations and safeguard against future advancements in computing power that could compromise keys of shorter lengths.\\n\\n\`\`\`go\\n// Example of generating a secure RSA key with 2048 bits\\nimport (\\n \\"crypto/rand\\"\\n \\"crypto/rsa\\"\\n \\"log\\"\\n)\\n\\nfunc generateSecureKey() {\\n // Use at least 2048 bits for secure RSA keys\\n privateKey, err := rsa.GenerateKey(rand.Reader, 2048)\\n if err != nil {\\n log.Fatalf(\\"Error generating RSA key: %v\\", err)\\n }\\n // privateKey can now be used for secure cryptographic operations\\n}\\n\`\`\`\\n\\n❌ Avoid Short Keys\\n\\nDo not use RSA keys that are less than 2048 bits in length, as they do not offer sufficient protection against brute-force attacks.\\n\\n❌ Don't Ignore Industry Standards\\n\\nAlways follow industry standards and guidelines for cryptographic practices to maintain the integrity and confidentiality of data.\\n\\n## Resources\\n\\n- [NIST Special Publication 800-57 Part 1](https://csrc.nist.gov/publications/detail/sp/800-57-part-1/rev-5/final)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_key_strength", + "line_number": 11, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 11, + "end": 11, + "column": { + "start": 14, + "end": 48 + } + }, + "sink": { + "start": 11, + "end": 11, + "column": { + "start": 14, + "end": 48 + }, + "content": "rsa.GenerateKey(rand.Reader, 1024)" + }, + "parent_line_number": 11, + "snippet": "rsa.GenerateKey(rand.Reader, 1024)", + "fingerprint": "695a0322af8c5cb0665169f1eaca776f_0", + "old_fingerprint": "63ff529d2f1ee14097f9417bfa5ffde8_0", + "code_extract": "\\tpvk, err := rsa.GenerateKey(rand.Reader, 1024)" + } + ] +}" +`; diff --git a/tests/go/gosec/crypto/weak_key_strength/test.js b/tests/go/gosec/crypto/weak_key_strength/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/crypto/weak_key_strength/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/crypto/weak_key_strength/testdata/main.go b/tests/go/gosec/crypto/weak_key_strength/testdata/main.go new file mode 100644 index 000000000..c44fdf9a7 --- /dev/null +++ b/tests/go/gosec/crypto/weak_key_strength/testdata/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "crypto/rand" + "crypto/rsa" + "fmt" +) + +func mainweakkey() { + //Generate Private Key + pvk, err := rsa.GenerateKey(rand.Reader, 1024) + if err != nil { + fmt.Println(err) + } + fmt.Println(pvk) +} diff --git a/tests/go/gosec/crypto/weak_random/__snapshots__/test.js.snap b/tests/go/gosec/crypto/weak_random/__snapshots__/test.js.snap new file mode 100644 index 000000000..4c77ea119 --- /dev/null +++ b/tests/go/gosec/crypto/weak_random/__snapshots__/test.js.snap @@ -0,0 +1,144 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_crypto_weak_random test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "338" + ], + "id": "go_gosec_crypto_weak_random", + "title": "Use of cryptographically weak Pseudo-Random Number Generator (PRNG)", + "description": "## Description\\n\\nThe \`math/rand\` package in Go is designed for generating pseudorandom numbers, which are not secure for cryptographic purposes. These numbers are predictable if the seed is known, which could compromise the security of applications using them for secrets, tokens, or other security-sensitive features.\\n\\n## Remediations\\n\\nTo securely generate random numbers in a security-sensitive context, implement the following measures:\\n\\n✅ Use Cryptographically Secure Randomness\\n\\nReplace the use of \`math/rand\` with \`crypto/rand\` to ensure that the random numbers generated are suitable for cryptographic use and are not predictable.\\n\\n\`\`\`go\\nimport (\\n \\"crypto/rand\\"\\n \\"log\\"\\n \\"math/big\\"\\n)\\n\\nfunc generateSecureRandomNumber() *big.Int {\\n // Generate a cryptographically secure random number\\n randomNumber, err := rand.Int(rand.Reader, big.NewInt(1<<62))\\n if err != nil {\\n log.Fatalf(\\"Failed to generate a secure random number: %v\\", err)\\n }\\n return randomNumber\\n}\\n\`\`\`\\n\\n✅ Audit Existing Code\\n\\nReview your codebase for instances where \`math/rand\` is used in security-sensitive contexts and update them to use \`crypto/rand\`.\\n\\n❌ Do Not Use Predictable Seeds\\n\\nAvoid initializing \`math/rand\` with predictable seeds, such as timestamps or other easily guessable values, especially in a security context.\\n\\n❌ Don't Use for Security Purposes\\n\\nNever rely on \`math/rand\` for generating random numbers in cryptographic applications, like key generation, authentication tokens, or any form of security challenge.\\n\\n## Resources\\n\\n- [crypto/rand package documentation](https://pkg.go.dev/crypto/rand)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_random", + "line_number": 9, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 9, + "end": 9, + "column": { + "start": 9, + "end": 19 + } + }, + "sink": { + "start": 9, + "end": 9, + "column": { + "start": 9, + "end": 19 + }, + "content": "rand.Int()" + }, + "parent_line_number": 9, + "snippet": "rand.Int()", + "fingerprint": "7b6ec9847cbc39c97fa0639013146ac3_0", + "old_fingerprint": "a2f5db50454d53a8f3aa6a3845a5f64b_0", + "code_extract": "\\tbad := rand.Int() // detected" + }, + { + "cwe_ids": [ + "338" + ], + "id": "go_gosec_crypto_weak_random", + "title": "Use of cryptographically weak Pseudo-Random Number Generator (PRNG)", + "description": "## Description\\n\\nThe \`math/rand\` package in Go is designed for generating pseudorandom numbers, which are not secure for cryptographic purposes. These numbers are predictable if the seed is known, which could compromise the security of applications using them for secrets, tokens, or other security-sensitive features.\\n\\n## Remediations\\n\\nTo securely generate random numbers in a security-sensitive context, implement the following measures:\\n\\n✅ Use Cryptographically Secure Randomness\\n\\nReplace the use of \`math/rand\` with \`crypto/rand\` to ensure that the random numbers generated are suitable for cryptographic use and are not predictable.\\n\\n\`\`\`go\\nimport (\\n \\"crypto/rand\\"\\n \\"log\\"\\n \\"math/big\\"\\n)\\n\\nfunc generateSecureRandomNumber() *big.Int {\\n // Generate a cryptographically secure random number\\n randomNumber, err := rand.Int(rand.Reader, big.NewInt(1<<62))\\n if err != nil {\\n log.Fatalf(\\"Failed to generate a secure random number: %v\\", err)\\n }\\n return randomNumber\\n}\\n\`\`\`\\n\\n✅ Audit Existing Code\\n\\nReview your codebase for instances where \`math/rand\` is used in security-sensitive contexts and update them to use \`crypto/rand\`.\\n\\n❌ Do Not Use Predictable Seeds\\n\\nAvoid initializing \`math/rand\` with predictable seeds, such as timestamps or other easily guessable values, especially in a security context.\\n\\n❌ Don't Use for Security Purposes\\n\\nNever rely on \`math/rand\` for generating random numbers in cryptographic applications, like key generation, authentication tokens, or any form of security challenge.\\n\\n## Resources\\n\\n- [crypto/rand package documentation](https://pkg.go.dev/crypto/rand)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_random", + "line_number": 16, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 16, + "end": 16, + "column": { + "start": 9, + "end": 22 + } + }, + "sink": { + "start": 16, + "end": 16, + "column": { + "start": 9, + "end": 22 + }, + "content": "mrand.Int31()" + }, + "parent_line_number": 16, + "snippet": "mrand.Int31()", + "fingerprint": "7b6ec9847cbc39c97fa0639013146ac3_1", + "old_fingerprint": "a2f5db50454d53a8f3aa6a3845a5f64b_1", + "code_extract": "\\tbad := mrand.Int31() // detected" + }, + { + "cwe_ids": [ + "338" + ], + "id": "go_gosec_crypto_weak_random", + "title": "Use of cryptographically weak Pseudo-Random Number Generator (PRNG)", + "description": "## Description\\n\\nThe \`math/rand\` package in Go is designed for generating pseudorandom numbers, which are not secure for cryptographic purposes. These numbers are predictable if the seed is known, which could compromise the security of applications using them for secrets, tokens, or other security-sensitive features.\\n\\n## Remediations\\n\\nTo securely generate random numbers in a security-sensitive context, implement the following measures:\\n\\n✅ Use Cryptographically Secure Randomness\\n\\nReplace the use of \`math/rand\` with \`crypto/rand\` to ensure that the random numbers generated are suitable for cryptographic use and are not predictable.\\n\\n\`\`\`go\\nimport (\\n \\"crypto/rand\\"\\n \\"log\\"\\n \\"math/big\\"\\n)\\n\\nfunc generateSecureRandomNumber() *big.Int {\\n // Generate a cryptographically secure random number\\n randomNumber, err := rand.Int(rand.Reader, big.NewInt(1<<62))\\n if err != nil {\\n log.Fatalf(\\"Failed to generate a secure random number: %v\\", err)\\n }\\n return randomNumber\\n}\\n\`\`\`\\n\\n✅ Audit Existing Code\\n\\nReview your codebase for instances where \`math/rand\` is used in security-sensitive contexts and update them to use \`crypto/rand\`.\\n\\n❌ Do Not Use Predictable Seeds\\n\\nAvoid initializing \`math/rand\` with predictable seeds, such as timestamps or other easily guessable values, especially in a security context.\\n\\n❌ Don't Use for Security Purposes\\n\\nNever rely on \`math/rand\` for generating random numbers in cryptographic applications, like key generation, authentication tokens, or any form of security challenge.\\n\\n## Resources\\n\\n- [crypto/rand package documentation](https://pkg.go.dev/crypto/rand)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_random", + "line_number": 22, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 22, + "end": 22, + "column": { + "start": 9, + "end": 18 + } + }, + "sink": { + "start": 22, + "end": 22, + "column": { + "start": 9, + "end": 18 + }, + "content": "gen.Int()" + }, + "parent_line_number": 22, + "snippet": "gen.Int()", + "fingerprint": "7b6ec9847cbc39c97fa0639013146ac3_2", + "old_fingerprint": "a2f5db50454d53a8f3aa6a3845a5f64b_2", + "code_extract": "\\tbad := gen.Int() // detected" + }, + { + "cwe_ids": [ + "338" + ], + "id": "go_gosec_crypto_weak_random", + "title": "Use of cryptographically weak Pseudo-Random Number Generator (PRNG)", + "description": "## Description\\n\\nThe \`math/rand\` package in Go is designed for generating pseudorandom numbers, which are not secure for cryptographic purposes. These numbers are predictable if the seed is known, which could compromise the security of applications using them for secrets, tokens, or other security-sensitive features.\\n\\n## Remediations\\n\\nTo securely generate random numbers in a security-sensitive context, implement the following measures:\\n\\n✅ Use Cryptographically Secure Randomness\\n\\nReplace the use of \`math/rand\` with \`crypto/rand\` to ensure that the random numbers generated are suitable for cryptographic use and are not predictable.\\n\\n\`\`\`go\\nimport (\\n \\"crypto/rand\\"\\n \\"log\\"\\n \\"math/big\\"\\n)\\n\\nfunc generateSecureRandomNumber() *big.Int {\\n // Generate a cryptographically secure random number\\n randomNumber, err := rand.Int(rand.Reader, big.NewInt(1<<62))\\n if err != nil {\\n log.Fatalf(\\"Failed to generate a secure random number: %v\\", err)\\n }\\n return randomNumber\\n}\\n\`\`\`\\n\\n✅ Audit Existing Code\\n\\nReview your codebase for instances where \`math/rand\` is used in security-sensitive contexts and update them to use \`crypto/rand\`.\\n\\n❌ Do Not Use Predictable Seeds\\n\\nAvoid initializing \`math/rand\` with predictable seeds, such as timestamps or other easily guessable values, especially in a security context.\\n\\n❌ Don't Use for Security Purposes\\n\\nNever rely on \`math/rand\` for generating random numbers in cryptographic applications, like key generation, authentication tokens, or any form of security challenge.\\n\\n## Resources\\n\\n- [crypto/rand package documentation](https://pkg.go.dev/crypto/rand)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_random", + "line_number": 27, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 27, + "end": 27, + "column": { + "start": 9, + "end": 22 + } + }, + "sink": { + "start": 27, + "end": 27, + "column": { + "start": 9, + "end": 22 + }, + "content": "rand.Intn(10)" + }, + "parent_line_number": 27, + "snippet": "rand.Intn(10)", + "fingerprint": "7b6ec9847cbc39c97fa0639013146ac3_3", + "old_fingerprint": "a2f5db50454d53a8f3aa6a3845a5f64b_3", + "code_extract": "\\tbad := rand.Intn(10) // detected" + } + ] +}" +`; diff --git a/tests/go/gosec/crypto/weak_random/test.js b/tests/go/gosec/crypto/weak_random/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/crypto/weak_random/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/crypto/weak_random/testdata/main.go b/tests/go/gosec/crypto/weak_random/testdata/main.go new file mode 100644 index 000000000..26246120c --- /dev/null +++ b/tests/go/gosec/crypto/weak_random/testdata/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "math/rand" + mrand "math/rand" +) + +func bad1() { + bad := rand.Int() // detected + println(bad) +} + +func bad2() { + good, _ := rand.Read(nil) + println(good) + bad := mrand.Int31() // detected + println(bad) +} + +func badnewsource() { + gen := rand.New(rand.NewSource(10)) + bad := gen.Int() // detected + println(bad) +} + +func badIntn() { + bad := rand.Intn(10) // detected + println(bad) +} diff --git a/tests/go/gosec/crypto/weak_tls_version/__snapshots__/test.js.snap b/tests/go/gosec/crypto/weak_tls_version/__snapshots__/test.js.snap new file mode 100644 index 000000000..55f3df9fe --- /dev/null +++ b/tests/go/gosec/crypto/weak_tls_version/__snapshots__/test.js.snap @@ -0,0 +1,76 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_crypto_weak_tls_version test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "310" + ], + "id": "go_gosec_crypto_weak_tls_version", + "title": "Use of deprecated TLS version", + "description": "## Description\\n\\nTLS (Transport Layer Security) versions 1.1 and 1.0 have been deprecated due to known security vulnerabilities that can expose sensitive data to interception and attacks. Using these versions can put data transmissions at risk.\\n\\n## Remediations\\n\\nTo ensure secure data transmission, you should enforce the use of TLS 1.3, which includes security enhancements over its predecessors. The following steps can be taken:\\n\\n✅ Enforce TLS 1.3\\n\\nUpdate your server configuration to support and prefer TLS 1.3, which includes modern security features and mitigates known vulnerabilities found in older versions.\\n\\n✅ Configure Go’s TLS Library\\n\\nSet \`MinVersion\` in the \`tls.Config\` struct to \`tls.VersionTLS13\` to ensure that the server only accepts TLS 1.3 connections.\\n\\n\`\`\`go\\nimport (\\n \\"crypto/tls\\"\\n \\"log\\"\\n \\"net/http\\"\\n \\"time\\"\\n)\\n\\nfunc main() {\\n cert, err := tls.LoadX509KeyPair(\\"server.crt\\", \\"server.key\\")\\n if err != nil {\\n log.Fatalf(\\"failed to load key pair: %s\\", err)\\n }\\n\\n cfg := &tls.Config{\\n Certificates: []tls.Certificate{cert},\\n MinVersion: tls.VersionTLS13, // Enforce TLS 1.3\\n }\\n\\n srv := &http.Server{\\n Addr: \\":8999\\", // Listen on port 8999\\n TLSConfig: cfg,\\n ReadTimeout: time.Minute,\\n WriteTimeout: time.Minute,\\n }\\n\\n log.Printf(\\"Server is starting...\\")\\n log.Fatal(srv.ListenAndServeTLS(\\"\\", \\"\\")) // TLS cert and key are already provided in the TLSConfig\\n}\\n\`\`\`\\n\\n✅ Perfect Forward Secrecy (PFS)\\n\\nTLS 1.3 configurations ensure PFS by default, which protects past communications even if future session keys are compromised.\\n\\n✅ Regularly Update Dependencies\\n\\nKeep your Go version and dependencies up-to-date to benefit from the latest security fixes and improvements.\\n\\n❌ Do Not Use Deprecated TLS Versions\\n\\nAvoid configuring your server to accept TLS 1.0 or 1.1. Remove these options from your TLS configuration to prevent downgrade attacks.\\n\\n## Resources\\n\\n- [IETF's Deprecation of TLS 1.0 and 1.1](https://tools.ietf.org/html/rfc8996)\\n- [OWASP TLS Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/TLS_Security_Cheat_Sheet.html)\\n- [Go \`crypto/tls\` package documentation](https://pkg.go.dev/crypto/tls)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_tls_version", + "line_number": 12, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 12, + "end": 12, + "column": { + "start": 4, + "end": 20 + } + }, + "sink": { + "start": 12, + "end": 12, + "column": { + "start": 4, + "end": 20 + }, + "content": "tls.VersionTLS11" + }, + "parent_line_number": 12, + "snippet": "tls.VersionTLS11", + "fingerprint": "764f08ca905f8ac862ad3f7f1aedd81e_0", + "old_fingerprint": "e2cbe75a9a29dfc680f083a7d6379e4f_0", + "code_extract": "\\t\\t\\ttls.VersionTLS11," + }, + { + "cwe_ids": [ + "310" + ], + "id": "go_gosec_crypto_weak_tls_version", + "title": "Use of deprecated TLS version", + "description": "## Description\\n\\nTLS (Transport Layer Security) versions 1.1 and 1.0 have been deprecated due to known security vulnerabilities that can expose sensitive data to interception and attacks. Using these versions can put data transmissions at risk.\\n\\n## Remediations\\n\\nTo ensure secure data transmission, you should enforce the use of TLS 1.3, which includes security enhancements over its predecessors. The following steps can be taken:\\n\\n✅ Enforce TLS 1.3\\n\\nUpdate your server configuration to support and prefer TLS 1.3, which includes modern security features and mitigates known vulnerabilities found in older versions.\\n\\n✅ Configure Go’s TLS Library\\n\\nSet \`MinVersion\` in the \`tls.Config\` struct to \`tls.VersionTLS13\` to ensure that the server only accepts TLS 1.3 connections.\\n\\n\`\`\`go\\nimport (\\n \\"crypto/tls\\"\\n \\"log\\"\\n \\"net/http\\"\\n \\"time\\"\\n)\\n\\nfunc main() {\\n cert, err := tls.LoadX509KeyPair(\\"server.crt\\", \\"server.key\\")\\n if err != nil {\\n log.Fatalf(\\"failed to load key pair: %s\\", err)\\n }\\n\\n cfg := &tls.Config{\\n Certificates: []tls.Certificate{cert},\\n MinVersion: tls.VersionTLS13, // Enforce TLS 1.3\\n }\\n\\n srv := &http.Server{\\n Addr: \\":8999\\", // Listen on port 8999\\n TLSConfig: cfg,\\n ReadTimeout: time.Minute,\\n WriteTimeout: time.Minute,\\n }\\n\\n log.Printf(\\"Server is starting...\\")\\n log.Fatal(srv.ListenAndServeTLS(\\"\\", \\"\\")) // TLS cert and key are already provided in the TLSConfig\\n}\\n\`\`\`\\n\\n✅ Perfect Forward Secrecy (PFS)\\n\\nTLS 1.3 configurations ensure PFS by default, which protects past communications even if future session keys are compromised.\\n\\n✅ Regularly Update Dependencies\\n\\nKeep your Go version and dependencies up-to-date to benefit from the latest security fixes and improvements.\\n\\n❌ Do Not Use Deprecated TLS Versions\\n\\nAvoid configuring your server to accept TLS 1.0 or 1.1. Remove these options from your TLS configuration to prevent downgrade attacks.\\n\\n## Resources\\n\\n- [IETF's Deprecation of TLS 1.0 and 1.1](https://tools.ietf.org/html/rfc8996)\\n- [OWASP TLS Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/TLS_Security_Cheat_Sheet.html)\\n- [Go \`crypto/tls\` package documentation](https://pkg.go.dev/crypto/tls)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_crypto_weak_tls_version", + "line_number": 13, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 13, + "end": 13, + "column": { + "start": 4, + "end": 20 + } + }, + "sink": { + "start": 13, + "end": 13, + "column": { + "start": 4, + "end": 20 + }, + "content": "tls.VersionTLS10" + }, + "parent_line_number": 13, + "snippet": "tls.VersionTLS10", + "fingerprint": "764f08ca905f8ac862ad3f7f1aedd81e_1", + "old_fingerprint": "e2cbe75a9a29dfc680f083a7d6379e4f_1", + "code_extract": "\\t\\t\\ttls.VersionTLS10," + } + ] +}" +`; diff --git a/tests/go/gosec/crypto/weak_tls_version/test.js b/tests/go/gosec/crypto/weak_tls_version/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/crypto/weak_tls_version/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/crypto/weak_tls_version/testdata/main.go b/tests/go/gosec/crypto/weak_tls_version/testdata/main.go new file mode 100644 index 000000000..e28f8923e --- /dev/null +++ b/tests/go/gosec/crypto/weak_tls_version/testdata/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "crypto/tls" + "fmt" + "net/http" +) + +func mainbadciphersuites() { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{CipherSuites: []uint16{ + tls.VersionTLS11, + tls.VersionTLS10, + }}, + } + + client := &http.Client{Transport: tr} + _, err := client.Get("https://golang.org/") + if err != nil { + fmt.Println(err) + } +} diff --git a/tests/go/gosec/file_permissions/file_perm/__snapshots__/test.js.snap b/tests/go/gosec/file_permissions/file_perm/__snapshots__/test.js.snap new file mode 100644 index 000000000..db60fb8f3 --- /dev/null +++ b/tests/go/gosec/file_permissions/file_perm/__snapshots__/test.js.snap @@ -0,0 +1,144 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_file_permissions_file_perm test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "732" + ], + "id": "go_gosec_file_permissions_file_perm", + "title": "Incorrect permission assignment for critical resource", + "description": "## Description\\n\\nWhen creating or updating files, ensuring proper file permissions is crucial to maintain security. Overly permissive file settings can allow unauthorized users to read, modify, or execute files, which could lead to information disclosure, data tampering, or compromise of the system.\\n\\n## Remediations\\n\\nTo prevent unauthorized access, it's important to set restrictive file permissions, particularly when sensitive data is involved. Here's how to manage file permissions in Go:\\n\\n✅ Restrict File Permissions\\n\\nSet file permissions to allow only the necessary access level for the application. Avoid using permissions like \`0777\`, which allows read, write, and execute permissions for all users.\\n\\n✅ Use Go’s \`os\` Package\\n\\nUtilize the \`os.OpenFile\` function with the appropriate file permission flags.\\n\\n✅ Recommended File Permissions\\n\\n- \`0400\` grants read-only access to the file's owner.\\n- \`0200\` grants write-only access to the file's owner.\\n- \`0600\` grants read and write access to the file's owner and is commonly used for files that need to be both read from and written to by the application.\\n\\n\`\`\`go\\nimport (\\n \\"log\\"\\n \\"os\\"\\n)\\n\\nfunc main() {\\n // Use os.OpenFile to create a file with restricted permissions\\n // 0600 permission: Read and write for the owner, no permissions for others\\n f, err := os.OpenFile(\\"file.txt\\", os.O_CREATE|os.O_WRONLY, 0600)\\n if err != nil {\\n log.Fatalf(\\"failed to create file: %s\\", err)\\n }\\n defer f.Close()\\n // Continue to work with the file here\\n}\\n\`\`\`\\n\\n✅ Verify File Permissions\\n\\nAfter file creation, check the file permissions to ensure they are set correctly.\\n\\n✅ Secure Default Permissions\\n\\nIf you are developing an application that creates multiple files, consider setting umask in your application to a secure default.\\n\\n✅ Review File Permission Settings\\n\\nRegularly audit file permissions to ensure they adhere to the principle of least privilege.\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_file_permissions_file_perm", + "line_number": 10, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 10, + "end": 10, + "column": { + "start": 9, + "end": 40 + } + }, + "sink": { + "start": 10, + "end": 10, + "column": { + "start": 9, + "end": 40 + }, + "content": "os.Chmod(\\"/tmp/somefile\\", 0777)" + }, + "parent_line_number": 10, + "snippet": "os.Chmod(\\"/tmp/somefile\\", 0777)", + "fingerprint": "209f59925365847834972fad1f6b8239_0", + "old_fingerprint": "d7910697f643b646416f0860cd20d867_0", + "code_extract": "\\terr := os.Chmod(\\"/tmp/somefile\\", 0777) // detected" + }, + { + "cwe_ids": [ + "732" + ], + "id": "go_gosec_file_permissions_file_perm", + "title": "Incorrect permission assignment for critical resource", + "description": "## Description\\n\\nWhen creating or updating files, ensuring proper file permissions is crucial to maintain security. Overly permissive file settings can allow unauthorized users to read, modify, or execute files, which could lead to information disclosure, data tampering, or compromise of the system.\\n\\n## Remediations\\n\\nTo prevent unauthorized access, it's important to set restrictive file permissions, particularly when sensitive data is involved. Here's how to manage file permissions in Go:\\n\\n✅ Restrict File Permissions\\n\\nSet file permissions to allow only the necessary access level for the application. Avoid using permissions like \`0777\`, which allows read, write, and execute permissions for all users.\\n\\n✅ Use Go’s \`os\` Package\\n\\nUtilize the \`os.OpenFile\` function with the appropriate file permission flags.\\n\\n✅ Recommended File Permissions\\n\\n- \`0400\` grants read-only access to the file's owner.\\n- \`0200\` grants write-only access to the file's owner.\\n- \`0600\` grants read and write access to the file's owner and is commonly used for files that need to be both read from and written to by the application.\\n\\n\`\`\`go\\nimport (\\n \\"log\\"\\n \\"os\\"\\n)\\n\\nfunc main() {\\n // Use os.OpenFile to create a file with restricted permissions\\n // 0600 permission: Read and write for the owner, no permissions for others\\n f, err := os.OpenFile(\\"file.txt\\", os.O_CREATE|os.O_WRONLY, 0600)\\n if err != nil {\\n log.Fatalf(\\"failed to create file: %s\\", err)\\n }\\n defer f.Close()\\n // Continue to work with the file here\\n}\\n\`\`\`\\n\\n✅ Verify File Permissions\\n\\nAfter file creation, check the file permissions to ensure they are set correctly.\\n\\n✅ Secure Default Permissions\\n\\nIf you are developing an application that creates multiple files, consider setting umask in your application to a secure default.\\n\\n✅ Review File Permission Settings\\n\\nRegularly audit file permissions to ensure they adhere to the principle of least privilege.\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_file_permissions_file_perm", + "line_number": 18, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 18, + "end": 18, + "column": { + "start": 12, + "end": 68 + } + }, + "sink": { + "start": 18, + "end": 18, + "column": { + "start": 12, + "end": 68 + }, + "content": "os.OpenFile(\\"/tmp/thing\\", os.O_CREATE|os.O_WRONLY, 0666)" + }, + "parent_line_number": 18, + "snippet": "os.OpenFile(\\"/tmp/thing\\", os.O_CREATE|os.O_WRONLY, 0666)", + "fingerprint": "209f59925365847834972fad1f6b8239_1", + "old_fingerprint": "d7910697f643b646416f0860cd20d867_1", + "code_extract": "\\t_, err := os.OpenFile(\\"/tmp/thing\\", os.O_CREATE|os.O_WRONLY, 0666) // detected" + }, + { + "cwe_ids": [ + "732" + ], + "id": "go_gosec_file_permissions_file_perm", + "title": "Incorrect permission assignment for critical resource", + "description": "## Description\\n\\nWhen creating or updating files, ensuring proper file permissions is crucial to maintain security. Overly permissive file settings can allow unauthorized users to read, modify, or execute files, which could lead to information disclosure, data tampering, or compromise of the system.\\n\\n## Remediations\\n\\nTo prevent unauthorized access, it's important to set restrictive file permissions, particularly when sensitive data is involved. Here's how to manage file permissions in Go:\\n\\n✅ Restrict File Permissions\\n\\nSet file permissions to allow only the necessary access level for the application. Avoid using permissions like \`0777\`, which allows read, write, and execute permissions for all users.\\n\\n✅ Use Go’s \`os\` Package\\n\\nUtilize the \`os.OpenFile\` function with the appropriate file permission flags.\\n\\n✅ Recommended File Permissions\\n\\n- \`0400\` grants read-only access to the file's owner.\\n- \`0200\` grants write-only access to the file's owner.\\n- \`0600\` grants read and write access to the file's owner and is commonly used for files that need to be both read from and written to by the application.\\n\\n\`\`\`go\\nimport (\\n \\"log\\"\\n \\"os\\"\\n)\\n\\nfunc main() {\\n // Use os.OpenFile to create a file with restricted permissions\\n // 0600 permission: Read and write for the owner, no permissions for others\\n f, err := os.OpenFile(\\"file.txt\\", os.O_CREATE|os.O_WRONLY, 0600)\\n if err != nil {\\n log.Fatalf(\\"failed to create file: %s\\", err)\\n }\\n defer f.Close()\\n // Continue to work with the file here\\n}\\n\`\`\`\\n\\n✅ Verify File Permissions\\n\\nAfter file creation, check the file permissions to ensure they are set correctly.\\n\\n✅ Secure Default Permissions\\n\\nIf you are developing an application that creates multiple files, consider setting umask in your application to a secure default.\\n\\n✅ Review File Permission Settings\\n\\nRegularly audit file permissions to ensure they adhere to the principle of least privilege.\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_file_permissions_file_perm", + "line_number": 43, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 43, + "end": 43, + "column": { + "start": 12, + "end": 82 + } + }, + "sink": { + "start": 43, + "end": 43, + "column": { + "start": 12, + "end": 82 + }, + "content": "os.OpenFile(\\"/tmp/thing\\", os.O_CREATE|os.O_WRONLY, fs.FileMode(perms))" + }, + "parent_line_number": 43, + "snippet": "os.OpenFile(\\"/tmp/thing\\", os.O_CREATE|os.O_WRONLY, fs.FileMode(perms))", + "fingerprint": "209f59925365847834972fad1f6b8239_2", + "old_fingerprint": "d7910697f643b646416f0860cd20d867_2", + "code_extract": "\\t_, err := os.OpenFile(\\"/tmp/thing\\", os.O_CREATE|os.O_WRONLY, fs.FileMode(perms)) // detected" + }, + { + "cwe_ids": [ + "732" + ], + "id": "go_gosec_file_permissions_file_perm", + "title": "Incorrect permission assignment for critical resource", + "description": "## Description\\n\\nWhen creating or updating files, ensuring proper file permissions is crucial to maintain security. Overly permissive file settings can allow unauthorized users to read, modify, or execute files, which could lead to information disclosure, data tampering, or compromise of the system.\\n\\n## Remediations\\n\\nTo prevent unauthorized access, it's important to set restrictive file permissions, particularly when sensitive data is involved. Here's how to manage file permissions in Go:\\n\\n✅ Restrict File Permissions\\n\\nSet file permissions to allow only the necessary access level for the application. Avoid using permissions like \`0777\`, which allows read, write, and execute permissions for all users.\\n\\n✅ Use Go’s \`os\` Package\\n\\nUtilize the \`os.OpenFile\` function with the appropriate file permission flags.\\n\\n✅ Recommended File Permissions\\n\\n- \`0400\` grants read-only access to the file's owner.\\n- \`0200\` grants write-only access to the file's owner.\\n- \`0600\` grants read and write access to the file's owner and is commonly used for files that need to be both read from and written to by the application.\\n\\n\`\`\`go\\nimport (\\n \\"log\\"\\n \\"os\\"\\n)\\n\\nfunc main() {\\n // Use os.OpenFile to create a file with restricted permissions\\n // 0600 permission: Read and write for the owner, no permissions for others\\n f, err := os.OpenFile(\\"file.txt\\", os.O_CREATE|os.O_WRONLY, 0600)\\n if err != nil {\\n log.Fatalf(\\"failed to create file: %s\\", err)\\n }\\n defer f.Close()\\n // Continue to work with the file here\\n}\\n\`\`\`\\n\\n✅ Verify File Permissions\\n\\nAfter file creation, check the file permissions to ensure they are set correctly.\\n\\n✅ Secure Default Permissions\\n\\nIf you are developing an application that creates multiple files, consider setting umask in your application to a secure default.\\n\\n✅ Review File Permission Settings\\n\\nRegularly audit file permissions to ensure they adhere to the principle of least privilege.\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_file_permissions_file_perm", + "line_number": 51, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 51, + "end": 51, + "column": { + "start": 9, + "end": 50 + } + }, + "sink": { + "start": 51, + "end": 51, + "column": { + "start": 9, + "end": 50 + }, + "content": "os.WriteFile(\\"/tmp/thing\\", content, 0666)" + }, + "parent_line_number": 51, + "snippet": "os.WriteFile(\\"/tmp/thing\\", content, 0666)", + "fingerprint": "209f59925365847834972fad1f6b8239_3", + "old_fingerprint": "d7910697f643b646416f0860cd20d867_3", + "code_extract": "\\terr := os.WriteFile(\\"/tmp/thing\\", content, 0666) // detected" + } + ] +}" +`; diff --git a/tests/go/gosec/file_permissions/file_perm/test.js b/tests/go/gosec/file_permissions/file_perm/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/file_permissions/file_perm/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/file_permissions/file_perm/testdata/main.go b/tests/go/gosec/file_permissions/file_perm/testdata/main.go new file mode 100644 index 000000000..7f68044ae --- /dev/null +++ b/tests/go/gosec/file_permissions/file_perm/testdata/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "fmt" + "io/fs" + "os" +) + +func foo1() { + err := os.Chmod("/tmp/somefile", 0777) // detected + if err != nil { + fmt.Println("Error when changing file permissions!") + return + } +} + +func foo2() { + _, err := os.OpenFile("/tmp/thing", os.O_CREATE|os.O_WRONLY, 0666) // detected + if err != nil { + fmt.Println("Error opening a file!") + return + } +} + +func foo3() { + err := os.Chmod("/tmp/mydir", 0400) + if err != nil { + fmt.Println("Error") + return + } +} + +func foo4() { + _, err := os.OpenFile("/tmp/thing", os.O_CREATE|os.O_WRONLY, 0600) + if err != nil { + fmt.Println("Error opening a file!") + return + } +} + +func foo5() { + perms := 0402 + _, err := os.OpenFile("/tmp/thing", os.O_CREATE|os.O_WRONLY, fs.FileMode(perms)) // detected + if err != nil { + fmt.Println("Error opening a file!") + return + } +} + +func foo6(content []byte) { + err := os.WriteFile("/tmp/thing", content, 0666) // detected + if err != nil { + fmt.Println("Error opening a file!") + return + } +} diff --git a/tests/go/gosec/file_permissions/mkdir/__snapshots__/test.js.snap b/tests/go/gosec/file_permissions/mkdir/__snapshots__/test.js.snap new file mode 100644 index 000000000..027551441 --- /dev/null +++ b/tests/go/gosec/file_permissions/mkdir/__snapshots__/test.js.snap @@ -0,0 +1,76 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_file_permissions_mkdir test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "732" + ], + "id": "go_gosec_file_permissions_mkdir", + "title": "Incorrect permission assignment for critical resource", + "description": "## Description\\n\\nSetting correct directory permissions is critical to maintaining the security of a system. Directories with overly permissive access rights can become a vector for security breaches, allowing unauthorized users to add, remove, or change files, potentially leading to the execution of malicious code, data leaks, or system compromise.\\n\\n## Remediations\\n\\nWhen creating directories, apply the principle of least privilege to ensure users have only the permissions necessary for their role:\\n\\n✅ Restrict Directory Permissions\\n\\nUse permissions that restrict access to what is strictly necessary for the operation of the application. Avoid overly permissive settings such as \`0777\`, which allow all users to read, write, and execute.\\n\\n✅ Use Go’s \`os\` Package\\n\\nLeverage the \`os.Mkdir\` or \`os.MkdirAll\` function with appropriate permission flags to create directories.\\n\\n✅ Recommended Directory Permissions\\n\\n- \`0700\` gives the owner read, write, and execute permissions, with no access for group and others, suitable for private user data.\\n- \`0750\` gives the owner full permissions, the group read and execute permissions, and no permissions for others, which is commonly used for directories that need to be shared within a group.\\n\\n\`\`\`go\\nimport (\\n \\"log\\"\\n \\"os\\"\\n)\\n\\nfunc main() {\\n // Use os.Mkdir to create a directory with restricted permissions\\n // 0700 permission: Full control for the owner, no permissions for group and others\\n err := os.Mkdir(\\"secure_directory\\", 0700)\\n if err != nil {\\n log.Fatalf(\\"failed to create directory: %s\\", err)\\n }\\n // Continue setting up the directory here\\n}\\n\`\`\`\\n\\n✅ Verify Directory Permissions\\n\\nAfter creating a directory, confirm the permissions to ensure they have been set correctly.\\n\\n✅ Set Secure Umask\\n\\nConsider setting a secure umask in your application or user profile to ensure that all newly created files and directories have restrictive permissions by default.\\n\\n✅ Regular Auditing\\n\\nImplement regular checks of directory permissions as part of your security auditing procedures to identify and correct any permissions that are too broad.\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_file_permissions_mkdir", + "line_number": 17, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 17, + "end": 17, + "column": { + "start": 9, + "end": 37 + } + }, + "sink": { + "start": 17, + "end": 17, + "column": { + "start": 9, + "end": 37 + }, + "content": "os.Mkdir(\\"/tmp/mydir\\", 0777)" + }, + "parent_line_number": 17, + "snippet": "os.Mkdir(\\"/tmp/mydir\\", 0777)", + "fingerprint": "48915bcd60ffd3454f55db2924e59c62_0", + "old_fingerprint": "253074ffb8917f7c68f698b97a6d661b_0", + "code_extract": "\\terr := os.Mkdir(\\"/tmp/mydir\\", 0777)" + }, + { + "cwe_ids": [ + "732" + ], + "id": "go_gosec_file_permissions_mkdir", + "title": "Incorrect permission assignment for critical resource", + "description": "## Description\\n\\nSetting correct directory permissions is critical to maintaining the security of a system. Directories with overly permissive access rights can become a vector for security breaches, allowing unauthorized users to add, remove, or change files, potentially leading to the execution of malicious code, data leaks, or system compromise.\\n\\n## Remediations\\n\\nWhen creating directories, apply the principle of least privilege to ensure users have only the permissions necessary for their role:\\n\\n✅ Restrict Directory Permissions\\n\\nUse permissions that restrict access to what is strictly necessary for the operation of the application. Avoid overly permissive settings such as \`0777\`, which allow all users to read, write, and execute.\\n\\n✅ Use Go’s \`os\` Package\\n\\nLeverage the \`os.Mkdir\` or \`os.MkdirAll\` function with appropriate permission flags to create directories.\\n\\n✅ Recommended Directory Permissions\\n\\n- \`0700\` gives the owner read, write, and execute permissions, with no access for group and others, suitable for private user data.\\n- \`0750\` gives the owner full permissions, the group read and execute permissions, and no permissions for others, which is commonly used for directories that need to be shared within a group.\\n\\n\`\`\`go\\nimport (\\n \\"log\\"\\n \\"os\\"\\n)\\n\\nfunc main() {\\n // Use os.Mkdir to create a directory with restricted permissions\\n // 0700 permission: Full control for the owner, no permissions for group and others\\n err := os.Mkdir(\\"secure_directory\\", 0700)\\n if err != nil {\\n log.Fatalf(\\"failed to create directory: %s\\", err)\\n }\\n // Continue setting up the directory here\\n}\\n\`\`\`\\n\\n✅ Verify Directory Permissions\\n\\nAfter creating a directory, confirm the permissions to ensure they have been set correctly.\\n\\n✅ Set Secure Umask\\n\\nConsider setting a secure umask in your application or user profile to ensure that all newly created files and directories have restrictive permissions by default.\\n\\n✅ Regular Auditing\\n\\nImplement regular checks of directory permissions as part of your security auditing procedures to identify and correct any permissions that are too broad.\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_file_permissions_mkdir", + "line_number": 25, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 25, + "end": 25, + "column": { + "start": 9, + "end": 40 + } + }, + "sink": { + "start": 25, + "end": 25, + "column": { + "start": 9, + "end": 40 + }, + "content": "os.MkdirAll(\\"/tmp/mydir\\", 0777)" + }, + "parent_line_number": 25, + "snippet": "os.MkdirAll(\\"/tmp/mydir\\", 0777)", + "fingerprint": "48915bcd60ffd3454f55db2924e59c62_1", + "old_fingerprint": "253074ffb8917f7c68f698b97a6d661b_1", + "code_extract": "\\terr := os.MkdirAll(\\"/tmp/mydir\\", 0777)" + } + ] +}" +`; diff --git a/tests/go/gosec/file_permissions/mkdir/test.js b/tests/go/gosec/file_permissions/mkdir/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/file_permissions/mkdir/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/file_permissions/mkdir/testdata/main.go b/tests/go/gosec/file_permissions/mkdir/testdata/main.go new file mode 100644 index 000000000..447c968b1 --- /dev/null +++ b/tests/go/gosec/file_permissions/mkdir/testdata/main.go @@ -0,0 +1,46 @@ +package main + +import ( + "fmt" + "os" +) + +func foo0() { + err := os.Mkdir("/tmp/mydir", 0700) + if err != nil { + fmt.Println("Error when creating a directory!") + return + } +} + +func foo1() { + err := os.Mkdir("/tmp/mydir", 0777) + if err != nil { + fmt.Println("Error when creating a directory!") + return + } +} + +func foo2() { + err := os.MkdirAll("/tmp/mydir", 0777) + if err != nil { + fmt.Println("Error when creating a directory!") + return + } +} + +func foo3() { + err := os.Mkdir("/tmp/mydir", 0600) + if err != nil { + fmt.Println("Error when creating a directory!") + return + } +} + +func foo4() { + err := os.Mkdir("/tmp/mydir", 0750) + if err != nil { + fmt.Println("Error when creating a directory!") + return + } +} diff --git a/tests/go/gosec/filesystem/decompression_bomb/__snapshots__/test.js.snap b/tests/go/gosec/filesystem/decompression_bomb/__snapshots__/test.js.snap new file mode 100644 index 000000000..6335066b2 --- /dev/null +++ b/tests/go/gosec/filesystem/decompression_bomb/__snapshots__/test.js.snap @@ -0,0 +1,110 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_filesystem_decompression_bomb test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_filesystem_decompression_bomb", + "title": "Use of a Broken or Risky Cryptographic Algorithm", + "description": "## Description\\n\\nDecompression bombs are a form of attack against an application or service that processes compressed files. The attacker crafts a compressed file that is small in size, but when decompressed, expands to a much larger size that is disproportionate to the original. This can exhaust system resources like CPU, memory, or disk space, leading to a Denial of Service (DoS).\\n\\n## Remediations\\n\\nImplement measures to mitigate the impact of decompression bombs:\\n\\n✅ Limit Decompression Size\\n\\nUse \`io.LimitReader\` to restrict the amount of data that a reader will decompress. This prevents the decompression of large files that could fill up memory or disk space.\\n\\n✅ Monitor Resource Usage\\n\\nImplement resource monitoring to watch for unexpected spikes in CPU, memory, or disk usage, which could indicate an attempted decompression bomb attack.\\n\\n✅ Input Validation\\n\\nValidate the size and type of the input before decompressing. If possible, reject files that do not meet expected criteria.\\n\\n✅ Fail Safely\\n\\nEnsure that your application can handle errors from the decompression process safely, without crashing or becoming unresponsive.\\n\\n✅ Regular Updates\\n\\nKeep compression libraries up to date with the latest security patches to protect against known vulnerabilities.\\n\\n✅ User Education\\n\\nEducate users about the risks of decompression bombs if they are able to upload compressed files.\\n\\n\`\`\`go\\nimport (\\n \\"compress/gzip\\"\\n \\"io\\"\\n \\"log\\"\\n \\"os\\"\\n)\\n\\nfunc main() {\\n // Open the gzip file\\n f, err := os.Open(\\"example.gz\\")\\n if err != nil {\\n log.Fatal(err)\\n }\\n defer f.Close()\\n\\n // Create a gzip reader on the file\\n r, err := gzip.NewReader(f)\\n if err != nil {\\n log.Fatal(err)\\n }\\n defer r.Close()\\n\\n // Define a limit for decompression\\n const maxDecompressSize = 10 * 1024 * 1024 // 10 MB\\n\\n // Limit the size of the reader\\n limitedReader := io.LimitReader(r, maxDecompressSize)\\n\\n // Use the limited reader to decompress, preventing the decompression bomb from expanding fully\\n if _, err := io.Copy(os.Stdout, limitedReader); err != nil {\\n log.Fatal(err)\\n }\\n}\\n\`\`\`\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_decompression_bomb", + "line_number": 24, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 24, + "end": 24, + "column": { + "start": 11, + "end": 32 + } + }, + "sink": { + "start": 24, + "end": 24, + "column": { + "start": 11, + "end": 32 + }, + "content": "io.Copy(os.Stdout, r)" + }, + "parent_line_number": 24, + "snippet": "io.Copy(os.Stdout, r)", + "fingerprint": "2d4190d402514b2a6ed40a15b61c33ea_0", + "old_fingerprint": "b56cbf630ceb5b94f25b38725079b469_0", + "code_extract": "\\t_, err = io.Copy(os.Stdout, r)" + }, + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_filesystem_decompression_bomb", + "title": "Use of a Broken or Risky Cryptographic Algorithm", + "description": "## Description\\n\\nDecompression bombs are a form of attack against an application or service that processes compressed files. The attacker crafts a compressed file that is small in size, but when decompressed, expands to a much larger size that is disproportionate to the original. This can exhaust system resources like CPU, memory, or disk space, leading to a Denial of Service (DoS).\\n\\n## Remediations\\n\\nImplement measures to mitigate the impact of decompression bombs:\\n\\n✅ Limit Decompression Size\\n\\nUse \`io.LimitReader\` to restrict the amount of data that a reader will decompress. This prevents the decompression of large files that could fill up memory or disk space.\\n\\n✅ Monitor Resource Usage\\n\\nImplement resource monitoring to watch for unexpected spikes in CPU, memory, or disk usage, which could indicate an attempted decompression bomb attack.\\n\\n✅ Input Validation\\n\\nValidate the size and type of the input before decompressing. If possible, reject files that do not meet expected criteria.\\n\\n✅ Fail Safely\\n\\nEnsure that your application can handle errors from the decompression process safely, without crashing or becoming unresponsive.\\n\\n✅ Regular Updates\\n\\nKeep compression libraries up to date with the latest security patches to protect against known vulnerabilities.\\n\\n✅ User Education\\n\\nEducate users about the risks of decompression bombs if they are able to upload compressed files.\\n\\n\`\`\`go\\nimport (\\n \\"compress/gzip\\"\\n \\"io\\"\\n \\"log\\"\\n \\"os\\"\\n)\\n\\nfunc main() {\\n // Open the gzip file\\n f, err := os.Open(\\"example.gz\\")\\n if err != nil {\\n log.Fatal(err)\\n }\\n defer f.Close()\\n\\n // Create a gzip reader on the file\\n r, err := gzip.NewReader(f)\\n if err != nil {\\n log.Fatal(err)\\n }\\n defer r.Close()\\n\\n // Define a limit for decompression\\n const maxDecompressSize = 10 * 1024 * 1024 // 10 MB\\n\\n // Limit the size of the reader\\n limitedReader := io.LimitReader(r, maxDecompressSize)\\n\\n // Use the limited reader to decompress, preventing the decompression bomb from expanding fully\\n if _, err := io.Copy(os.Stdout, limitedReader); err != nil {\\n log.Fatal(err)\\n }\\n}\\n\`\`\`\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_decompression_bomb", + "line_number": 40, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 40, + "end": 40, + "column": { + "start": 11, + "end": 43 + } + }, + "sink": { + "start": 40, + "end": 40, + "column": { + "start": 11, + "end": 43 + }, + "content": "io.CopyBuffer(os.Stdout, r, buf)" + }, + "parent_line_number": 40, + "snippet": "io.CopyBuffer(os.Stdout, r, buf)", + "fingerprint": "2d4190d402514b2a6ed40a15b61c33ea_1", + "old_fingerprint": "b56cbf630ceb5b94f25b38725079b469_1", + "code_extract": "\\t_, err = io.CopyBuffer(os.Stdout, r, buf)" + }, + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_filesystem_decompression_bomb", + "title": "Use of a Broken or Risky Cryptographic Algorithm", + "description": "## Description\\n\\nDecompression bombs are a form of attack against an application or service that processes compressed files. The attacker crafts a compressed file that is small in size, but when decompressed, expands to a much larger size that is disproportionate to the original. This can exhaust system resources like CPU, memory, or disk space, leading to a Denial of Service (DoS).\\n\\n## Remediations\\n\\nImplement measures to mitigate the impact of decompression bombs:\\n\\n✅ Limit Decompression Size\\n\\nUse \`io.LimitReader\` to restrict the amount of data that a reader will decompress. This prevents the decompression of large files that could fill up memory or disk space.\\n\\n✅ Monitor Resource Usage\\n\\nImplement resource monitoring to watch for unexpected spikes in CPU, memory, or disk usage, which could indicate an attempted decompression bomb attack.\\n\\n✅ Input Validation\\n\\nValidate the size and type of the input before decompressing. If possible, reject files that do not meet expected criteria.\\n\\n✅ Fail Safely\\n\\nEnsure that your application can handle errors from the decompression process safely, without crashing or becoming unresponsive.\\n\\n✅ Regular Updates\\n\\nKeep compression libraries up to date with the latest security patches to protect against known vulnerabilities.\\n\\n✅ User Education\\n\\nEducate users about the risks of decompression bombs if they are able to upload compressed files.\\n\\n\`\`\`go\\nimport (\\n \\"compress/gzip\\"\\n \\"io\\"\\n \\"log\\"\\n \\"os\\"\\n)\\n\\nfunc main() {\\n // Open the gzip file\\n f, err := os.Open(\\"example.gz\\")\\n if err != nil {\\n log.Fatal(err)\\n }\\n defer f.Close()\\n\\n // Create a gzip reader on the file\\n r, err := gzip.NewReader(f)\\n if err != nil {\\n log.Fatal(err)\\n }\\n defer r.Close()\\n\\n // Define a limit for decompression\\n const maxDecompressSize = 10 * 1024 * 1024 // 10 MB\\n\\n // Limit the size of the reader\\n limitedReader := io.LimitReader(r, maxDecompressSize)\\n\\n // Use the limited reader to decompress, preventing the decompression bomb from expanding fully\\n if _, err := io.Copy(os.Stdout, limitedReader); err != nil {\\n log.Fatal(err)\\n }\\n}\\n\`\`\`\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_decompression_bomb", + "line_number": 62, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 62, + "end": 62, + "column": { + "start": 12, + "end": 28 + } + }, + "sink": { + "start": 62, + "end": 62, + "column": { + "start": 12, + "end": 28 + }, + "content": "io.Copy(out, rc)" + }, + "parent_line_number": 62, + "snippet": "io.Copy(out, rc)", + "fingerprint": "2d4190d402514b2a6ed40a15b61c33ea_2", + "old_fingerprint": "b56cbf630ceb5b94f25b38725079b469_2", + "code_extract": "\\t\\t_, err = io.Copy(out, rc)" + } + ] +}" +`; diff --git a/tests/go/gosec/filesystem/decompression_bomb/test.js b/tests/go/gosec/filesystem/decompression_bomb/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/filesystem/decompression_bomb/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/filesystem/decompression_bomb/testdata/main.go b/tests/go/gosec/filesystem/decompression_bomb/testdata/main.go new file mode 100644 index 000000000..29a1aa159 --- /dev/null +++ b/tests/go/gosec/filesystem/decompression_bomb/testdata/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "archive/zip" + "bytes" + "compress/zlib" + "io" + "os" + "strconv" +) + +func foo1() { + buff := []byte{120, 156, 202, 72, 205, 201, 201, 215, 81, 40, 207, + 47, 202, 73, 225, 2, 4, 0, 0, 255, 255, 33, 231, 4, 147} + b := bytes.NewReader(buff) + r, err := zlib.NewReader(b) + if err != nil { + panic(err) + } + _, err = io.Copy(os.Stdout, r) + if err != nil { + panic(err) + } + r.Close() +} + +func foo2() { + buff := []byte{120, 156, 202, 72, 205, 201, 201, 215, 81, 40, 207, + 47, 202, 73, 225, 2, 4, 0, 0, 255, 255, 33, 231, 4, 147} + b := bytes.NewReader(buff) + r, err := zlib.NewReader(b) + if err != nil { + panic(err) + } + buf := make([]byte, 8) + _, err = io.CopyBuffer(os.Stdout, r, buf) + if err != nil { + panic(err) + } + r.Close() +} + +func foo3() { + r, err := zip.OpenReader("tmp.zip") + if err != nil { + panic(err) + } + defer r.Close() + for i, f := range r.File { + out, err := os.OpenFile("output"+strconv.Itoa(i), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + panic(err) + } + rc, err := f.Open() + if err != nil { + panic(err) + } + _, err = io.Copy(out, rc) + out.Close() + rc.Close() + if err != nil { + panic(err) + } + } +} + +func foo4() { + s, err := os.Open("src") + if err != nil { + panic(err) + } + defer s.Close() + d, err := os.Create("dst") + if err != nil { + panic(err) + } + defer d.Close() + _, err = io.Copy(d, s) + if err != nil { + panic(err) + } +} diff --git a/tests/go/gosec/filesystem/dirtraversal/__snapshots__/test.js.snap b/tests/go/gosec/filesystem/dirtraversal/__snapshots__/test.js.snap new file mode 100644 index 000000000..20cd95e10 --- /dev/null +++ b/tests/go/gosec/filesystem/dirtraversal/__snapshots__/test.js.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_filesystem_dirtraversal test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_filesystem_dirtraversal", + "title": "Relative path traversal", + "description": "## Description\\n\\nMounting the root directory (\`/\`) in an HTTP server is a significant security risk. It potentially allows anyone with access to the HTTP service to browse and access system files, which can lead to information disclosure, data breaches, or further exploitation of the system.\\n\\n## Remediations\\n\\nImplement the following measures to prevent exposing the entire filesystem through your web server:\\n\\n✅ Serve Specific Directory\\n\\nChange the \`http.Dir\` to serve files from a specific, safe directory intended for public access rather than the root directory. Ensure this directory contains only the files that are meant to be publicly accessible.\\n\\n✅ Access Control\\n\\nApply appropriate permissions to the directory being served to ensure that the server process can only access the files that it's supposed to serve.\\n\\n✅ Use of Configuration Files\\n\\nIf supported, use configuration files like \`.htaccess\` (for Apache HTTP Server) or equivalent server configuration to control access to directories.\\n\\n✅ Isolate Environment\\n\\nConsider running your server in a containerized or virtualized environment with strict access controls to limit the potential damage in case of a security breach.\\n\\n✅ Regular Audits\\n\\nPerform regular audits of the filesystem and the files being served to ensure that no sensitive information is being unintentionally exposed.\\n\\n\`\`\`go\\nimport (\\n \\"net/http\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n // Define the specific path to a directory to be served\\n const safePath = \\"/var/www/html/public\\"\\n\\n // Create a new file server handler that serves files from the safePath\\n fs := http.FileServer(http.Dir(safePath))\\n\\n // Configure the server to handle requests to the root with the file server handler\\n http.Handle(\\"/\\", http.StripPrefix(\\"/\\", fs))\\n\\n // Start the server\\n log.Fatal(http.ListenAndServe(\\":9000\\", nil))\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [Go Documentation: http package](https://pkg.go.dev/net/http)\\n- [OWASP: Securing File Uploads](https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload)\\n- [NIST Guidelines on Securing Public Web Servers](https://csrc.nist.gov/publications/detail/sp/800-44/version-2/final)\\n- [Docker Documentation: Use containers for isolation](https://docs.docker.com/get-started/overview/#use-containers-for-isolation)\\n- [Linux man page for chmod (file permissions)](https://linux.die.net/man/1/chmod)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_dirtraversal", + "line_number": 10, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 10, + "end": 10, + "column": { + "start": 7, + "end": 20 + } + }, + "sink": { + "start": 10, + "end": 10, + "column": { + "start": 7, + "end": 20 + }, + "content": "net2.Dir(\\"/\\")" + }, + "parent_line_number": 10, + "snippet": "net2.Dir(\\"/\\")", + "fingerprint": "3a1c1dd6de643ca21a3d5b17af97729a_0", + "old_fingerprint": "bb634df7af20e27aa8f7123da072efdc_0", + "code_extract": "\\td := net2.Dir(\\"/\\")" + } + ] +}" +`; diff --git a/tests/go/gosec/filesystem/dirtraversal/test.js b/tests/go/gosec/filesystem/dirtraversal/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/filesystem/dirtraversal/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/filesystem/dirtraversal/testdata/main.go b/tests/go/gosec/filesystem/dirtraversal/testdata/main.go new file mode 100644 index 000000000..84ecee727 --- /dev/null +++ b/tests/go/gosec/filesystem/dirtraversal/testdata/main.go @@ -0,0 +1,16 @@ +// License: MIT (c) GitLab Inc. + +package main + +import ( + net2 "net/http" +) + +func dtrav() { + d := net2.Dir("/") + f, err := d.Open("some file") + if err != nil { + panic(err) + } + defer f.Close() +} diff --git a/tests/go/gosec/filesystem/filereadtaint/__snapshots__/test.js.snap b/tests/go/gosec/filesystem/filereadtaint/__snapshots__/test.js.snap new file mode 100644 index 000000000..f41618a71 --- /dev/null +++ b/tests/go/gosec/filesystem/filereadtaint/__snapshots__/test.js.snap @@ -0,0 +1,212 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_filesystem_filereadtaint test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_filesystem_filereadtaint", + "title": "Improper limitation of a pathname to a restricted directory ('Path Traversal')", + "description": "## Description\\n\\nConstructing file or path information dynamically, especially from user input, poses a significant security risk. If not handled correctly, attackers could manipulate these paths to access or manipulate sensitive files, leading to data breaches or system compromise. It is crucial to ensure that user input is not used directly to interact with the file system, as this can be exploited to perform path traversal attacks.\\n\\n## Remediations\\n\\nTo mitigate the risks associated with dynamic file path construction from user inputs, apply the following best practices:\\n\\n✅ Hash or Replace User Input\\n\\nWhen dealing with user input for file operations, hash the input or replace it with a system-generated unique identifier to prevent path manipulation.\\n\\n✅ Use \`filepath.Base\`\\n\\nExtract only the filename from the path, ignoring any directory information, to avoid directory traversal vulnerabilities.\\n\\n✅ Validate Paths Before Use\\n\\nAlways perform validation on the resolved paths before accessing files to ensure they are within the expected directory.\\n\\n\`\`\`go\\nimport (\\n \\"crypto/rand\\"\\n \\"encoding/hex\\"\\n \\"io\\"\\n \\"log\\"\\n \\"path/filepath\\"\\n \\"strings\\"\\n)\\n\\n// userData struct holds user-related data with a unique ID for file operations\\ntype userData struct {\\n id string // Unique identifier for the filename\\n userFilename string // Original filename from the user, kept for reference\\n}\\n\\n// newUserData constructs a new userData instance with a random file ID\\nfunc newUserData(userFilename string) userData {\\n return userData{\\n id: randomFileID(), // Use a random ID instead of user-provided filename\\n userFilename: userFilename,\\n }\\n}\\n\\n// randomFileID generates a secure random ID to be used as a filename\\nfunc randomFileID() string {\\n id := make([]byte, 16)\\n if _, err := io.ReadFull(rand.Reader, id); err != nil {\\n log.Fatal(err)\\n }\\n return hex.EncodeToString(id)\\n}\\n\\nfunc main() {\\n // Simulated user input, which may be malicious\\n data := newUserData(\\"../../possibly/malicious\\")\\n\\n // Define a safe base path for file operations\\n const basePath = \\"/tmp/\\"\\n\\n // Resolve the full path using the safe base path and the random ID\\n resolvedPath, err := filepath.Join(basePath, filepath.Base(data.id))\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Ensure the resolved path is within our designated base path\\n if !strings.HasPrefix(resolvedPath, basePath) {\\n log.Fatal(\\"The resolved path does not start with the expected base path\\")\\n }\\n\\n // The file can now be safely accessed using resolvedPath\\n // Further file processing code would go here\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP Guide to Preventing Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_filereadtaint", + "line_number": 19, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 19, + "end": 19, + "column": { + "start": 15, + "end": 33 + } + }, + "sink": { + "start": 19, + "end": 19, + "column": { + "start": 15, + "end": 33 + }, + "content": "ioutil.ReadFile(f)" + }, + "parent_line_number": 19, + "snippet": "ioutil.ReadFile(f)", + "fingerprint": "4cbdcba5327691a5836bdf26ca7b0f5d_0", + "old_fingerprint": "74ca07036b660b9689e4fe7febfbed2b_0", + "code_extract": "\\tbody, err := ioutil.ReadFile(f)" + }, + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_filesystem_filereadtaint", + "title": "Improper limitation of a pathname to a restricted directory ('Path Traversal')", + "description": "## Description\\n\\nConstructing file or path information dynamically, especially from user input, poses a significant security risk. If not handled correctly, attackers could manipulate these paths to access or manipulate sensitive files, leading to data breaches or system compromise. It is crucial to ensure that user input is not used directly to interact with the file system, as this can be exploited to perform path traversal attacks.\\n\\n## Remediations\\n\\nTo mitigate the risks associated with dynamic file path construction from user inputs, apply the following best practices:\\n\\n✅ Hash or Replace User Input\\n\\nWhen dealing with user input for file operations, hash the input or replace it with a system-generated unique identifier to prevent path manipulation.\\n\\n✅ Use \`filepath.Base\`\\n\\nExtract only the filename from the path, ignoring any directory information, to avoid directory traversal vulnerabilities.\\n\\n✅ Validate Paths Before Use\\n\\nAlways perform validation on the resolved paths before accessing files to ensure they are within the expected directory.\\n\\n\`\`\`go\\nimport (\\n \\"crypto/rand\\"\\n \\"encoding/hex\\"\\n \\"io\\"\\n \\"log\\"\\n \\"path/filepath\\"\\n \\"strings\\"\\n)\\n\\n// userData struct holds user-related data with a unique ID for file operations\\ntype userData struct {\\n id string // Unique identifier for the filename\\n userFilename string // Original filename from the user, kept for reference\\n}\\n\\n// newUserData constructs a new userData instance with a random file ID\\nfunc newUserData(userFilename string) userData {\\n return userData{\\n id: randomFileID(), // Use a random ID instead of user-provided filename\\n userFilename: userFilename,\\n }\\n}\\n\\n// randomFileID generates a secure random ID to be used as a filename\\nfunc randomFileID() string {\\n id := make([]byte, 16)\\n if _, err := io.ReadFull(rand.Reader, id); err != nil {\\n log.Fatal(err)\\n }\\n return hex.EncodeToString(id)\\n}\\n\\nfunc main() {\\n // Simulated user input, which may be malicious\\n data := newUserData(\\"../../possibly/malicious\\")\\n\\n // Define a safe base path for file operations\\n const basePath = \\"/tmp/\\"\\n\\n // Resolve the full path using the safe base path and the random ID\\n resolvedPath, err := filepath.Join(basePath, filepath.Base(data.id))\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Ensure the resolved path is within our designated base path\\n if !strings.HasPrefix(resolvedPath, basePath) {\\n log.Fatal(\\"The resolved path does not start with the expected base path\\")\\n }\\n\\n // The file can now be safely accessed using resolvedPath\\n // Further file processing code would go here\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP Guide to Preventing Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_filereadtaint", + "line_number": 30, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 30, + "end": 30, + "column": { + "start": 13, + "end": 27 + } + }, + "sink": { + "start": 30, + "end": 30, + "column": { + "start": 13, + "end": 27 + }, + "content": "os.Open(title)" + }, + "parent_line_number": 30, + "snippet": "os.Open(title)", + "fingerprint": "4cbdcba5327691a5836bdf26ca7b0f5d_1", + "old_fingerprint": "74ca07036b660b9689e4fe7febfbed2b_1", + "code_extract": "\\t\\tf, err := os.Open(title)" + }, + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_filesystem_filereadtaint", + "title": "Improper limitation of a pathname to a restricted directory ('Path Traversal')", + "description": "## Description\\n\\nConstructing file or path information dynamically, especially from user input, poses a significant security risk. If not handled correctly, attackers could manipulate these paths to access or manipulate sensitive files, leading to data breaches or system compromise. It is crucial to ensure that user input is not used directly to interact with the file system, as this can be exploited to perform path traversal attacks.\\n\\n## Remediations\\n\\nTo mitigate the risks associated with dynamic file path construction from user inputs, apply the following best practices:\\n\\n✅ Hash or Replace User Input\\n\\nWhen dealing with user input for file operations, hash the input or replace it with a system-generated unique identifier to prevent path manipulation.\\n\\n✅ Use \`filepath.Base\`\\n\\nExtract only the filename from the path, ignoring any directory information, to avoid directory traversal vulnerabilities.\\n\\n✅ Validate Paths Before Use\\n\\nAlways perform validation on the resolved paths before accessing files to ensure they are within the expected directory.\\n\\n\`\`\`go\\nimport (\\n \\"crypto/rand\\"\\n \\"encoding/hex\\"\\n \\"io\\"\\n \\"log\\"\\n \\"path/filepath\\"\\n \\"strings\\"\\n)\\n\\n// userData struct holds user-related data with a unique ID for file operations\\ntype userData struct {\\n id string // Unique identifier for the filename\\n userFilename string // Original filename from the user, kept for reference\\n}\\n\\n// newUserData constructs a new userData instance with a random file ID\\nfunc newUserData(userFilename string) userData {\\n return userData{\\n id: randomFileID(), // Use a random ID instead of user-provided filename\\n userFilename: userFilename,\\n }\\n}\\n\\n// randomFileID generates a secure random ID to be used as a filename\\nfunc randomFileID() string {\\n id := make([]byte, 16)\\n if _, err := io.ReadFull(rand.Reader, id); err != nil {\\n log.Fatal(err)\\n }\\n return hex.EncodeToString(id)\\n}\\n\\nfunc main() {\\n // Simulated user input, which may be malicious\\n data := newUserData(\\"../../possibly/malicious\\")\\n\\n // Define a safe base path for file operations\\n const basePath = \\"/tmp/\\"\\n\\n // Resolve the full path using the safe base path and the random ID\\n resolvedPath, err := filepath.Join(basePath, filepath.Base(data.id))\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Ensure the resolved path is within our designated base path\\n if !strings.HasPrefix(resolvedPath, basePath) {\\n log.Fatal(\\"The resolved path does not start with the expected base path\\")\\n }\\n\\n // The file can now be safely accessed using resolvedPath\\n // Further file processing code would go here\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP Guide to Preventing Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_filereadtaint", + "line_number": 46, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 46, + "end": 46, + "column": { + "start": 13, + "end": 60 + } + }, + "sink": { + "start": 46, + "end": 46, + "column": { + "start": 13, + "end": 60 + }, + "content": "os.OpenFile(title, os.O_RDWR|os.O_CREATE, 0755)" + }, + "parent_line_number": 46, + "snippet": "os.OpenFile(title, os.O_RDWR|os.O_CREATE, 0755)", + "fingerprint": "4cbdcba5327691a5836bdf26ca7b0f5d_2", + "old_fingerprint": "74ca07036b660b9689e4fe7febfbed2b_2", + "code_extract": "\\t\\tf, err := os.OpenFile(title, os.O_RDWR|os.O_CREATE, 0755)" + }, + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_filesystem_filereadtaint", + "title": "Improper limitation of a pathname to a restricted directory ('Path Traversal')", + "description": "## Description\\n\\nConstructing file or path information dynamically, especially from user input, poses a significant security risk. If not handled correctly, attackers could manipulate these paths to access or manipulate sensitive files, leading to data breaches or system compromise. It is crucial to ensure that user input is not used directly to interact with the file system, as this can be exploited to perform path traversal attacks.\\n\\n## Remediations\\n\\nTo mitigate the risks associated with dynamic file path construction from user inputs, apply the following best practices:\\n\\n✅ Hash or Replace User Input\\n\\nWhen dealing with user input for file operations, hash the input or replace it with a system-generated unique identifier to prevent path manipulation.\\n\\n✅ Use \`filepath.Base\`\\n\\nExtract only the filename from the path, ignoring any directory information, to avoid directory traversal vulnerabilities.\\n\\n✅ Validate Paths Before Use\\n\\nAlways perform validation on the resolved paths before accessing files to ensure they are within the expected directory.\\n\\n\`\`\`go\\nimport (\\n \\"crypto/rand\\"\\n \\"encoding/hex\\"\\n \\"io\\"\\n \\"log\\"\\n \\"path/filepath\\"\\n \\"strings\\"\\n)\\n\\n// userData struct holds user-related data with a unique ID for file operations\\ntype userData struct {\\n id string // Unique identifier for the filename\\n userFilename string // Original filename from the user, kept for reference\\n}\\n\\n// newUserData constructs a new userData instance with a random file ID\\nfunc newUserData(userFilename string) userData {\\n return userData{\\n id: randomFileID(), // Use a random ID instead of user-provided filename\\n userFilename: userFilename,\\n }\\n}\\n\\n// randomFileID generates a secure random ID to be used as a filename\\nfunc randomFileID() string {\\n id := make([]byte, 16)\\n if _, err := io.ReadFull(rand.Reader, id); err != nil {\\n log.Fatal(err)\\n }\\n return hex.EncodeToString(id)\\n}\\n\\nfunc main() {\\n // Simulated user input, which may be malicious\\n data := newUserData(\\"../../possibly/malicious\\")\\n\\n // Define a safe base path for file operations\\n const basePath = \\"/tmp/\\"\\n\\n // Resolve the full path using the safe base path and the random ID\\n resolvedPath, err := filepath.Join(basePath, filepath.Base(data.id))\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Ensure the resolved path is within our designated base path\\n if !strings.HasPrefix(resolvedPath, basePath) {\\n log.Fatal(\\"The resolved path does not start with the expected base path\\")\\n }\\n\\n // The file can now be safely accessed using resolvedPath\\n // Further file processing code would go here\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP Guide to Preventing Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_filereadtaint", + "line_number": 61, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 61, + "end": 61, + "column": { + "start": 15, + "end": 44 + } + }, + "sink": { + "start": 61, + "end": 61, + "column": { + "start": 15, + "end": 44 + }, + "content": "ioutil.ReadFile(\\"/tmp/\\" + f2)" + }, + "parent_line_number": 61, + "snippet": "ioutil.ReadFile(\\"/tmp/\\" + f2)", + "fingerprint": "4cbdcba5327691a5836bdf26ca7b0f5d_3", + "old_fingerprint": "74ca07036b660b9689e4fe7febfbed2b_3", + "code_extract": "\\tbody, err := ioutil.ReadFile(\\"/tmp/\\" + f2)" + }, + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_filesystem_filereadtaint", + "title": "Improper limitation of a pathname to a restricted directory ('Path Traversal')", + "description": "## Description\\n\\nConstructing file or path information dynamically, especially from user input, poses a significant security risk. If not handled correctly, attackers could manipulate these paths to access or manipulate sensitive files, leading to data breaches or system compromise. It is crucial to ensure that user input is not used directly to interact with the file system, as this can be exploited to perform path traversal attacks.\\n\\n## Remediations\\n\\nTo mitigate the risks associated with dynamic file path construction from user inputs, apply the following best practices:\\n\\n✅ Hash or Replace User Input\\n\\nWhen dealing with user input for file operations, hash the input or replace it with a system-generated unique identifier to prevent path manipulation.\\n\\n✅ Use \`filepath.Base\`\\n\\nExtract only the filename from the path, ignoring any directory information, to avoid directory traversal vulnerabilities.\\n\\n✅ Validate Paths Before Use\\n\\nAlways perform validation on the resolved paths before accessing files to ensure they are within the expected directory.\\n\\n\`\`\`go\\nimport (\\n \\"crypto/rand\\"\\n \\"encoding/hex\\"\\n \\"io\\"\\n \\"log\\"\\n \\"path/filepath\\"\\n \\"strings\\"\\n)\\n\\n// userData struct holds user-related data with a unique ID for file operations\\ntype userData struct {\\n id string // Unique identifier for the filename\\n userFilename string // Original filename from the user, kept for reference\\n}\\n\\n// newUserData constructs a new userData instance with a random file ID\\nfunc newUserData(userFilename string) userData {\\n return userData{\\n id: randomFileID(), // Use a random ID instead of user-provided filename\\n userFilename: userFilename,\\n }\\n}\\n\\n// randomFileID generates a secure random ID to be used as a filename\\nfunc randomFileID() string {\\n id := make([]byte, 16)\\n if _, err := io.ReadFull(rand.Reader, id); err != nil {\\n log.Fatal(err)\\n }\\n return hex.EncodeToString(id)\\n}\\n\\nfunc main() {\\n // Simulated user input, which may be malicious\\n data := newUserData(\\"../../possibly/malicious\\")\\n\\n // Define a safe base path for file operations\\n const basePath = \\"/tmp/\\"\\n\\n // Resolve the full path using the safe base path and the random ID\\n resolvedPath, err := filepath.Join(basePath, filepath.Base(data.id))\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Ensure the resolved path is within our designated base path\\n if !strings.HasPrefix(resolvedPath, basePath) {\\n log.Fatal(\\"The resolved path does not start with the expected base path\\")\\n }\\n\\n // The file can now be safely accessed using resolvedPath\\n // Further file processing code would go here\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP Guide to Preventing Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_filereadtaint", + "line_number": 73, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 73, + "end": 73, + "column": { + "start": 12, + "end": 57 + } + }, + "sink": { + "start": 73, + "end": 73, + "column": { + "start": 12, + "end": 57 + }, + "content": "os.Open(filepath.Join(\\"/tmp/service/\\", file))" + }, + "parent_line_number": 73, + "snippet": "os.Open(filepath.Join(\\"/tmp/service/\\", file))", + "fingerprint": "4cbdcba5327691a5836bdf26ca7b0f5d_4", + "old_fingerprint": "74ca07036b660b9689e4fe7febfbed2b_4", + "code_extract": "\\tf, err := os.Open(filepath.Join(\\"/tmp/service/\\", file))" + }, + { + "cwe_ids": [ + "327" + ], + "id": "go_gosec_filesystem_filereadtaint", + "title": "Improper limitation of a pathname to a restricted directory ('Path Traversal')", + "description": "## Description\\n\\nConstructing file or path information dynamically, especially from user input, poses a significant security risk. If not handled correctly, attackers could manipulate these paths to access or manipulate sensitive files, leading to data breaches or system compromise. It is crucial to ensure that user input is not used directly to interact with the file system, as this can be exploited to perform path traversal attacks.\\n\\n## Remediations\\n\\nTo mitigate the risks associated with dynamic file path construction from user inputs, apply the following best practices:\\n\\n✅ Hash or Replace User Input\\n\\nWhen dealing with user input for file operations, hash the input or replace it with a system-generated unique identifier to prevent path manipulation.\\n\\n✅ Use \`filepath.Base\`\\n\\nExtract only the filename from the path, ignoring any directory information, to avoid directory traversal vulnerabilities.\\n\\n✅ Validate Paths Before Use\\n\\nAlways perform validation on the resolved paths before accessing files to ensure they are within the expected directory.\\n\\n\`\`\`go\\nimport (\\n \\"crypto/rand\\"\\n \\"encoding/hex\\"\\n \\"io\\"\\n \\"log\\"\\n \\"path/filepath\\"\\n \\"strings\\"\\n)\\n\\n// userData struct holds user-related data with a unique ID for file operations\\ntype userData struct {\\n id string // Unique identifier for the filename\\n userFilename string // Original filename from the user, kept for reference\\n}\\n\\n// newUserData constructs a new userData instance with a random file ID\\nfunc newUserData(userFilename string) userData {\\n return userData{\\n id: randomFileID(), // Use a random ID instead of user-provided filename\\n userFilename: userFilename,\\n }\\n}\\n\\n// randomFileID generates a secure random ID to be used as a filename\\nfunc randomFileID() string {\\n id := make([]byte, 16)\\n if _, err := io.ReadFull(rand.Reader, id); err != nil {\\n log.Fatal(err)\\n }\\n return hex.EncodeToString(id)\\n}\\n\\nfunc main() {\\n // Simulated user input, which may be malicious\\n data := newUserData(\\"../../possibly/malicious\\")\\n\\n // Define a safe base path for file operations\\n const basePath = \\"/tmp/\\"\\n\\n // Resolve the full path using the safe base path and the random ID\\n resolvedPath, err := filepath.Join(basePath, filepath.Base(data.id))\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Ensure the resolved path is within our designated base path\\n if !strings.HasPrefix(resolvedPath, basePath) {\\n log.Fatal(\\"The resolved path does not start with the expected base path\\")\\n }\\n\\n // The file can now be safely accessed using resolvedPath\\n // Further file processing code would go here\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP Guide to Preventing Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_filereadtaint", + "line_number": 88, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 88, + "end": 88, + "column": { + "start": 15, + "end": 62 + } + }, + "sink": { + "start": 88, + "end": 88, + "column": { + "start": 15, + "end": 62 + }, + "content": "ioutil.ReadFile(filepath.Join(\\"/var/\\"+dir, f3))" + }, + "parent_line_number": 88, + "snippet": "ioutil.ReadFile(filepath.Join(\\"/var/\\"+dir, f3))", + "fingerprint": "4cbdcba5327691a5836bdf26ca7b0f5d_5", + "old_fingerprint": "74ca07036b660b9689e4fe7febfbed2b_5", + "code_extract": "\\tbody, err := ioutil.ReadFile(filepath.Join(\\"/var/\\"+dir, f3))" + } + ] +}" +`; diff --git a/tests/go/gosec/filesystem/filereadtaint/test.js b/tests/go/gosec/filesystem/filereadtaint/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/filesystem/filereadtaint/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/filesystem/filereadtaint/testdata/main.go b/tests/go/gosec/filesystem/filereadtaint/testdata/main.go new file mode 100644 index 000000000..0df36d1c5 --- /dev/null +++ b/tests/go/gosec/filesystem/filereadtaint/testdata/main.go @@ -0,0 +1,122 @@ +package main + +import ( + "bufio" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "path/filepath" +) + +func mainenvreadfile() { + f := os.Getenv("tainted_file") + body, err := ioutil.ReadFile(f) + if err != nil { + log.Printf("Error: %v\n", err) + } + log.Print(body) + +} + +func mainqueryopen() { + http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { + title := r.URL.Query().Get("title") + f, err := os.Open(title) + if err != nil { + fmt.Printf("Error: %v\n", err) + } + body := make([]byte, 5) + if _, err = f.Read(body); err != nil { + fmt.Printf("Error: %v\n", err) + } + fmt.Fprintf(w, "%s", body) + }) + log.Fatal(http.ListenAndServe(":3000", nil)) +} + +func mainqueryopenfile() { + http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { + title := r.URL.Query().Get("title") + f, err := os.OpenFile(title, os.O_RDWR|os.O_CREATE, 0755) + if err != nil { + fmt.Printf("Error: %v\n", err) + } + body := make([]byte, 5) + if _, err = f.Read(body); err != nil { + fmt.Printf("Error: %v\n", err) + } + fmt.Fprintf(w, "%s", body) + }) + log.Fatal(http.ListenAndServe(":3000", nil)) +} + +func mainenvreadfileappend() { + f2 := os.Getenv("tainted_file2") + body, err := ioutil.ReadFile("/tmp/" + f2) + if err != nil { + log.Printf("Error: %v\n", err) + } + log.Print(body) +} + +func mainstdinopenjoin() { + reader := bufio.NewReader(os.Stdin) + fmt.Print("Please enter file to read: ") + file, _ := reader.ReadString('\n') + file = file[:len(file)-1] + f, err := os.Open(filepath.Join("/tmp/service/", file)) + if err != nil { + fmt.Printf("Error: %v\n", err) + } + contents := make([]byte, 15) + if _, err = f.Read(contents); err != nil { + fmt.Printf("Error: %v\n", err) + } + fmt.Println(string(contents)) +} + +func mainenvreadfilejointwice() { + dir := os.Getenv("server_root") + f3 := os.Getenv("tainted_file3") + // edge case where both a binary expression and file Join are used. + body, err := ioutil.ReadFile(filepath.Join("/var/"+dir, f3)) + if err != nil { + log.Printf("Error: %v\n", err) + } + log.Print(body) +} + +func maincleanshouldTN() { + repoFile := "path_of_file" + cleanRepoFile := filepath.Clean(repoFile) + _, err := os.OpenFile(cleanRepoFile, os.O_RDONLY, 0600) + if err != nil { + panic(err) + } +} + +func openFile(filePath string) { + _, err := os.OpenFile(filepath.Clean(filePath), os.O_RDONLY, 0600) + if err != nil { + panic(err) + } +} + +func mainintraproceduralopen() { + repoFile := "path_of_file" + openFile(repoFile) +} + +func mainfilepathrelTN() { + repoFile := "path_of_file" + relFile, err := filepath.Rel("./", repoFile) + if err != nil { + panic(err) + } + _, err = os.OpenFile(relFile, os.O_RDONLY, 0600) + if err != nil { + panic(err) + } +} diff --git a/tests/go/gosec/filesystem/poor_write_permissions/__snapshots__/test.js.snap b/tests/go/gosec/filesystem/poor_write_permissions/__snapshots__/test.js.snap new file mode 100644 index 000000000..de14ed94f --- /dev/null +++ b/tests/go/gosec/filesystem/poor_write_permissions/__snapshots__/test.js.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_filesystem_poor_write_permissions test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "276" + ], + "id": "go_gosec_filesystem_poor_write_permissions", + "title": "Incorrect default permissions", + "description": "## Description\\n\\nThe application has been detected setting file permissions that are too permissive. This configuration could allow unauthorized users to read, write, or execute files, potentially leading to information disclosure or other security vulnerabilities.\\n\\n## Remediations\\n\\nTo enhance security, file permissions should be set to more restrictive values, especially when the files contain sensitive information:\\n\\n✅ Use Restrictive File Permissions\\n\\nAssign file permissions to limit access appropriately based on the application's requirements.\\n\\n- \`0400\`: Grants read-only access to the file for the owner.\\n- \`0200\`: Grants write-only access to the file for the owner.\\n- \`0600\`: Grants read and write access to the file for the owner.\\n\\n✅ Apply Permissions During File Creation\\n\\nWhen creating or modifying files, set the appropriate permissions to prevent unauthorized access.\\n\\n\`\`\`go\\nimport (\\n \\"os\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n // Data to be written to the file\\n dat := []byte(\\"sensitive data\\")\\n\\n // Write the data to 'file.txt' with read and write permissions for the owner only\\n if err := os.WriteFile(\\"file.txt\\", dat, 0600); err != nil {\\n log.Fatalf(\\"failed to write file: %s\\", err)\\n }\\n // File is now safely written with restricted permissions\\n}\\n\`\`\`\\n\\n✅ Review File Permission Settings\\n\\nRegularly audit the permissions of files to ensure they conform to the principle of least privilege.\\n\\n## Resources\\n\\n- [Go Documentation for os Package](https://pkg.go.dev/os)\\n- [Linux 'chmod' Command](https://linux.die.net/man/1/chmod)\\n- [OWASP File Handling Best Practices](https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_poor_write_permissions", + "line_number": 23, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 23, + "end": 23, + "column": { + "start": 9, + "end": 48 + } + }, + "sink": { + "start": 23, + "end": 23, + "column": { + "start": 9, + "end": 48 + }, + "content": "ioutil.WriteFile(\\"/tmp/dat1\\", d1, 0744)" + }, + "parent_line_number": 23, + "snippet": "ioutil.WriteFile(\\"/tmp/dat1\\", d1, 0744)", + "fingerprint": "608e26e1e7ff94dddcae86ff0bb9a03b_0", + "old_fingerprint": "bea73efa91d0340a983a52974a790bff_0", + "code_extract": "\\terr := ioutil.WriteFile(\\"/tmp/dat1\\", d1, 0744)" + } + ] +}" +`; diff --git a/tests/go/gosec/filesystem/poor_write_permissions/test.js b/tests/go/gosec/filesystem/poor_write_permissions/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/filesystem/poor_write_permissions/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/filesystem/poor_write_permissions/testdata/main.go b/tests/go/gosec/filesystem/poor_write_permissions/testdata/main.go new file mode 100644 index 000000000..bfed084ac --- /dev/null +++ b/tests/go/gosec/filesystem/poor_write_permissions/testdata/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "bufio" + "fmt" + "io/ioutil" + "os" +) + +func check(e error) { + if e != nil { + panic(e) + } +} + +func mainwriteperms() { + + d1 := []byte("hello\ngo\n") + err := ioutil.WriteFile("/tmp/dat1", d1, 0744) + check(err) + + allowed := ioutil.WriteFile("/tmp/dat1", d1, 0600) + check(allowed) + + f, err := os.Create("/tmp/dat2") + check(err) + + defer f.Close() + + d2 := []byte{115, 111, 109, 101, 10} + n2, err := f.Write(d2) + + defer check(err) + fmt.Printf("wrote %d bytes\n", n2) + + n3, err := f.WriteString("writes\n") + fmt.Printf("wrote %d bytes\n", n3) + + f.Sync() + + w := bufio.NewWriter(f) + n4, err := w.WriteString("buffered\n") + fmt.Printf("wrote %d bytes\n", n4) + + w.Flush() + +} diff --git a/tests/go/gosec/filesystem/tempfile/__snapshots__/test.js.snap b/tests/go/gosec/filesystem/tempfile/__snapshots__/test.js.snap new file mode 100644 index 000000000..aec640274 --- /dev/null +++ b/tests/go/gosec/filesystem/tempfile/__snapshots__/test.js.snap @@ -0,0 +1,110 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_filesystem_tempfile test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "378" + ], + "id": "go_gosec_filesystem_tempfile", + "title": "Incorrect default permissions", + "description": "## Description\\n\\nThe application has been observed creating files in shared system temporary directories, such as \`/tmp\` or \`/var/tmp\`, without the use of secure functions like \`os.CreateTemp\`. This practice is insecure because it opens up the possibility of symlink attacks, where an attacker could anticipate the temporary file name and create a symlink to a target file, leading to unauthorized file creation or overwriting when the application writes to what it believes is a temporary file.\\n\\n## Remediations\\n\\nTo prevent symlink attacks and other vulnerabilities associated with the use of shared temporary directories:\\n\\n✅ Use Secure Temporary File Creation\\n\\nImplement \`os.CreateTemp\` to safely create temporary files within a directory that's restricted to the application. This reduces the risk of symlink attacks and ensures that temporary files are handled securely.\\n\\n\`\`\`go\\nimport (\\n \\"os\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n // Ensure the application-restricted directory exists with appropriate permissions\\n restrictedDir := \\"/opt/appdir/restricted\\"\\n if err := os.MkdirAll(restrictedDir, 0700); err != nil {\\n log.Fatalf(\\"failed to create restricted directory: %s\\", err)\\n }\\n\\n // Securely create a temporary file within the restricted directory\\n f, err := os.CreateTemp(restrictedDir, \\"temp-*.txt\\")\\n if err != nil {\\n log.Fatalf(\\"failed to create temporary file: %s\\", err)\\n }\\n defer f.Close() // Ensure the file is closed when no longer needed\\n\\n defer os.Remove(f.Name()) // Clean up the file upon exit\\n // Continue working with the temporary file\\n}\\n\`\`\`\\n\\n✅ Avoid Shared Temporary Directories for Sensitive Operations\\n\\nDo not use common temporary directories for storing sensitive information or for operations that require secure handling of files.\\n\\n✅ Clean Up After Use\\n\\nAlways remove temporary files after their use to prevent accumulation and potential misuse.\\n\\n## Resources\\n\\n- [Go Documentation: os.CreateTemp](https://pkg.go.dev/os#CreateTemp)\\n- [Secure Coding Guidelines for File I/O](https://wiki.sei.cmu.edu/confluence/display/seccode/TOCTOU+Race+Conditions)\\n- [OWASP Guide to File System Security](https://owasp.org/www-community/controls/Path_Traversal)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_tempfile", + "line_number": 11, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 11, + "end": 11, + "column": { + "start": 9, + "end": 74 + } + }, + "sink": { + "start": 11, + "end": 11, + "column": { + "start": 9, + "end": 74 + }, + "content": "ioutil.WriteFile(\\"/tmp/demo2\\", []byte(\\"This is some data\\"), 0644)" + }, + "parent_line_number": 11, + "snippet": "ioutil.WriteFile(\\"/tmp/demo2\\", []byte(\\"This is some data\\"), 0644)", + "fingerprint": "6ecf2eddbc7c43a01ece9577a34e9e61_0", + "old_fingerprint": "e8eb39c1772c573d1ca552003803ccbb_0", + "code_extract": "\\terr := ioutil.WriteFile(\\"/tmp/demo2\\", []byte(\\"This is some data\\"), 0644)" + }, + { + "cwe_ids": [ + "378" + ], + "id": "go_gosec_filesystem_tempfile", + "title": "Incorrect default permissions", + "description": "## Description\\n\\nThe application has been observed creating files in shared system temporary directories, such as \`/tmp\` or \`/var/tmp\`, without the use of secure functions like \`os.CreateTemp\`. This practice is insecure because it opens up the possibility of symlink attacks, where an attacker could anticipate the temporary file name and create a symlink to a target file, leading to unauthorized file creation or overwriting when the application writes to what it believes is a temporary file.\\n\\n## Remediations\\n\\nTo prevent symlink attacks and other vulnerabilities associated with the use of shared temporary directories:\\n\\n✅ Use Secure Temporary File Creation\\n\\nImplement \`os.CreateTemp\` to safely create temporary files within a directory that's restricted to the application. This reduces the risk of symlink attacks and ensures that temporary files are handled securely.\\n\\n\`\`\`go\\nimport (\\n \\"os\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n // Ensure the application-restricted directory exists with appropriate permissions\\n restrictedDir := \\"/opt/appdir/restricted\\"\\n if err := os.MkdirAll(restrictedDir, 0700); err != nil {\\n log.Fatalf(\\"failed to create restricted directory: %s\\", err)\\n }\\n\\n // Securely create a temporary file within the restricted directory\\n f, err := os.CreateTemp(restrictedDir, \\"temp-*.txt\\")\\n if err != nil {\\n log.Fatalf(\\"failed to create temporary file: %s\\", err)\\n }\\n defer f.Close() // Ensure the file is closed when no longer needed\\n\\n defer os.Remove(f.Name()) // Clean up the file upon exit\\n // Continue working with the temporary file\\n}\\n\`\`\`\\n\\n✅ Avoid Shared Temporary Directories for Sensitive Operations\\n\\nDo not use common temporary directories for storing sensitive information or for operations that require secure handling of files.\\n\\n✅ Clean Up After Use\\n\\nAlways remove temporary files after their use to prevent accumulation and potential misuse.\\n\\n## Resources\\n\\n- [Go Documentation: os.CreateTemp](https://pkg.go.dev/os#CreateTemp)\\n- [Secure Coding Guidelines for File I/O](https://wiki.sei.cmu.edu/confluence/display/seccode/TOCTOU+Race+Conditions)\\n- [OWASP Guide to File System Security](https://owasp.org/www-community/controls/Path_Traversal)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_tempfile", + "line_number": 18, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 18, + "end": 18, + "column": { + "start": 9, + "end": 70 + } + }, + "sink": { + "start": 18, + "end": 18, + "column": { + "start": 9, + "end": 70 + }, + "content": "os.WriteFile(\\"/tmp/demo2\\", []byte(\\"This is some data\\"), 0644)" + }, + "parent_line_number": 18, + "snippet": "os.WriteFile(\\"/tmp/demo2\\", []byte(\\"This is some data\\"), 0644)", + "fingerprint": "6ecf2eddbc7c43a01ece9577a34e9e61_1", + "old_fingerprint": "e8eb39c1772c573d1ca552003803ccbb_1", + "code_extract": "\\terr := os.WriteFile(\\"/tmp/demo2\\", []byte(\\"This is some data\\"), 0644)" + }, + { + "cwe_ids": [ + "378" + ], + "id": "go_gosec_filesystem_tempfile", + "title": "Incorrect default permissions", + "description": "## Description\\n\\nThe application has been observed creating files in shared system temporary directories, such as \`/tmp\` or \`/var/tmp\`, without the use of secure functions like \`os.CreateTemp\`. This practice is insecure because it opens up the possibility of symlink attacks, where an attacker could anticipate the temporary file name and create a symlink to a target file, leading to unauthorized file creation or overwriting when the application writes to what it believes is a temporary file.\\n\\n## Remediations\\n\\nTo prevent symlink attacks and other vulnerabilities associated with the use of shared temporary directories:\\n\\n✅ Use Secure Temporary File Creation\\n\\nImplement \`os.CreateTemp\` to safely create temporary files within a directory that's restricted to the application. This reduces the risk of symlink attacks and ensures that temporary files are handled securely.\\n\\n\`\`\`go\\nimport (\\n \\"os\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n // Ensure the application-restricted directory exists with appropriate permissions\\n restrictedDir := \\"/opt/appdir/restricted\\"\\n if err := os.MkdirAll(restrictedDir, 0700); err != nil {\\n log.Fatalf(\\"failed to create restricted directory: %s\\", err)\\n }\\n\\n // Securely create a temporary file within the restricted directory\\n f, err := os.CreateTemp(restrictedDir, \\"temp-*.txt\\")\\n if err != nil {\\n log.Fatalf(\\"failed to create temporary file: %s\\", err)\\n }\\n defer f.Close() // Ensure the file is closed when no longer needed\\n\\n defer os.Remove(f.Name()) // Clean up the file upon exit\\n // Continue working with the temporary file\\n}\\n\`\`\`\\n\\n✅ Avoid Shared Temporary Directories for Sensitive Operations\\n\\nDo not use common temporary directories for storing sensitive information or for operations that require secure handling of files.\\n\\n✅ Clean Up After Use\\n\\nAlways remove temporary files after their use to prevent accumulation and potential misuse.\\n\\n## Resources\\n\\n- [Go Documentation: os.CreateTemp](https://pkg.go.dev/os#CreateTemp)\\n- [Secure Coding Guidelines for File I/O](https://wiki.sei.cmu.edu/confluence/display/seccode/TOCTOU+Race+Conditions)\\n- [OWASP Guide to File System Security](https://owasp.org/www-community/controls/Path_Traversal)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_tempfile", + "line_number": 26, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 26, + "end": 26, + "column": { + "start": 9, + "end": 62 + } + }, + "sink": { + "start": 26, + "end": 26, + "column": { + "start": 9, + "end": 62 + }, + "content": "os.WriteFile(path, []byte(\\"This is some data\\"), 0644)" + }, + "parent_line_number": 26, + "snippet": "os.WriteFile(path, []byte(\\"This is some data\\"), 0644)", + "fingerprint": "6ecf2eddbc7c43a01ece9577a34e9e61_2", + "old_fingerprint": "e8eb39c1772c573d1ca552003803ccbb_2", + "code_extract": "\\terr := os.WriteFile(path, []byte(\\"This is some data\\"), 0644)" + } + ] +}" +`; diff --git a/tests/go/gosec/filesystem/tempfile/test.js b/tests/go/gosec/filesystem/tempfile/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/filesystem/tempfile/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/filesystem/tempfile/testdata/main.go b/tests/go/gosec/filesystem/tempfile/testdata/main.go new file mode 100644 index 000000000..1f83a455a --- /dev/null +++ b/tests/go/gosec/filesystem/tempfile/testdata/main.go @@ -0,0 +1,44 @@ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "os" +) + +func foo1() { + err := ioutil.WriteFile("/tmp/demo2", []byte("This is some data"), 0644) + if err != nil { + fmt.Println("Error while writing!") + } +} + +func foo2() { + err := os.WriteFile("/tmp/demo2", []byte("This is some data"), 0644) + if err != nil { + fmt.Println("Error while writing!") + } +} + +func foo3() { + path := "/tmp/demo" + err := os.WriteFile(path, []byte("This is some data"), 0644) + if err != nil { + fmt.Println("Error while writing!") + } +} + +func foo4() { + if err := os.MkdirAll("/opt/appdir/restricted", 0700); err != nil { + log.Fatal(err) + } + + f, err := os.CreateTemp("/opt/appdir/restricted", "temp-*.txt") + if err != nil { + log.Fatal(err) + } + + defer f.Close() + defer os.Remove(f.Name()) +} diff --git a/tests/go/gosec/filesystem/ziparchive/__snapshots__/test.js.snap b/tests/go/gosec/filesystem/ziparchive/__snapshots__/test.js.snap new file mode 100644 index 000000000..11d76cfea --- /dev/null +++ b/tests/go/gosec/filesystem/ziparchive/__snapshots__/test.js.snap @@ -0,0 +1,110 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_filesystem_ziparchive test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "22" + ], + "id": "go_gosec_filesystem_ziparchive", + "title": "Improper limitation of a pathname to a restricted directory ('Path Traversal')", + "description": "## Description\\n\\nThe application is at risk of a path traversal vulnerability, commonly known as 'Zip Slip', when extracting files from untrusted archives. Maliciously crafted archive files can contain relative path specifications that lead to writing files outside the intended directory when extracted, potentially overwriting system files or placing unauthorized files in critical paths.\\n\\n## Remediations\\n\\nTo safeguard against 'Zip Slip' and related exploitation techniques, follow these security practices:\\n\\n✅ Limit Archive Size\\n\\nImplement checks to ensure the zip archive's size does not exceed a maximum threshold, preventing 'Zip Bombs'—archives that decompress to disproportionately large sizes.\\n\\n✅ Generate Unique Filenames\\n\\nAvoid using the original filenames from the archive. If necessary, use only the base name after sanitizing or, better yet, generate a unique name to prevent intentional overwrites.\\n\\n✅ Validate Extraction Paths\\n\\nConfirm that extracted files are written to a specified, trusted directory and do not traverse outside of this directory.\\n\\n✅ Disallow Symbolic Links\\n\\nOnly process regular files. Exclude symbolic links to prevent indirect file read/write vulnerabilities.\\n\\n\`\`\`go\\nimport (\\n \\"archive/zip\\"\\n \\"io\\"\\n \\"log\\"\\n \\"os\\"\\n \\"path/filepath\\"\\n \\"strings\\"\\n)\\n\\nfunc main() {\\n // Open the zip file for reading\\n r, err := zip.OpenReader(\\"trusted.zip\\")\\n if err != nil {\\n log.Fatal(err)\\n }\\n defer r.Close()\\n\\n // Set up restrictions and base path\\n const (\\n expectedFileCount = 10\\n totalAllowedSize = 10 * 1024 * 1024 // 10MB\\n maxFileSize = 1024 * 1024 // 1MB\\n basePath = \\"/var/restricted/\\"\\n )\\n\\n // Calculate total size of uncompressed files\\n var totalSize uint64\\n for _, f := range r.File {\\n totalSize += f.UncompressedSize64\\n }\\n\\n // Check if total size exceeds the limit\\n if totalSize > totalAllowedSize {\\n log.Fatalf(\\"archive exceeds total allowed size: %d\\\\n\\", totalSize)\\n }\\n\\n // Process files in the archive\\n for _, f := range r.File {\\n // Skip overlarge files\\n if f.UncompressedSize64 > maxFileSize {\\n log.Printf(\\"skipping file as it exceeds maxFileSize: %s\\\\n\\", f.Name)\\n continue\\n }\\n\\n // Skip if not a regular file\\n if !f.Mode().IsRegular() {\\n log.Printf(\\"skipping non-regular file: %s\\\\n\\", f.Name)\\n continue\\n }\\n\\n // Securely resolve the file path\\n name := filepath.Base(f.Name)\\n resolvedPath, err := filepath.Join(basePath, name)\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Ensure the file does not traverse outside the base path\\n if !strings.HasPrefix(resolvedPath, basePath) {\\n log.Fatal(\\"path does not start with basePath\\")\\n }\\n\\n // Extract and process the file (omitted for brevity)\\n }\\n}\\n\`\`\`\\n\\nFor processing directories within the zip archive, ensure to clean the path and validate it strictly against the base path.\\n\\n## Resources\\n\\n- [OWASP Cheat Sheet: Zip Slip Vulnerability](https://cheatsheetseries.owasp.org/cheatsheets/Path_Traversal_Cheat_Sheet.html)\\n- [Go Documentation: archive/zip package](https://pkg.go.dev/archive/zip)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_ziparchive", + "line_number": 27, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 27, + "end": 27, + "column": { + "start": 11, + "end": 43 + } + }, + "sink": { + "start": 27, + "end": 27, + "column": { + "start": 11, + "end": 43 + }, + "content": "filepath.Join(target, file.Name)" + }, + "parent_line_number": 27, + "snippet": "filepath.Join(target, file.Name)", + "fingerprint": "704dc7b283b24c0deaaced0816ea502b_0", + "old_fingerprint": "1c766cd72bac736b6813d91ff99016c0_0", + "code_extract": "\\t\\tpath := filepath.Join(target, file.Name)" + }, + { + "cwe_ids": [ + "22" + ], + "id": "go_gosec_filesystem_ziparchive", + "title": "Improper limitation of a pathname to a restricted directory ('Path Traversal')", + "description": "## Description\\n\\nThe application is at risk of a path traversal vulnerability, commonly known as 'Zip Slip', when extracting files from untrusted archives. Maliciously crafted archive files can contain relative path specifications that lead to writing files outside the intended directory when extracted, potentially overwriting system files or placing unauthorized files in critical paths.\\n\\n## Remediations\\n\\nTo safeguard against 'Zip Slip' and related exploitation techniques, follow these security practices:\\n\\n✅ Limit Archive Size\\n\\nImplement checks to ensure the zip archive's size does not exceed a maximum threshold, preventing 'Zip Bombs'—archives that decompress to disproportionately large sizes.\\n\\n✅ Generate Unique Filenames\\n\\nAvoid using the original filenames from the archive. If necessary, use only the base name after sanitizing or, better yet, generate a unique name to prevent intentional overwrites.\\n\\n✅ Validate Extraction Paths\\n\\nConfirm that extracted files are written to a specified, trusted directory and do not traverse outside of this directory.\\n\\n✅ Disallow Symbolic Links\\n\\nOnly process regular files. Exclude symbolic links to prevent indirect file read/write vulnerabilities.\\n\\n\`\`\`go\\nimport (\\n \\"archive/zip\\"\\n \\"io\\"\\n \\"log\\"\\n \\"os\\"\\n \\"path/filepath\\"\\n \\"strings\\"\\n)\\n\\nfunc main() {\\n // Open the zip file for reading\\n r, err := zip.OpenReader(\\"trusted.zip\\")\\n if err != nil {\\n log.Fatal(err)\\n }\\n defer r.Close()\\n\\n // Set up restrictions and base path\\n const (\\n expectedFileCount = 10\\n totalAllowedSize = 10 * 1024 * 1024 // 10MB\\n maxFileSize = 1024 * 1024 // 1MB\\n basePath = \\"/var/restricted/\\"\\n )\\n\\n // Calculate total size of uncompressed files\\n var totalSize uint64\\n for _, f := range r.File {\\n totalSize += f.UncompressedSize64\\n }\\n\\n // Check if total size exceeds the limit\\n if totalSize > totalAllowedSize {\\n log.Fatalf(\\"archive exceeds total allowed size: %d\\\\n\\", totalSize)\\n }\\n\\n // Process files in the archive\\n for _, f := range r.File {\\n // Skip overlarge files\\n if f.UncompressedSize64 > maxFileSize {\\n log.Printf(\\"skipping file as it exceeds maxFileSize: %s\\\\n\\", f.Name)\\n continue\\n }\\n\\n // Skip if not a regular file\\n if !f.Mode().IsRegular() {\\n log.Printf(\\"skipping non-regular file: %s\\\\n\\", f.Name)\\n continue\\n }\\n\\n // Securely resolve the file path\\n name := filepath.Base(f.Name)\\n resolvedPath, err := filepath.Join(basePath, name)\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Ensure the file does not traverse outside the base path\\n if !strings.HasPrefix(resolvedPath, basePath) {\\n log.Fatal(\\"path does not start with basePath\\")\\n }\\n\\n // Extract and process the file (omitted for brevity)\\n }\\n}\\n\`\`\`\\n\\nFor processing directories within the zip archive, ensure to clean the path and validate it strictly against the base path.\\n\\n## Resources\\n\\n- [OWASP Cheat Sheet: Zip Slip Vulnerability](https://cheatsheetseries.owasp.org/cheatsheets/Path_Traversal_Cheat_Sheet.html)\\n- [Go Documentation: archive/zip package](https://pkg.go.dev/archive/zip)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_ziparchive", + "line_number": 65, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 65, + "end": 65, + "column": { + "start": 11, + "end": 45 + } + }, + "sink": { + "start": 65, + "end": 65, + "column": { + "start": 11, + "end": 45 + }, + "content": "filepath.Join(target, archiveFile)" + }, + "parent_line_number": 65, + "snippet": "filepath.Join(target, archiveFile)", + "fingerprint": "704dc7b283b24c0deaaced0816ea502b_1", + "old_fingerprint": "1c766cd72bac736b6813d91ff99016c0_1", + "code_extract": "\\t\\tpath := filepath.Join(target, archiveFile)" + }, + { + "cwe_ids": [ + "22" + ], + "id": "go_gosec_filesystem_ziparchive", + "title": "Improper limitation of a pathname to a restricted directory ('Path Traversal')", + "description": "## Description\\n\\nThe application is at risk of a path traversal vulnerability, commonly known as 'Zip Slip', when extracting files from untrusted archives. Maliciously crafted archive files can contain relative path specifications that lead to writing files outside the intended directory when extracted, potentially overwriting system files or placing unauthorized files in critical paths.\\n\\n## Remediations\\n\\nTo safeguard against 'Zip Slip' and related exploitation techniques, follow these security practices:\\n\\n✅ Limit Archive Size\\n\\nImplement checks to ensure the zip archive's size does not exceed a maximum threshold, preventing 'Zip Bombs'—archives that decompress to disproportionately large sizes.\\n\\n✅ Generate Unique Filenames\\n\\nAvoid using the original filenames from the archive. If necessary, use only the base name after sanitizing or, better yet, generate a unique name to prevent intentional overwrites.\\n\\n✅ Validate Extraction Paths\\n\\nConfirm that extracted files are written to a specified, trusted directory and do not traverse outside of this directory.\\n\\n✅ Disallow Symbolic Links\\n\\nOnly process regular files. Exclude symbolic links to prevent indirect file read/write vulnerabilities.\\n\\n\`\`\`go\\nimport (\\n \\"archive/zip\\"\\n \\"io\\"\\n \\"log\\"\\n \\"os\\"\\n \\"path/filepath\\"\\n \\"strings\\"\\n)\\n\\nfunc main() {\\n // Open the zip file for reading\\n r, err := zip.OpenReader(\\"trusted.zip\\")\\n if err != nil {\\n log.Fatal(err)\\n }\\n defer r.Close()\\n\\n // Set up restrictions and base path\\n const (\\n expectedFileCount = 10\\n totalAllowedSize = 10 * 1024 * 1024 // 10MB\\n maxFileSize = 1024 * 1024 // 1MB\\n basePath = \\"/var/restricted/\\"\\n )\\n\\n // Calculate total size of uncompressed files\\n var totalSize uint64\\n for _, f := range r.File {\\n totalSize += f.UncompressedSize64\\n }\\n\\n // Check if total size exceeds the limit\\n if totalSize > totalAllowedSize {\\n log.Fatalf(\\"archive exceeds total allowed size: %d\\\\n\\", totalSize)\\n }\\n\\n // Process files in the archive\\n for _, f := range r.File {\\n // Skip overlarge files\\n if f.UncompressedSize64 > maxFileSize {\\n log.Printf(\\"skipping file as it exceeds maxFileSize: %s\\\\n\\", f.Name)\\n continue\\n }\\n\\n // Skip if not a regular file\\n if !f.Mode().IsRegular() {\\n log.Printf(\\"skipping non-regular file: %s\\\\n\\", f.Name)\\n continue\\n }\\n\\n // Securely resolve the file path\\n name := filepath.Base(f.Name)\\n resolvedPath, err := filepath.Join(basePath, name)\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Ensure the file does not traverse outside the base path\\n if !strings.HasPrefix(resolvedPath, basePath) {\\n log.Fatal(\\"path does not start with basePath\\")\\n }\\n\\n // Extract and process the file (omitted for brevity)\\n }\\n}\\n\`\`\`\\n\\nFor processing directories within the zip archive, ensure to clean the path and validate it strictly against the base path.\\n\\n## Resources\\n\\n- [OWASP Cheat Sheet: Zip Slip Vulnerability](https://cheatsheetseries.owasp.org/cheatsheets/Path_Traversal_Cheat_Sheet.html)\\n- [Go Documentation: archive/zip package](https://pkg.go.dev/archive/zip)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_filesystem_ziparchive", + "line_number": 92, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 92, + "end": 92, + "column": { + "start": 14, + "end": 45 + } + }, + "sink": { + "start": 92, + "end": 92, + "column": { + "start": 14, + "end": 45 + }, + "content": "filepath.Join(destPath, f.Name)" + }, + "parent_line_number": 92, + "snippet": "filepath.Join(destPath, f.Name)", + "fingerprint": "704dc7b283b24c0deaaced0816ea502b_2", + "old_fingerprint": "1c766cd72bac736b6813d91ff99016c0_2", + "code_extract": "\\tfilePath := filepath.Join(destPath, f.Name)" + } + ] +}" +`; diff --git a/tests/go/gosec/filesystem/ziparchive/test.js b/tests/go/gosec/filesystem/ziparchive/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/filesystem/ziparchive/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/filesystem/ziparchive/testdata/main.go b/tests/go/gosec/filesystem/ziparchive/testdata/main.go new file mode 100644 index 000000000..9ab24c034 --- /dev/null +++ b/tests/go/gosec/filesystem/ziparchive/testdata/main.go @@ -0,0 +1,139 @@ +package main + +import ( + "archive/tar" + "archive/zip" + "io" + "os" + "path" + "path/filepath" +) + +func unzip(archive, target string) error { + reader, err := zip.OpenReader(archive) + if err != nil { + return err + } + + if err := os.MkdirAll(target, 0750); err != nil { + return err + } + + for _, file := range reader.File { + path := filepath.Join(target, file.Name) + if file.FileInfo().IsDir() { + os.MkdirAll(path, file.Mode()) // #nosec + continue + } + + fileReader, err := file.Open() + if err != nil { + return err + } + defer fileReader.Close() + + targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode()) + if err != nil { + return err + } + defer targetFile.Close() + + if _, err := io.Copy(targetFile, fileReader); err != nil { + return err + } + } + + return nil +} + +func unzipIndirectFilename(archive, target string) error { + reader, err := zip.OpenReader(archive) + if err != nil { + return err + } + + if err := os.MkdirAll(target, 0750); err != nil { + return err + } + + for _, file := range reader.File { + archiveFile := file.Name + path := filepath.Join(target, archiveFile) + if file.FileInfo().IsDir() { + os.MkdirAll(path, file.Mode()) // #nosec + continue + } + + fileReader, err := file.Open() + if err != nil { + return err + } + defer fileReader.Close() + + targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode()) + if err != nil { + return err + } + defer targetFile.Close() + + if _, err := io.Copy(targetFile, fileReader); err != nil { + return err + } + } + + return nil +} + +func extractFile(f *zip.File, destPath string) error { + filePath := filepath.Join(destPath, f.Name) + os.MkdirAll(path.Dir(filePath), os.ModePerm) + + rc, err := f.Open() + if err != nil { + return err + } + defer rc.Close() + + fw, err := os.Create(filePath) + if err != nil { + return err + } + defer fw.Close() + + if _, err = io.Copy(fw, rc); err != nil { + return err + } + + if f.FileInfo().Mode()&os.ModeSymlink != 0 { + return nil + } + + if err = os.Chtimes(filePath, f.ModTime(), f.ModTime()); err != nil { + return err + } + return os.Chmod(filePath, f.FileInfo().Mode()) +} + +func extractFileHeader(f *tar.Header, tr *tar.Reader, destPath string) error { + filePath := path.Join(destPath, f.Name) + os.MkdirAll(path.Dir(filePath), os.ModePerm) + + fw, err := os.Create(filePath) + if err != nil { + return err + } + defer fw.Close() + + if _, err = io.Copy(fw, tr); err != nil { + return err + } + + if f.FileInfo().Mode()&os.ModeSymlink != 0 { + return nil + } + + if err = os.Chtimes(filePath, f.FileInfo().ModTime(), f.FileInfo().ModTime()); err != nil { + return err + } + return os.Chmod(filePath, f.FileInfo().Mode()) +} diff --git a/tests/go/gosec/http/http_serve/__snapshots__/test.js.snap b/tests/go/gosec/http/http_serve/__snapshots__/test.js.snap new file mode 100644 index 000000000..2872a587e --- /dev/null +++ b/tests/go/gosec/http/http_serve/__snapshots__/test.js.snap @@ -0,0 +1,144 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_http_http_serve test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "400" + ], + "id": "go_gosec_http_http_serve", + "title": "Uncontrolled resource consumption", + "description": "## Description\\n\\nThe \`net/http\` serve functions in Go, when used with default settings, are vulnerable to resource consumption attacks. Attackers can exploit this by creating numerous connections to the server, intentionally not completing data transfers or leaving connections open, which can exhaust the server's resources and prevent it from accepting new legitimate connections.\\n\\n## Remediations\\n\\nTo mitigate such attacks, specific server configurations are necessary:\\n\\n❌ Avoid Default Serve Functions for Production\\n\\nFunctions like \`http.ListenAndServe\` and \`http.Serve\` should not be used in a production setting as they do not allow for timeout configurations.\\n\\n✅ Configure Timeouts on Custom \`http.Server\` Object\\n\\nCreate a custom \`http.Server\` object and set appropriate timeouts to prevent resource exhaustion.\\n\\n\`\`\`go\\nimport (\\n \\"net/http\\"\\n \\"time\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n srv := &http.Server{\\n Addr: \\"localhost:8000\\",\\n ReadHeaderTimeout: 15 * time.Second,\\n ReadTimeout: 15 * time.Second,\\n WriteTimeout: 10 * time.Second,\\n IdleTimeout: 30 * time.Second,\\n }\\n\\n if err := srv.ListenAndServe(); err != nil {\\n log.Fatal(err)\\n }\\n}\\n\`\`\`\\n\\n✅ Use \`http.TimeoutHandler\` for Per Request Timeouts\\n\\nTo set timeouts for individual requests, use the \`http.TimeoutHandler\` wrapper on your handlers. This ensures that the server does not wait indefinitely for a request to complete.\\n\\n## Resources\\n\\n- [http.Server Timeouts Documentation](https://pkg.go.dev/net/http#Server)\\n- [Guide to Setting Request-Based Timeouts](https://pkg.go.dev/net/http#TimeoutHandler)\\n- [Understanding the Slowloris Attack](https://en.wikipedia.org/wiki/Slowloris_(computer_security))\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_http_http_serve", + "line_number": 16, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 16, + "end": 16, + "column": { + "start": 9, + "end": 42 + } + }, + "sink": { + "start": 16, + "end": 16, + "column": { + "start": 9, + "end": 42 + }, + "content": "http.ListenAndServe(\\":8443\\", nil)" + }, + "parent_line_number": 16, + "snippet": "http.ListenAndServe(\\":8443\\", nil)", + "fingerprint": "6f179bc162124459b0090fb499c31d43_0", + "old_fingerprint": "719e09cb6b2922d8ae44e9968c9c756f_0", + "code_extract": "\\terr := http.ListenAndServe(\\":8443\\", nil)" + }, + { + "cwe_ids": [ + "400" + ], + "id": "go_gosec_http_http_serve", + "title": "Uncontrolled resource consumption", + "description": "## Description\\n\\nThe \`net/http\` serve functions in Go, when used with default settings, are vulnerable to resource consumption attacks. Attackers can exploit this by creating numerous connections to the server, intentionally not completing data transfers or leaving connections open, which can exhaust the server's resources and prevent it from accepting new legitimate connections.\\n\\n## Remediations\\n\\nTo mitigate such attacks, specific server configurations are necessary:\\n\\n❌ Avoid Default Serve Functions for Production\\n\\nFunctions like \`http.ListenAndServe\` and \`http.Serve\` should not be used in a production setting as they do not allow for timeout configurations.\\n\\n✅ Configure Timeouts on Custom \`http.Server\` Object\\n\\nCreate a custom \`http.Server\` object and set appropriate timeouts to prevent resource exhaustion.\\n\\n\`\`\`go\\nimport (\\n \\"net/http\\"\\n \\"time\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n srv := &http.Server{\\n Addr: \\"localhost:8000\\",\\n ReadHeaderTimeout: 15 * time.Second,\\n ReadTimeout: 15 * time.Second,\\n WriteTimeout: 10 * time.Second,\\n IdleTimeout: 30 * time.Second,\\n }\\n\\n if err := srv.ListenAndServe(); err != nil {\\n log.Fatal(err)\\n }\\n}\\n\`\`\`\\n\\n✅ Use \`http.TimeoutHandler\` for Per Request Timeouts\\n\\nTo set timeouts for individual requests, use the \`http.TimeoutHandler\` wrapper on your handlers. This ensures that the server does not wait indefinitely for a request to complete.\\n\\n## Resources\\n\\n- [http.Server Timeouts Documentation](https://pkg.go.dev/net/http#Server)\\n- [Guide to Setting Request-Based Timeouts](https://pkg.go.dev/net/http#TimeoutHandler)\\n- [Understanding the Slowloris Attack](https://en.wikipedia.org/wiki/Slowloris_(computer_security))\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_http_http_serve", + "line_number": 21, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 21, + "end": 21, + "column": { + "start": 9, + "end": 68 + } + }, + "sink": { + "start": 21, + "end": 21, + "column": { + "start": 9, + "end": 68 + }, + "content": "http.ListenAndServeTLS(\\":8443\\", \\"cert.pem\\", \\"key.pem\\", nil)" + }, + "parent_line_number": 21, + "snippet": "http.ListenAndServeTLS(\\":8443\\", \\"cert.pem\\", \\"key.pem\\", nil)", + "fingerprint": "6f179bc162124459b0090fb499c31d43_1", + "old_fingerprint": "719e09cb6b2922d8ae44e9968c9c756f_1", + "code_extract": "\\terr := http.ListenAndServeTLS(\\":8443\\", \\"cert.pem\\", \\"key.pem\\", nil)" + }, + { + "cwe_ids": [ + "400" + ], + "id": "go_gosec_http_http_serve", + "title": "Uncontrolled resource consumption", + "description": "## Description\\n\\nThe \`net/http\` serve functions in Go, when used with default settings, are vulnerable to resource consumption attacks. Attackers can exploit this by creating numerous connections to the server, intentionally not completing data transfers or leaving connections open, which can exhaust the server's resources and prevent it from accepting new legitimate connections.\\n\\n## Remediations\\n\\nTo mitigate such attacks, specific server configurations are necessary:\\n\\n❌ Avoid Default Serve Functions for Production\\n\\nFunctions like \`http.ListenAndServe\` and \`http.Serve\` should not be used in a production setting as they do not allow for timeout configurations.\\n\\n✅ Configure Timeouts on Custom \`http.Server\` Object\\n\\nCreate a custom \`http.Server\` object and set appropriate timeouts to prevent resource exhaustion.\\n\\n\`\`\`go\\nimport (\\n \\"net/http\\"\\n \\"time\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n srv := &http.Server{\\n Addr: \\"localhost:8000\\",\\n ReadHeaderTimeout: 15 * time.Second,\\n ReadTimeout: 15 * time.Second,\\n WriteTimeout: 10 * time.Second,\\n IdleTimeout: 30 * time.Second,\\n }\\n\\n if err := srv.ListenAndServe(); err != nil {\\n log.Fatal(err)\\n }\\n}\\n\`\`\`\\n\\n✅ Use \`http.TimeoutHandler\` for Per Request Timeouts\\n\\nTo set timeouts for individual requests, use the \`http.TimeoutHandler\` wrapper on your handlers. This ensures that the server does not wait indefinitely for a request to complete.\\n\\n## Resources\\n\\n- [http.Server Timeouts Documentation](https://pkg.go.dev/net/http#Server)\\n- [Guide to Setting Request-Based Timeouts](https://pkg.go.dev/net/http#TimeoutHandler)\\n- [Understanding the Slowloris Attack](https://en.wikipedia.org/wiki/Slowloris_(computer_security))\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_http_http_serve", + "line_number": 31, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 31, + "end": 31, + "column": { + "start": 8, + "end": 26 + } + }, + "sink": { + "start": 31, + "end": 31, + "column": { + "start": 8, + "end": 26 + }, + "content": "http.Serve(l, nil)" + }, + "parent_line_number": 31, + "snippet": "http.Serve(l, nil)", + "fingerprint": "6f179bc162124459b0090fb499c31d43_2", + "old_fingerprint": "719e09cb6b2922d8ae44e9968c9c756f_2", + "code_extract": "\\terr = http.Serve(l, nil)" + }, + { + "cwe_ids": [ + "400" + ], + "id": "go_gosec_http_http_serve", + "title": "Uncontrolled resource consumption", + "description": "## Description\\n\\nThe \`net/http\` serve functions in Go, when used with default settings, are vulnerable to resource consumption attacks. Attackers can exploit this by creating numerous connections to the server, intentionally not completing data transfers or leaving connections open, which can exhaust the server's resources and prevent it from accepting new legitimate connections.\\n\\n## Remediations\\n\\nTo mitigate such attacks, specific server configurations are necessary:\\n\\n❌ Avoid Default Serve Functions for Production\\n\\nFunctions like \`http.ListenAndServe\` and \`http.Serve\` should not be used in a production setting as they do not allow for timeout configurations.\\n\\n✅ Configure Timeouts on Custom \`http.Server\` Object\\n\\nCreate a custom \`http.Server\` object and set appropriate timeouts to prevent resource exhaustion.\\n\\n\`\`\`go\\nimport (\\n \\"net/http\\"\\n \\"time\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n srv := &http.Server{\\n Addr: \\"localhost:8000\\",\\n ReadHeaderTimeout: 15 * time.Second,\\n ReadTimeout: 15 * time.Second,\\n WriteTimeout: 10 * time.Second,\\n IdleTimeout: 30 * time.Second,\\n }\\n\\n if err := srv.ListenAndServe(); err != nil {\\n log.Fatal(err)\\n }\\n}\\n\`\`\`\\n\\n✅ Use \`http.TimeoutHandler\` for Per Request Timeouts\\n\\nTo set timeouts for individual requests, use the \`http.TimeoutHandler\` wrapper on your handlers. This ensures that the server does not wait indefinitely for a request to complete.\\n\\n## Resources\\n\\n- [http.Server Timeouts Documentation](https://pkg.go.dev/net/http#Server)\\n- [Guide to Setting Request-Based Timeouts](https://pkg.go.dev/net/http#TimeoutHandler)\\n- [Understanding the Slowloris Attack](https://en.wikipedia.org/wiki/Slowloris_(computer_security))\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_http_http_serve", + "line_number": 41, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 41, + "end": 41, + "column": { + "start": 8, + "end": 52 + } + }, + "sink": { + "start": 41, + "end": 41, + "column": { + "start": 8, + "end": 52 + }, + "content": "http.ServeTLS(l, nil, \\"cert.pem\\", \\"key.pem\\")" + }, + "parent_line_number": 41, + "snippet": "http.ServeTLS(l, nil, \\"cert.pem\\", \\"key.pem\\")", + "fingerprint": "6f179bc162124459b0090fb499c31d43_3", + "old_fingerprint": "719e09cb6b2922d8ae44e9968c9c756f_3", + "code_extract": "\\terr = http.ServeTLS(l, nil, \\"cert.pem\\", \\"key.pem\\")" + } + ] +}" +`; diff --git a/tests/go/gosec/http/http_serve/test.js b/tests/go/gosec/http/http_serve/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/http/http_serve/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/http/http_serve/testdata/main.go b/tests/go/gosec/http/http_serve/testdata/main.go new file mode 100644 index 000000000..ec099cb1d --- /dev/null +++ b/tests/go/gosec/http/http_serve/testdata/main.go @@ -0,0 +1,80 @@ +package main + +import ( + "fmt" + "log" + "net" + "net/http" + "time" +) + +func bad() { + err := http.ListenAndServe(":8443", nil) + log.Fatal(err) +} + +func bad2() { + err := http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", nil) + log.Fatal(err) +} + +func bad3() { + l, err := net.Listen("tcp", ":8080") + if err != nil { + log.Fatal(err) + } + defer l.Close() + err = http.Serve(l, nil) + log.Fatal(err) +} + +func bad4() { + l, err := net.Listen("tcp", ":8443") + if err != nil { + log.Fatal(err) + } + defer l.Close() + err = http.ServeTLS(l, nil, "cert.pem", "key.pem") + log.Fatal(err) +} + +func bad5() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) + }) + server := &http.Server{ + Addr: ":1234", + } + err := server.ListenAndServe() + if err != nil { + panic(err) + } +} + +func ok() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) + }) + server := &http.Server{ + Addr: ":1234", + ReadTimeout: 1 * time.Second, + } + err := server.ListenAndServe() + if err != nil { + panic(err) + } +} + +func ok2() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) + }) + server := &http.Server{ + Addr: ":1234", + ReadHeaderTimeout: 1 * time.Second, + } + err := server.ListenAndServe() + if err != nil { + panic(err) + } +} diff --git a/tests/go/gosec/http/http_slowloris/__snapshots__/test.js.snap b/tests/go/gosec/http/http_slowloris/__snapshots__/test.js.snap new file mode 100644 index 000000000..05ab7cd70 --- /dev/null +++ b/tests/go/gosec/http/http_slowloris/__snapshots__/test.js.snap @@ -0,0 +1,110 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_http_http_slowloris test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "400" + ], + "id": "go_gosec_http_http_slowloris", + "title": "Uncontrolled resource consumption (Slowloris)", + "description": "## Description\\n\\nThe server configuration lacks a \`ReadHeaderTimeout\`, making it vulnerable to a Slowloris attack. This type of attack occurs when an attacker opens multiple connections to the server but sends only partial requests. The server keeps each connection open, waiting for the headers to be completed, ultimately leading to resource exhaustion.\\n\\n## Remediations\\n\\nTo protect against such attacks, the following steps should be taken:\\n\\n❌ Avoid Default Serve Functions for Production\\n\\nDo not use \`http.ListenAndServe\` and \`http.Serve\` in a production environment, as they do not support timeout settings.\\n\\n✅ Configure \`http.Server\` with Timeouts\\n\\nEstablish a custom \`http.Server\` instance with appropriate timeouts to prevent attackers from exploiting the lack of \`ReadHeaderTimeout\`.\\n\\n\`\`\`go\\nimport (\\n \\"net/http\\"\\n \\"time\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n srv := &http.Server{\\n Addr: \\"localhost:8000\\",\\n ReadHeaderTimeout: 15 * time.Second,\\n ReadTimeout: 15 * time.Second,\\n WriteTimeout: 10 * time.Second,\\n IdleTimeout: 30 * time.Second,\\n }\\n\\n if err := srv.ListenAndServe(); err != nil {\\n log.Fatal(err)\\n }\\n}\\n\`\`\`\\n\\n✅ Enforce Request Timeouts\\n\\nImplement \`http.TimeoutHandler\` to apply timeouts to individual HTTP handlers, which starts counting down only after the headers have been read.\\n\\n## Resources\\n\\n- [Configuring Timeouts in http.Server](https://pkg.go.dev/net/http#Server)\\n- [How to Set Request-Based Timeouts](https://pkg.go.dev/net/http#TimeoutHandler)\\n- [Understanding Slowloris Attacks](https://en.wikipedia.org/wiki/Slowloris_(computer_security))\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_http_http_slowloris", + "line_number": 33, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 33, + "end": 33, + "column": { + "start": 9, + "end": 32 + } + }, + "sink": { + "start": 33, + "end": 33, + "column": { + "start": 9, + "end": 32 + }, + "content": "server.ListenAndServe()" + }, + "parent_line_number": 33, + "snippet": "server.ListenAndServe()", + "fingerprint": "02c458471df80632680a2bcaf7e424f5_0", + "old_fingerprint": "16ccd1d3f814b5266924a8d012f4d13a_0", + "code_extract": "\\terr := server.ListenAndServe()" + }, + { + "cwe_ids": [ + "400" + ], + "id": "go_gosec_http_http_slowloris", + "title": "Uncontrolled resource consumption (Slowloris)", + "description": "## Description\\n\\nThe server configuration lacks a \`ReadHeaderTimeout\`, making it vulnerable to a Slowloris attack. This type of attack occurs when an attacker opens multiple connections to the server but sends only partial requests. The server keeps each connection open, waiting for the headers to be completed, ultimately leading to resource exhaustion.\\n\\n## Remediations\\n\\nTo protect against such attacks, the following steps should be taken:\\n\\n❌ Avoid Default Serve Functions for Production\\n\\nDo not use \`http.ListenAndServe\` and \`http.Serve\` in a production environment, as they do not support timeout settings.\\n\\n✅ Configure \`http.Server\` with Timeouts\\n\\nEstablish a custom \`http.Server\` instance with appropriate timeouts to prevent attackers from exploiting the lack of \`ReadHeaderTimeout\`.\\n\\n\`\`\`go\\nimport (\\n \\"net/http\\"\\n \\"time\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n srv := &http.Server{\\n Addr: \\"localhost:8000\\",\\n ReadHeaderTimeout: 15 * time.Second,\\n ReadTimeout: 15 * time.Second,\\n WriteTimeout: 10 * time.Second,\\n IdleTimeout: 30 * time.Second,\\n }\\n\\n if err := srv.ListenAndServe(); err != nil {\\n log.Fatal(err)\\n }\\n}\\n\`\`\`\\n\\n✅ Enforce Request Timeouts\\n\\nImplement \`http.TimeoutHandler\` to apply timeouts to individual HTTP handlers, which starts counting down only after the headers have been read.\\n\\n## Resources\\n\\n- [Configuring Timeouts in http.Server](https://pkg.go.dev/net/http#Server)\\n- [How to Set Request-Based Timeouts](https://pkg.go.dev/net/http#TimeoutHandler)\\n- [Understanding Slowloris Attacks](https://en.wikipedia.org/wiki/Slowloris_(computer_security))\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_http_http_slowloris", + "line_number": 47, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 47, + "end": 47, + "column": { + "start": 9, + "end": 32 + } + }, + "sink": { + "start": 47, + "end": 47, + "column": { + "start": 9, + "end": 32 + }, + "content": "server.ListenAndServe()" + }, + "parent_line_number": 47, + "snippet": "server.ListenAndServe()", + "fingerprint": "02c458471df80632680a2bcaf7e424f5_1", + "old_fingerprint": "16ccd1d3f814b5266924a8d012f4d13a_1", + "code_extract": "\\terr := server.ListenAndServe()" + }, + { + "cwe_ids": [ + "400" + ], + "id": "go_gosec_http_http_slowloris", + "title": "Uncontrolled resource consumption (Slowloris)", + "description": "## Description\\n\\nThe server configuration lacks a \`ReadHeaderTimeout\`, making it vulnerable to a Slowloris attack. This type of attack occurs when an attacker opens multiple connections to the server but sends only partial requests. The server keeps each connection open, waiting for the headers to be completed, ultimately leading to resource exhaustion.\\n\\n## Remediations\\n\\nTo protect against such attacks, the following steps should be taken:\\n\\n❌ Avoid Default Serve Functions for Production\\n\\nDo not use \`http.ListenAndServe\` and \`http.Serve\` in a production environment, as they do not support timeout settings.\\n\\n✅ Configure \`http.Server\` with Timeouts\\n\\nEstablish a custom \`http.Server\` instance with appropriate timeouts to prevent attackers from exploiting the lack of \`ReadHeaderTimeout\`.\\n\\n\`\`\`go\\nimport (\\n \\"net/http\\"\\n \\"time\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n srv := &http.Server{\\n Addr: \\"localhost:8000\\",\\n ReadHeaderTimeout: 15 * time.Second,\\n ReadTimeout: 15 * time.Second,\\n WriteTimeout: 10 * time.Second,\\n IdleTimeout: 30 * time.Second,\\n }\\n\\n if err := srv.ListenAndServe(); err != nil {\\n log.Fatal(err)\\n }\\n}\\n\`\`\`\\n\\n✅ Enforce Request Timeouts\\n\\nImplement \`http.TimeoutHandler\` to apply timeouts to individual HTTP handlers, which starts counting down only after the headers have been read.\\n\\n## Resources\\n\\n- [Configuring Timeouts in http.Server](https://pkg.go.dev/net/http#Server)\\n- [How to Set Request-Based Timeouts](https://pkg.go.dev/net/http#TimeoutHandler)\\n- [Understanding Slowloris Attacks](https://en.wikipedia.org/wiki/Slowloris_(computer_security))\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_http_http_slowloris", + "line_number": 62, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 62, + "end": 62, + "column": { + "start": 9, + "end": 32 + } + }, + "sink": { + "start": 62, + "end": 62, + "column": { + "start": 9, + "end": 32 + }, + "content": "server.ListenAndServe()" + }, + "parent_line_number": 62, + "snippet": "server.ListenAndServe()", + "fingerprint": "02c458471df80632680a2bcaf7e424f5_2", + "old_fingerprint": "16ccd1d3f814b5266924a8d012f4d13a_2", + "code_extract": "\\terr := server.ListenAndServe()" + } + ] +}" +`; diff --git a/tests/go/gosec/http/http_slowloris/test.js b/tests/go/gosec/http/http_slowloris/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/http/http_slowloris/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/http/http_slowloris/testdata/main.go b/tests/go/gosec/http/http_slowloris/testdata/main.go new file mode 100644 index 000000000..8d6dc959a --- /dev/null +++ b/tests/go/gosec/http/http_slowloris/testdata/main.go @@ -0,0 +1,62 @@ +package main + +import ( + "fmt" + "net/http" + "time" +) + +func bad() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) + }) + err := (&http.Server{ + Addr: ":1234", + }).ListenAndServe() + if err != nil { + panic(err) + } +} + +func ok() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) + }) + server := &http.Server{ + Addr: ":1234", + ReadHeaderTimeout: 3 * time.Second, + } + err := server.ListenAndServe() + if err != nil { + panic(err) + } +} + +func ok2() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) + }) + server := &http.Server{ + Addr: ":1234", + ReadTimeout: 1 * time.Second, + } + err := server.ListenAndServe() + if err != nil { + panic(err) + } +} + +func ok3() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) + }) + server := &http.Server{ + Addr: ":1234", + } + // FIXME: unsupported for now + server.ReadHeaderTimeout = 1 * time.Second + err := server.ListenAndServe() + if err != nil { + panic(err) + } +} diff --git a/tests/go/gosec/injection/ssrf_injection/__snapshots__/test.js.snap b/tests/go/gosec/injection/ssrf_injection/__snapshots__/test.js.snap new file mode 100644 index 000000000..5ac9ec85c --- /dev/null +++ b/tests/go/gosec/injection/ssrf_injection/__snapshots__/test.js.snap @@ -0,0 +1,144 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_injection_ssrf_injection test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "918" + ], + "id": "go_gosec_injection_ssrf_injection", + "title": "Server Side Request Forgery (SSRF)", + "description": "## Description\\n\\nServer-Side Request Forgery (SSRF) is a security vulnerability that occurs when a server-side application makes HTTP requests to arbitrary URLs controlled by the user. SSRF can be exploited by attackers to target internal systems behind firewalls that are otherwise inaccessible from the external network, by tricking the server into making requests to these systems.\\n\\n## Remediations\\n\\nTo mitigate SSRF vulnerabilities, follow these guidelines:\\n\\n✅ Validate User Input\\n\\nAvoid using direct user input to construct URLs for backend requests. If you must use user input, validate or sanitize it rigorously.\\n\\n✅ Restrict URLs to Known Safe Domains\\n\\nWhere possible, limit requests to a predefined set of safe URLs or domains. This can be done using server-side mapping from user-supplied keys to URLs.\\n\\n✅ Implement IP Safelists and Blocklists\\n\\nUse an HTTP client that allows customizing and blocking specific IP ranges, such as private network addresses and other non-routable IP ranges.\\n\\n✅ Use Network-Level Security\\n\\nIf the HTTP client doesn't support IP range blocking, consider running it with restricted system permissions, or within a secure network where firewall rules can block dangerous addresses.\\n\\n✅ Leverage a Secure HTTP Proxy\\n\\nAs a last resort, route all backend HTTP requests through a secure proxy that can filter out and block requests to potentially harmful addresses.\\n\\n\`\`\`go\\nimport (\\n \\"context\\"\\n \\"crypto/tls\\"\\n \\"errors\\"\\n \\"net\\"\\n \\"net/http\\"\\n \\"time\\"\\n)\\n\\n// IsDisallowedIP checks if an IP address falls within a range of disallowed IPs.\\nfunc IsDisallowedIP(hostIP string) bool {\\n ip := net.ParseIP(hostIP)\\n // Add more checks as necessary\\n return ip.IsMulticast() || ip.IsUnspecified() || ip.IsLoopback() || ip.IsPrivate()\\n}\\n\\n// SafeTransport defines a custom transport that filters out disallowed IP addresses.\\nfunc SafeTransport(timeout time.Duration) *http.Transport {\\n return &http.Transport{\\n DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\\n c, err := net.DialTimeout(network, addr, timeout)\\n if err != nil {\\n return nil, err\\n }\\n ip, _, _ := net.SplitHostPort(c.RemoteAddr().String())\\n if IsDisallowedIP(ip) {\\n c.Close()\\n return nil, errors.New(\\"ip address is not allowed\\")\\n }\\n return c, err\\n },\\n DialTLS: func(network, addr string) (net.Conn, error) {\\n dialer := &net.Dialer{Timeout: timeout}\\n c, err := tls.DialWithDialer(dialer, network, addr, &tls.Config{})\\n if err != nil {\\n return nil, err\\n }\\n ip, _, _ := net.SplitHostPort(c.RemoteAddr().String())\\n if IsDisallowedIP(ip) {\\n c.Close()\\n return nil, errors.New(\\"ip address is not allowed\\")\\n }\\n return c, c.Handshake()\\n },\\n TLSHandshakeTimeout: timeout,\\n }\\n}\\n\\n// httpRequest performs a secure HTTP request, filtering out disallowed IPs.\\nfunc httpRequest(requestUrl string) {\\n const clientConnectTimeout = time.Second * 10\\n httpClient := &http.Client{\\n Transport: SafeTransport(clientConnectTimeout),\\n }\\n resp, err := httpClient.Get(requestUrl)\\n if err != nil {\\n log.Fatal(err)\\n }\\n defer resp.Body.Close()\\n // Process response\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP SSRF Prevention Cheat Sheet](https://owasp.org/www-community/attacks/Server_Side_Request_Forgery)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_injection_ssrf_injection", + "line_number": 23, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 23, + "end": 23, + "column": { + "start": 15, + "end": 28 + } + }, + "sink": { + "start": 23, + "end": 23, + "column": { + "start": 15, + "end": 28 + }, + "content": "http.Get(url)" + }, + "parent_line_number": 23, + "snippet": "http.Get(url)", + "fingerprint": "9be6d64d7285e77ffafc4015fe97ab33_0", + "old_fingerprint": "3062d5fa090e29d011c48ba41511badf_0", + "code_extract": "\\tresp, err := http.Get(url)" + }, + { + "cwe_ids": [ + "918" + ], + "id": "go_gosec_injection_ssrf_injection", + "title": "Server Side Request Forgery (SSRF)", + "description": "## Description\\n\\nServer-Side Request Forgery (SSRF) is a security vulnerability that occurs when a server-side application makes HTTP requests to arbitrary URLs controlled by the user. SSRF can be exploited by attackers to target internal systems behind firewalls that are otherwise inaccessible from the external network, by tricking the server into making requests to these systems.\\n\\n## Remediations\\n\\nTo mitigate SSRF vulnerabilities, follow these guidelines:\\n\\n✅ Validate User Input\\n\\nAvoid using direct user input to construct URLs for backend requests. If you must use user input, validate or sanitize it rigorously.\\n\\n✅ Restrict URLs to Known Safe Domains\\n\\nWhere possible, limit requests to a predefined set of safe URLs or domains. This can be done using server-side mapping from user-supplied keys to URLs.\\n\\n✅ Implement IP Safelists and Blocklists\\n\\nUse an HTTP client that allows customizing and blocking specific IP ranges, such as private network addresses and other non-routable IP ranges.\\n\\n✅ Use Network-Level Security\\n\\nIf the HTTP client doesn't support IP range blocking, consider running it with restricted system permissions, or within a secure network where firewall rules can block dangerous addresses.\\n\\n✅ Leverage a Secure HTTP Proxy\\n\\nAs a last resort, route all backend HTTP requests through a secure proxy that can filter out and block requests to potentially harmful addresses.\\n\\n\`\`\`go\\nimport (\\n \\"context\\"\\n \\"crypto/tls\\"\\n \\"errors\\"\\n \\"net\\"\\n \\"net/http\\"\\n \\"time\\"\\n)\\n\\n// IsDisallowedIP checks if an IP address falls within a range of disallowed IPs.\\nfunc IsDisallowedIP(hostIP string) bool {\\n ip := net.ParseIP(hostIP)\\n // Add more checks as necessary\\n return ip.IsMulticast() || ip.IsUnspecified() || ip.IsLoopback() || ip.IsPrivate()\\n}\\n\\n// SafeTransport defines a custom transport that filters out disallowed IP addresses.\\nfunc SafeTransport(timeout time.Duration) *http.Transport {\\n return &http.Transport{\\n DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\\n c, err := net.DialTimeout(network, addr, timeout)\\n if err != nil {\\n return nil, err\\n }\\n ip, _, _ := net.SplitHostPort(c.RemoteAddr().String())\\n if IsDisallowedIP(ip) {\\n c.Close()\\n return nil, errors.New(\\"ip address is not allowed\\")\\n }\\n return c, err\\n },\\n DialTLS: func(network, addr string) (net.Conn, error) {\\n dialer := &net.Dialer{Timeout: timeout}\\n c, err := tls.DialWithDialer(dialer, network, addr, &tls.Config{})\\n if err != nil {\\n return nil, err\\n }\\n ip, _, _ := net.SplitHostPort(c.RemoteAddr().String())\\n if IsDisallowedIP(ip) {\\n c.Close()\\n return nil, errors.New(\\"ip address is not allowed\\")\\n }\\n return c, c.Handshake()\\n },\\n TLSHandshakeTimeout: timeout,\\n }\\n}\\n\\n// httpRequest performs a secure HTTP request, filtering out disallowed IPs.\\nfunc httpRequest(requestUrl string) {\\n const clientConnectTimeout = time.Second * 10\\n httpClient := &http.Client{\\n Transport: SafeTransport(clientConnectTimeout),\\n }\\n resp, err := httpClient.Get(requestUrl)\\n if err != nil {\\n log.Fatal(err)\\n }\\n defer resp.Body.Close()\\n // Process response\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP SSRF Prevention Cheat Sheet](https://owasp.org/www-community/attacks/Server_Side_Request_Forgery)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_injection_ssrf_injection", + "line_number": 52, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 52, + "end": 52, + "column": { + "start": 15, + "end": 28 + } + }, + "sink": { + "start": 52, + "end": 52, + "column": { + "start": 15, + "end": 28 + }, + "content": "http.Get(url)" + }, + "parent_line_number": 52, + "snippet": "http.Get(url)", + "fingerprint": "9be6d64d7285e77ffafc4015fe97ab33_1", + "old_fingerprint": "3062d5fa090e29d011c48ba41511badf_1", + "code_extract": "\\tresp, err := http.Get(url)" + }, + { + "cwe_ids": [ + "918" + ], + "id": "go_gosec_injection_ssrf_injection", + "title": "Server Side Request Forgery (SSRF)", + "description": "## Description\\n\\nServer-Side Request Forgery (SSRF) is a security vulnerability that occurs when a server-side application makes HTTP requests to arbitrary URLs controlled by the user. SSRF can be exploited by attackers to target internal systems behind firewalls that are otherwise inaccessible from the external network, by tricking the server into making requests to these systems.\\n\\n## Remediations\\n\\nTo mitigate SSRF vulnerabilities, follow these guidelines:\\n\\n✅ Validate User Input\\n\\nAvoid using direct user input to construct URLs for backend requests. If you must use user input, validate or sanitize it rigorously.\\n\\n✅ Restrict URLs to Known Safe Domains\\n\\nWhere possible, limit requests to a predefined set of safe URLs or domains. This can be done using server-side mapping from user-supplied keys to URLs.\\n\\n✅ Implement IP Safelists and Blocklists\\n\\nUse an HTTP client that allows customizing and blocking specific IP ranges, such as private network addresses and other non-routable IP ranges.\\n\\n✅ Use Network-Level Security\\n\\nIf the HTTP client doesn't support IP range blocking, consider running it with restricted system permissions, or within a secure network where firewall rules can block dangerous addresses.\\n\\n✅ Leverage a Secure HTTP Proxy\\n\\nAs a last resort, route all backend HTTP requests through a secure proxy that can filter out and block requests to potentially harmful addresses.\\n\\n\`\`\`go\\nimport (\\n \\"context\\"\\n \\"crypto/tls\\"\\n \\"errors\\"\\n \\"net\\"\\n \\"net/http\\"\\n \\"time\\"\\n)\\n\\n// IsDisallowedIP checks if an IP address falls within a range of disallowed IPs.\\nfunc IsDisallowedIP(hostIP string) bool {\\n ip := net.ParseIP(hostIP)\\n // Add more checks as necessary\\n return ip.IsMulticast() || ip.IsUnspecified() || ip.IsLoopback() || ip.IsPrivate()\\n}\\n\\n// SafeTransport defines a custom transport that filters out disallowed IP addresses.\\nfunc SafeTransport(timeout time.Duration) *http.Transport {\\n return &http.Transport{\\n DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\\n c, err := net.DialTimeout(network, addr, timeout)\\n if err != nil {\\n return nil, err\\n }\\n ip, _, _ := net.SplitHostPort(c.RemoteAddr().String())\\n if IsDisallowedIP(ip) {\\n c.Close()\\n return nil, errors.New(\\"ip address is not allowed\\")\\n }\\n return c, err\\n },\\n DialTLS: func(network, addr string) (net.Conn, error) {\\n dialer := &net.Dialer{Timeout: timeout}\\n c, err := tls.DialWithDialer(dialer, network, addr, &tls.Config{})\\n if err != nil {\\n return nil, err\\n }\\n ip, _, _ := net.SplitHostPort(c.RemoteAddr().String())\\n if IsDisallowedIP(ip) {\\n c.Close()\\n return nil, errors.New(\\"ip address is not allowed\\")\\n }\\n return c, c.Handshake()\\n },\\n TLSHandshakeTimeout: timeout,\\n }\\n}\\n\\n// httpRequest performs a secure HTTP request, filtering out disallowed IPs.\\nfunc httpRequest(requestUrl string) {\\n const clientConnectTimeout = time.Second * 10\\n httpClient := &http.Client{\\n Transport: SafeTransport(clientConnectTimeout),\\n }\\n resp, err := httpClient.Get(requestUrl)\\n if err != nil {\\n log.Fatal(err)\\n }\\n defer resp.Body.Close()\\n // Process response\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP SSRF Prevention Cheat Sheet](https://owasp.org/www-community/attacks/Server_Side_Request_Forgery)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_injection_ssrf_injection", + "line_number": 114, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 114, + "end": 114, + "column": { + "start": 15, + "end": 28 + } + }, + "sink": { + "start": 114, + "end": 114, + "column": { + "start": 15, + "end": 28 + }, + "content": "http.Get(url)" + }, + "parent_line_number": 114, + "snippet": "http.Get(url)", + "fingerprint": "9be6d64d7285e77ffafc4015fe97ab33_2", + "old_fingerprint": "3062d5fa090e29d011c48ba41511badf_2", + "code_extract": "\\tresp, err := http.Get(url)" + }, + { + "cwe_ids": [ + "918" + ], + "id": "go_gosec_injection_ssrf_injection", + "title": "Server Side Request Forgery (SSRF)", + "description": "## Description\\n\\nServer-Side Request Forgery (SSRF) is a security vulnerability that occurs when a server-side application makes HTTP requests to arbitrary URLs controlled by the user. SSRF can be exploited by attackers to target internal systems behind firewalls that are otherwise inaccessible from the external network, by tricking the server into making requests to these systems.\\n\\n## Remediations\\n\\nTo mitigate SSRF vulnerabilities, follow these guidelines:\\n\\n✅ Validate User Input\\n\\nAvoid using direct user input to construct URLs for backend requests. If you must use user input, validate or sanitize it rigorously.\\n\\n✅ Restrict URLs to Known Safe Domains\\n\\nWhere possible, limit requests to a predefined set of safe URLs or domains. This can be done using server-side mapping from user-supplied keys to URLs.\\n\\n✅ Implement IP Safelists and Blocklists\\n\\nUse an HTTP client that allows customizing and blocking specific IP ranges, such as private network addresses and other non-routable IP ranges.\\n\\n✅ Use Network-Level Security\\n\\nIf the HTTP client doesn't support IP range blocking, consider running it with restricted system permissions, or within a secure network where firewall rules can block dangerous addresses.\\n\\n✅ Leverage a Secure HTTP Proxy\\n\\nAs a last resort, route all backend HTTP requests through a secure proxy that can filter out and block requests to potentially harmful addresses.\\n\\n\`\`\`go\\nimport (\\n \\"context\\"\\n \\"crypto/tls\\"\\n \\"errors\\"\\n \\"net\\"\\n \\"net/http\\"\\n \\"time\\"\\n)\\n\\n// IsDisallowedIP checks if an IP address falls within a range of disallowed IPs.\\nfunc IsDisallowedIP(hostIP string) bool {\\n ip := net.ParseIP(hostIP)\\n // Add more checks as necessary\\n return ip.IsMulticast() || ip.IsUnspecified() || ip.IsLoopback() || ip.IsPrivate()\\n}\\n\\n// SafeTransport defines a custom transport that filters out disallowed IP addresses.\\nfunc SafeTransport(timeout time.Duration) *http.Transport {\\n return &http.Transport{\\n DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\\n c, err := net.DialTimeout(network, addr, timeout)\\n if err != nil {\\n return nil, err\\n }\\n ip, _, _ := net.SplitHostPort(c.RemoteAddr().String())\\n if IsDisallowedIP(ip) {\\n c.Close()\\n return nil, errors.New(\\"ip address is not allowed\\")\\n }\\n return c, err\\n },\\n DialTLS: func(network, addr string) (net.Conn, error) {\\n dialer := &net.Dialer{Timeout: timeout}\\n c, err := tls.DialWithDialer(dialer, network, addr, &tls.Config{})\\n if err != nil {\\n return nil, err\\n }\\n ip, _, _ := net.SplitHostPort(c.RemoteAddr().String())\\n if IsDisallowedIP(ip) {\\n c.Close()\\n return nil, errors.New(\\"ip address is not allowed\\")\\n }\\n return c, c.Handshake()\\n },\\n TLSHandshakeTimeout: timeout,\\n }\\n}\\n\\n// httpRequest performs a secure HTTP request, filtering out disallowed IPs.\\nfunc httpRequest(requestUrl string) {\\n const clientConnectTimeout = time.Second * 10\\n httpClient := &http.Client{\\n Transport: SafeTransport(clientConnectTimeout),\\n }\\n resp, err := httpClient.Get(requestUrl)\\n if err != nil {\\n log.Fatal(err)\\n }\\n defer resp.Body.Close()\\n // Process response\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP SSRF Prevention Cheat Sheet](https://owasp.org/www-community/attacks/Server_Side_Request_Forgery)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_injection_ssrf_injection", + "line_number": 129, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 129, + "end": 129, + "column": { + "start": 14, + "end": 62 + } + }, + "sink": { + "start": 129, + "end": 129, + "column": { + "start": 14, + "end": 62 + }, + "content": "http.NewRequest(\\"POST\\", url, bytes.NewBuffer(q))" + }, + "parent_line_number": 129, + "snippet": "http.NewRequest(\\"POST\\", url, bytes.NewBuffer(q))", + "fingerprint": "9be6d64d7285e77ffafc4015fe97ab33_3", + "old_fingerprint": "3062d5fa090e29d011c48ba41511badf_3", + "code_extract": "\\treq, err := http.NewRequest(\\"POST\\", url, bytes.NewBuffer(q))" + } + ] +}" +`; diff --git a/tests/go/gosec/injection/ssrf_injection/test.js b/tests/go/gosec/injection/ssrf_injection/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/injection/ssrf_injection/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/injection/ssrf_injection/testdata/main.go b/tests/go/gosec/injection/ssrf_injection/testdata/main.go new file mode 100644 index 000000000..3b42b9862 --- /dev/null +++ b/tests/go/gosec/injection/ssrf_injection/testdata/main.go @@ -0,0 +1,131 @@ +// Input from the std in is considered insecure +package main + +import ( + "bufio" + "bytes" + "fmt" + "io/ioutil" + "net/http" + "os" +) + +func foo1() { + in := bufio.NewReader(os.Stdin) + url, err := in.ReadString('\n') + if err != nil { + panic(err) + } + resp, err := http.Get(url) + if err != nil { + panic(err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + panic(err) + } + fmt.Printf("%s", body) +} + +var url string = "https://www.google.com" + +func foo2() { + resp, err := http.Get(url) + if err != nil { + panic(err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + panic(err) + } + fmt.Printf("%s", body) +} + +func foo3() { + url := os.Getenv("tainted_url") + resp, err := http.Get(url) + if err != nil { + panic(err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + panic(err) + } + fmt.Printf("%s", body) +} + +const url1 = "http://127.0.0.1" + +func foo4() { + resp, err := http.Get(url1) + if err != nil { + fmt.Println(err) + } + fmt.Println(resp.Status) +} + +func foo5() { + var url string = "http://127.0.0.1" + resp, err := http.Get(url) + if err != nil { + fmt.Println(err) + } + fmt.Println(resp.Status) +} + +func foo6() { + url := "http://127.0.0.1" + resp, err := http.Get(url) + if err != nil { + fmt.Println(err) + } + fmt.Println(resp.Status) +} + +func foo7() { + url1 := "test" + var url2 string = "http://127.0.0.1" + url2 = url1 + resp, err := http.Get(url2) + if err != nil { + fmt.Println(err) + } + fmt.Println(resp.Status) +} + +var Url string + +func foo8() { + resp, err := http.Get(Url) + if err != nil { + fmt.Println(err) + } + fmt.Println(resp.Status) +} + +func get(url string) { + resp, err := http.Get(url) + if err != nil { + fmt.Println(err) + } + fmt.Println(resp.Status) +} + +func foo9() { + url := "http://127.0.0.1" + get(url) +} + +func foo10() { + url := os.Args[0] + var q = []byte(`your query`) + req, err := http.NewRequest("POST", url, bytes.NewBuffer(q)) + req.Header.Set("X-Custom-Header", "myvalue") + req.Header.Set("Content-Type", "text/plain") + + client := &http.Client{} + client.Do(req) +} diff --git a/tests/go/gosec/injection/subproc_injection/__snapshots__/test.js.snap b/tests/go/gosec/injection/subproc_injection/__snapshots__/test.js.snap new file mode 100644 index 000000000..e024e35b4 --- /dev/null +++ b/tests/go/gosec/injection/subproc_injection/__snapshots__/test.js.snap @@ -0,0 +1,280 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_injection_subproc_injection test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "95" + ], + "id": "go_gosec_injection_subproc_injection", + "title": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", + "description": "## Description\\n\\nOS command injection is a severe security vulnerability that occurs when an application incorrectly processes user input. This flaw can allow attackers to execute arbitrary commands on the host operating system, potentially leading to a full system compromise.\\n\\n## Remediations\\n\\nPrevent OS command injection by adhering to the following practices:\\n\\n❌ Avoid Direct User Input\\n\\nDo not use user-supplied information for constructing OS commands or command-line arguments, as this can lead to command injection vulnerabilities.\\n\\n✅ Implement Input Validation\\n\\nEnsure that any user input is validated against a set of strict rules to ensure it does not contain malicious characters or patterns.\\n\\n✅ Use Hardcoded Arguments\\n\\nWhen invoking OS commands, use a hardcoded set of arguments to ensure that user input cannot alter the command's behavior.\\n\\n✅ Utilize Temporary Files Securely\\n\\nWhen dealing with files, create temporary files in a restricted directory, avoiding the use of user-supplied filenames.\\n\\n✅ Employ Native Libraries\\n\\nWhere possible, use native libraries or features of the programming language instead of invoking shell commands, which can be safer and more efficient.\\n\\n\`\`\`go\\nimport (\\n \\"io/ioutil\\"\\n \\"os/exec\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n userData := []byte(\\"user data\\")\\n\\n // Create a temporary file in a secure, application-specific directory\\n f, err := ioutil.TempFile(\\"/var/app/restricted\\", \\"temp-*.dat\\")\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Write user data to the temporary file\\n if _, err := f.Write(userData); err != nil {\\n f.Close()\\n log.Fatal(err)\\n }\\n\\n // Close the file handle\\n if err := f.Close(); err != nil {\\n log.Fatal(err)\\n }\\n\\n // Execute a command using the temporary file, avoiding direct user input for filenames\\n out, err := exec.Command(\\"/bin/cat\\", f.Name()).Output()\\n if err != nil {\\n log.Fatal(err)\\n }\\n // Output can be used for further processing\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_injection_subproc_injection", + "line_number": 27, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 27, + "end": 27, + "column": { + "start": 9, + "end": 67 + } + }, + "sink": { + "start": 27, + "end": 27, + "column": { + "start": 9, + "end": 67 + }, + "content": "exec.CommandContext(context.Background(), os.Args[0], \\"5\\")" + }, + "parent_line_number": 27, + "snippet": "exec.CommandContext(context.Background(), os.Args[0], \\"5\\")", + "fingerprint": "e873320a35cd659369a6c8d2ac40f9cd_0", + "old_fingerprint": "f4ac6999992abf88d09564e3aebce4fb_0", + "code_extract": "\\terr := exec.CommandContext(context.Background(), os.Args[0], \\"5\\").Run() // detected" + }, + { + "cwe_ids": [ + "95" + ], + "id": "go_gosec_injection_subproc_injection", + "title": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", + "description": "## Description\\n\\nOS command injection is a severe security vulnerability that occurs when an application incorrectly processes user input. This flaw can allow attackers to execute arbitrary commands on the host operating system, potentially leading to a full system compromise.\\n\\n## Remediations\\n\\nPrevent OS command injection by adhering to the following practices:\\n\\n❌ Avoid Direct User Input\\n\\nDo not use user-supplied information for constructing OS commands or command-line arguments, as this can lead to command injection vulnerabilities.\\n\\n✅ Implement Input Validation\\n\\nEnsure that any user input is validated against a set of strict rules to ensure it does not contain malicious characters or patterns.\\n\\n✅ Use Hardcoded Arguments\\n\\nWhen invoking OS commands, use a hardcoded set of arguments to ensure that user input cannot alter the command's behavior.\\n\\n✅ Utilize Temporary Files Securely\\n\\nWhen dealing with files, create temporary files in a restricted directory, avoiding the use of user-supplied filenames.\\n\\n✅ Employ Native Libraries\\n\\nWhere possible, use native libraries or features of the programming language instead of invoking shell commands, which can be safer and more efficient.\\n\\n\`\`\`go\\nimport (\\n \\"io/ioutil\\"\\n \\"os/exec\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n userData := []byte(\\"user data\\")\\n\\n // Create a temporary file in a secure, application-specific directory\\n f, err := ioutil.TempFile(\\"/var/app/restricted\\", \\"temp-*.dat\\")\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Write user data to the temporary file\\n if _, err := f.Write(userData); err != nil {\\n f.Close()\\n log.Fatal(err)\\n }\\n\\n // Close the file handle\\n if err := f.Close(); err != nil {\\n log.Fatal(err)\\n }\\n\\n // Execute a command using the temporary file, avoiding direct user input for filenames\\n out, err := exec.Command(\\"/bin/cat\\", f.Name()).Output()\\n if err != nil {\\n log.Fatal(err)\\n }\\n // Output can be used for further processing\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_injection_subproc_injection", + "line_number": 37, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 37, + "end": 37, + "column": { + "start": 9, + "end": 31 + } + }, + "sink": { + "start": 37, + "end": 37, + "column": { + "start": 9, + "end": 31 + }, + "content": "exec.Command(run, \\"5\\")" + }, + "parent_line_number": 37, + "snippet": "exec.Command(run, \\"5\\")", + "fingerprint": "e873320a35cd659369a6c8d2ac40f9cd_1", + "old_fingerprint": "f4ac6999992abf88d09564e3aebce4fb_1", + "code_extract": "\\tcmd := exec.Command(run, \\"5\\") // detected" + }, + { + "cwe_ids": [ + "95" + ], + "id": "go_gosec_injection_subproc_injection", + "title": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", + "description": "## Description\\n\\nOS command injection is a severe security vulnerability that occurs when an application incorrectly processes user input. This flaw can allow attackers to execute arbitrary commands on the host operating system, potentially leading to a full system compromise.\\n\\n## Remediations\\n\\nPrevent OS command injection by adhering to the following practices:\\n\\n❌ Avoid Direct User Input\\n\\nDo not use user-supplied information for constructing OS commands or command-line arguments, as this can lead to command injection vulnerabilities.\\n\\n✅ Implement Input Validation\\n\\nEnsure that any user input is validated against a set of strict rules to ensure it does not contain malicious characters or patterns.\\n\\n✅ Use Hardcoded Arguments\\n\\nWhen invoking OS commands, use a hardcoded set of arguments to ensure that user input cannot alter the command's behavior.\\n\\n✅ Utilize Temporary Files Securely\\n\\nWhen dealing with files, create temporary files in a restricted directory, avoiding the use of user-supplied filenames.\\n\\n✅ Employ Native Libraries\\n\\nWhere possible, use native libraries or features of the programming language instead of invoking shell commands, which can be safer and more efficient.\\n\\n\`\`\`go\\nimport (\\n \\"io/ioutil\\"\\n \\"os/exec\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n userData := []byte(\\"user data\\")\\n\\n // Create a temporary file in a secure, application-specific directory\\n f, err := ioutil.TempFile(\\"/var/app/restricted\\", \\"temp-*.dat\\")\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Write user data to the temporary file\\n if _, err := f.Write(userData); err != nil {\\n f.Close()\\n log.Fatal(err)\\n }\\n\\n // Close the file handle\\n if err := f.Close(); err != nil {\\n log.Fatal(err)\\n }\\n\\n // Execute a command using the temporary file, avoiding direct user input for filenames\\n out, err := exec.Command(\\"/bin/cat\\", f.Name()).Output()\\n if err != nil {\\n log.Fatal(err)\\n }\\n // Output can be used for further processing\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_injection_subproc_injection", + "line_number": 49, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 49, + "end": 49, + "column": { + "start": 9, + "end": 35 + } + }, + "sink": { + "start": 49, + "end": 49, + "column": { + "start": 9, + "end": 35 + }, + "content": "exec.Command(command, \\"5\\")" + }, + "parent_line_number": 49, + "snippet": "exec.Command(command, \\"5\\")", + "fingerprint": "e873320a35cd659369a6c8d2ac40f9cd_2", + "old_fingerprint": "f4ac6999992abf88d09564e3aebce4fb_2", + "code_extract": "\\tcmd := exec.Command(command, \\"5\\")" + }, + { + "cwe_ids": [ + "95" + ], + "id": "go_gosec_injection_subproc_injection", + "title": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", + "description": "## Description\\n\\nOS command injection is a severe security vulnerability that occurs when an application incorrectly processes user input. This flaw can allow attackers to execute arbitrary commands on the host operating system, potentially leading to a full system compromise.\\n\\n## Remediations\\n\\nPrevent OS command injection by adhering to the following practices:\\n\\n❌ Avoid Direct User Input\\n\\nDo not use user-supplied information for constructing OS commands or command-line arguments, as this can lead to command injection vulnerabilities.\\n\\n✅ Implement Input Validation\\n\\nEnsure that any user input is validated against a set of strict rules to ensure it does not contain malicious characters or patterns.\\n\\n✅ Use Hardcoded Arguments\\n\\nWhen invoking OS commands, use a hardcoded set of arguments to ensure that user input cannot alter the command's behavior.\\n\\n✅ Utilize Temporary Files Securely\\n\\nWhen dealing with files, create temporary files in a restricted directory, avoiding the use of user-supplied filenames.\\n\\n✅ Employ Native Libraries\\n\\nWhere possible, use native libraries or features of the programming language instead of invoking shell commands, which can be safer and more efficient.\\n\\n\`\`\`go\\nimport (\\n \\"io/ioutil\\"\\n \\"os/exec\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n userData := []byte(\\"user data\\")\\n\\n // Create a temporary file in a secure, application-specific directory\\n f, err := ioutil.TempFile(\\"/var/app/restricted\\", \\"temp-*.dat\\")\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Write user data to the temporary file\\n if _, err := f.Write(userData); err != nil {\\n f.Close()\\n log.Fatal(err)\\n }\\n\\n // Close the file handle\\n if err := f.Close(); err != nil {\\n log.Fatal(err)\\n }\\n\\n // Execute a command using the temporary file, avoiding direct user input for filenames\\n out, err := exec.Command(\\"/bin/cat\\", f.Name()).Output()\\n if err != nil {\\n log.Fatal(err)\\n }\\n // Output can be used for further processing\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_injection_subproc_injection", + "line_number": 64, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 64, + "end": 64, + "column": { + "start": 9, + "end": 24 + } + }, + "sink": { + "start": 64, + "end": 64, + "column": { + "start": 9, + "end": 24 + }, + "content": "exec.Command(c)" + }, + "parent_line_number": 64, + "snippet": "exec.Command(c)", + "fingerprint": "e873320a35cd659369a6c8d2ac40f9cd_3", + "old_fingerprint": "f4ac6999992abf88d09564e3aebce4fb_3", + "code_extract": "\\tcmd := exec.Command(c)" + }, + { + "cwe_ids": [ + "95" + ], + "id": "go_gosec_injection_subproc_injection", + "title": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", + "description": "## Description\\n\\nOS command injection is a severe security vulnerability that occurs when an application incorrectly processes user input. This flaw can allow attackers to execute arbitrary commands on the host operating system, potentially leading to a full system compromise.\\n\\n## Remediations\\n\\nPrevent OS command injection by adhering to the following practices:\\n\\n❌ Avoid Direct User Input\\n\\nDo not use user-supplied information for constructing OS commands or command-line arguments, as this can lead to command injection vulnerabilities.\\n\\n✅ Implement Input Validation\\n\\nEnsure that any user input is validated against a set of strict rules to ensure it does not contain malicious characters or patterns.\\n\\n✅ Use Hardcoded Arguments\\n\\nWhen invoking OS commands, use a hardcoded set of arguments to ensure that user input cannot alter the command's behavior.\\n\\n✅ Utilize Temporary Files Securely\\n\\nWhen dealing with files, create temporary files in a restricted directory, avoiding the use of user-supplied filenames.\\n\\n✅ Employ Native Libraries\\n\\nWhere possible, use native libraries or features of the programming language instead of invoking shell commands, which can be safer and more efficient.\\n\\n\`\`\`go\\nimport (\\n \\"io/ioutil\\"\\n \\"os/exec\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n userData := []byte(\\"user data\\")\\n\\n // Create a temporary file in a secure, application-specific directory\\n f, err := ioutil.TempFile(\\"/var/app/restricted\\", \\"temp-*.dat\\")\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Write user data to the temporary file\\n if _, err := f.Write(userData); err != nil {\\n f.Close()\\n log.Fatal(err)\\n }\\n\\n // Close the file handle\\n if err := f.Close(); err != nil {\\n log.Fatal(err)\\n }\\n\\n // Execute a command using the temporary file, avoiding direct user input for filenames\\n out, err := exec.Command(\\"/bin/cat\\", f.Name()).Output()\\n if err != nil {\\n log.Fatal(err)\\n }\\n // Output can be used for further processing\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_injection_subproc_injection", + "line_number": 72, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 72, + "end": 72, + "column": { + "start": 8, + "end": 23 + } + }, + "sink": { + "start": 72, + "end": 72, + "column": { + "start": 8, + "end": 23 + }, + "content": "exec.Command(a)" + }, + "parent_line_number": 72, + "snippet": "exec.Command(a)", + "fingerprint": "e873320a35cd659369a6c8d2ac40f9cd_4", + "old_fingerprint": "f4ac6999992abf88d09564e3aebce4fb_4", + "code_extract": "\\tcmd = exec.Command(a)" + }, + { + "cwe_ids": [ + "95" + ], + "id": "go_gosec_injection_subproc_injection", + "title": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", + "description": "## Description\\n\\nOS command injection is a severe security vulnerability that occurs when an application incorrectly processes user input. This flaw can allow attackers to execute arbitrary commands on the host operating system, potentially leading to a full system compromise.\\n\\n## Remediations\\n\\nPrevent OS command injection by adhering to the following practices:\\n\\n❌ Avoid Direct User Input\\n\\nDo not use user-supplied information for constructing OS commands or command-line arguments, as this can lead to command injection vulnerabilities.\\n\\n✅ Implement Input Validation\\n\\nEnsure that any user input is validated against a set of strict rules to ensure it does not contain malicious characters or patterns.\\n\\n✅ Use Hardcoded Arguments\\n\\nWhen invoking OS commands, use a hardcoded set of arguments to ensure that user input cannot alter the command's behavior.\\n\\n✅ Utilize Temporary Files Securely\\n\\nWhen dealing with files, create temporary files in a restricted directory, avoiding the use of user-supplied filenames.\\n\\n✅ Employ Native Libraries\\n\\nWhere possible, use native libraries or features of the programming language instead of invoking shell commands, which can be safer and more efficient.\\n\\n\`\`\`go\\nimport (\\n \\"io/ioutil\\"\\n \\"os/exec\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n userData := []byte(\\"user data\\")\\n\\n // Create a temporary file in a secure, application-specific directory\\n f, err := ioutil.TempFile(\\"/var/app/restricted\\", \\"temp-*.dat\\")\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Write user data to the temporary file\\n if _, err := f.Write(userData); err != nil {\\n f.Close()\\n log.Fatal(err)\\n }\\n\\n // Close the file handle\\n if err := f.Close(); err != nil {\\n log.Fatal(err)\\n }\\n\\n // Execute a command using the temporary file, avoiding direct user input for filenames\\n out, err := exec.Command(\\"/bin/cat\\", f.Name()).Output()\\n if err != nil {\\n log.Fatal(err)\\n }\\n // Output can be used for further processing\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_injection_subproc_injection", + "line_number": 94, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 94, + "end": 94, + "column": { + "start": 12, + "end": 54 + } + }, + "sink": { + "start": 94, + "end": 94, + "column": { + "start": 12, + "end": 54 + }, + "content": "syscall.ForkExec(command, []string{}, nil)" + }, + "parent_line_number": 94, + "snippet": "syscall.ForkExec(command, []string{}, nil)", + "fingerprint": "e873320a35cd659369a6c8d2ac40f9cd_5", + "old_fingerprint": "f4ac6999992abf88d09564e3aebce4fb_5", + "code_extract": "\\t_, err := syscall.ForkExec(command, []string{}, nil)" + }, + { + "cwe_ids": [ + "95" + ], + "id": "go_gosec_injection_subproc_injection", + "title": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", + "description": "## Description\\n\\nOS command injection is a severe security vulnerability that occurs when an application incorrectly processes user input. This flaw can allow attackers to execute arbitrary commands on the host operating system, potentially leading to a full system compromise.\\n\\n## Remediations\\n\\nPrevent OS command injection by adhering to the following practices:\\n\\n❌ Avoid Direct User Input\\n\\nDo not use user-supplied information for constructing OS commands or command-line arguments, as this can lead to command injection vulnerabilities.\\n\\n✅ Implement Input Validation\\n\\nEnsure that any user input is validated against a set of strict rules to ensure it does not contain malicious characters or patterns.\\n\\n✅ Use Hardcoded Arguments\\n\\nWhen invoking OS commands, use a hardcoded set of arguments to ensure that user input cannot alter the command's behavior.\\n\\n✅ Utilize Temporary Files Securely\\n\\nWhen dealing with files, create temporary files in a restricted directory, avoiding the use of user-supplied filenames.\\n\\n✅ Employ Native Libraries\\n\\nWhere possible, use native libraries or features of the programming language instead of invoking shell commands, which can be safer and more efficient.\\n\\n\`\`\`go\\nimport (\\n \\"io/ioutil\\"\\n \\"os/exec\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n userData := []byte(\\"user data\\")\\n\\n // Create a temporary file in a secure, application-specific directory\\n f, err := ioutil.TempFile(\\"/var/app/restricted\\", \\"temp-*.dat\\")\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Write user data to the temporary file\\n if _, err := f.Write(userData); err != nil {\\n f.Close()\\n log.Fatal(err)\\n }\\n\\n // Close the file handle\\n if err := f.Close(); err != nil {\\n log.Fatal(err)\\n }\\n\\n // Execute a command using the temporary file, avoiding direct user input for filenames\\n out, err := exec.Command(\\"/bin/cat\\", f.Name()).Output()\\n if err != nil {\\n log.Fatal(err)\\n }\\n // Output can be used for further processing\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_injection_subproc_injection", + "line_number": 106, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 106, + "end": 106, + "column": { + "start": 15, + "end": 61 + } + }, + "sink": { + "start": 106, + "end": 106, + "column": { + "start": 15, + "end": 61 + }, + "content": "syscall.StartProcess(command, []string{}, nil)" + }, + "parent_line_number": 106, + "snippet": "syscall.StartProcess(command, []string{}, nil)", + "fingerprint": "e873320a35cd659369a6c8d2ac40f9cd_6", + "old_fingerprint": "f4ac6999992abf88d09564e3aebce4fb_6", + "code_extract": "\\t_, _, err := syscall.StartProcess(command, []string{}, nil)" + }, + { + "cwe_ids": [ + "95" + ], + "id": "go_gosec_injection_subproc_injection", + "title": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", + "description": "## Description\\n\\nOS command injection is a severe security vulnerability that occurs when an application incorrectly processes user input. This flaw can allow attackers to execute arbitrary commands on the host operating system, potentially leading to a full system compromise.\\n\\n## Remediations\\n\\nPrevent OS command injection by adhering to the following practices:\\n\\n❌ Avoid Direct User Input\\n\\nDo not use user-supplied information for constructing OS commands or command-line arguments, as this can lead to command injection vulnerabilities.\\n\\n✅ Implement Input Validation\\n\\nEnsure that any user input is validated against a set of strict rules to ensure it does not contain malicious characters or patterns.\\n\\n✅ Use Hardcoded Arguments\\n\\nWhen invoking OS commands, use a hardcoded set of arguments to ensure that user input cannot alter the command's behavior.\\n\\n✅ Utilize Temporary Files Securely\\n\\nWhen dealing with files, create temporary files in a restricted directory, avoiding the use of user-supplied filenames.\\n\\n✅ Employ Native Libraries\\n\\nWhere possible, use native libraries or features of the programming language instead of invoking shell commands, which can be safer and more efficient.\\n\\n\`\`\`go\\nimport (\\n \\"io/ioutil\\"\\n \\"os/exec\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n userData := []byte(\\"user data\\")\\n\\n // Create a temporary file in a secure, application-specific directory\\n f, err := ioutil.TempFile(\\"/var/app/restricted\\", \\"temp-*.dat\\")\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Write user data to the temporary file\\n if _, err := f.Write(userData); err != nil {\\n f.Close()\\n log.Fatal(err)\\n }\\n\\n // Close the file handle\\n if err := f.Close(); err != nil {\\n log.Fatal(err)\\n }\\n\\n // Execute a command using the temporary file, avoiding direct user input for filenames\\n out, err := exec.Command(\\"/bin/cat\\", f.Name()).Output()\\n if err != nil {\\n log.Fatal(err)\\n }\\n // Output can be used for further processing\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_injection_subproc_injection", + "line_number": 130, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 130, + "end": 130, + "column": { + "start": 9, + "end": 67 + } + }, + "sink": { + "start": 130, + "end": 130, + "column": { + "start": 9, + "end": 67 + }, + "content": "exec.CommandContext(context.Background(), os.Args[0], \\"5\\")" + }, + "parent_line_number": 130, + "snippet": "exec.CommandContext(context.Background(), os.Args[0], \\"5\\")", + "fingerprint": "e873320a35cd659369a6c8d2ac40f9cd_7", + "old_fingerprint": "f4ac6999992abf88d09564e3aebce4fb_7", + "code_extract": "\\terr := exec.CommandContext(context.Background(), os.Args[0], \\"5\\").Run() // detected" + } + ] +}" +`; diff --git a/tests/go/gosec/injection/subproc_injection/test.js b/tests/go/gosec/injection/subproc_injection/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/injection/subproc_injection/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/injection/subproc_injection/testdata/main.go b/tests/go/gosec/injection/subproc_injection/testdata/main.go new file mode 100644 index 000000000..b7c5c5cf4 --- /dev/null +++ b/tests/go/gosec/injection/subproc_injection/testdata/main.go @@ -0,0 +1,131 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + "os/exec" + "syscall" +) + +func foo1() { + // ok + err := exec.CommandContext(context.Background(), "git", "rev-parse", "--show-toplavel").Run() + if err != nil { + log.Fatal(err) + } + log.Printf("Command finished with error: %v", err) +} + +func foo2() { + // ruleid + err := exec.CommandContext(context.Background(), os.Args[0], "5").Run() // detected + if err != nil { + log.Fatal(err) + } + log.Printf("Command finished with error: %v", err) +} + +func foo3() { + run := "sleep" + os.Getenv("SOMETHING") + // ruleid + cmd := exec.Command(run, "5") // detected + err := cmd.Start() + if err != nil { + log.Fatal(err) + } + log.Printf("Waiting for command to finish...") + err = cmd.Wait() + log.Printf("Command finished with error: %v", err) +} + +func foo4(command string) { + // ruleid + cmd := exec.Command(command, "5") + err := cmd.Start() + if err != nil { + log.Fatal(err) + } + log.Printf("Waiting for command to finish...") + err = cmd.Wait() +} + +func foo5() { + foo4("sleep") +} + +func foo6(a string, c string) { + // ruleid + cmd := exec.Command(c) + err := cmd.Start() + if err != nil { + log.Fatal(err) + } + log.Printf("Waiting for command to finish...") + err = cmd.Wait() + // ruleid + cmd = exec.Command(a) + err = cmd.Start() + if err != nil { + log.Fatal(err) + } + log.Printf("Waiting for command to finish...") + err = cmd.Wait() +} + +func foo7() { + foo6("ll", "ls") +} + +func foo8() { + err := syscall.Exec("/bin/cat", []string{"/etc/passwd"}, nil) + if err != nil { + fmt.Printf("Error: %v\n", err) + } +} + +func foo9(command string) { + // ruleid + _, err := syscall.ForkExec(command, []string{}, nil) + if err != nil { + fmt.Printf("Error: %v\n", err) + } +} + +func foo10() { + foo9("sleep") +} + +func foo11(command string) { + // ruleid + _, _, err := syscall.StartProcess(command, []string{}, nil) + if err != nil { + fmt.Printf("Error: %v\n", err) + } +} + +func foo12() { + foo11("sleep") +} + +func foo13() { + run := "sleep" + cmd := exec.Command(run, "5") + err := cmd.Start() + if err != nil { + log.Fatal(err) + } + log.Printf("Waiting for command to finish...") + err = cmd.Wait() + log.Printf("Command finished with error: %v", err) +} + +func foo14() { + // ruleid + err := exec.CommandContext(context.Background(), os.Args[0], "5").Run() // detected + if err != nil { + log.Fatal(err) + } + log.Printf("Command finished with error: %v", err) +} diff --git a/tests/go/gosec/injection/template_injection/__snapshots__/test.js.snap b/tests/go/gosec/injection/template_injection/__snapshots__/test.js.snap new file mode 100644 index 000000000..4793e460f --- /dev/null +++ b/tests/go/gosec/injection/template_injection/__snapshots__/test.js.snap @@ -0,0 +1,110 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_injection_template_injection test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "79" + ], + "id": "go_gosec_injection_template_injection", + "title": "Improper neutralization of input during web page generation ('Cross-site Scripting')", + "description": "## Description\\n\\nCross-Site Scripting (XSS) is a vulnerability that allows attackers to run malicious scripts in the context of a trusted web application. This can happen when an application includes untrusted data without proper validation or escaping. There are several contexts where XSS can occur, each requiring specific encoding strategies to mitigate the risk.\\n\\n## Remediations\\n\\nTo defend against XSS attacks, consider the following measures:\\n\\n✅ Encode Based on Context\\n\\nWhen user input is reflected back in HTML, ensure it is encoded based on the context in which it is used (e.g., HTML content, HTML attributes, JavaScript context, CSS context, etc.).\\n\\n✅ Template Safely\\n\\nUtilize templating engines that automatically encode data based on context, and be cautious not to override these safeguards.\\n\\n✅ Sanitize Data\\n\\nUse libraries or functions designed to sanitize user input, particularly when inserting content into a web page.\\n\\n✅ Separate Data from Code\\n\\nAvoid inline scripting and event handlers, and instead use separate JavaScript files to handle events. This reduces the risk of script injection through event attributes.\\n\\n✅ Avoid Mixing Templating Systems\\n\\nDo not mix server-side and client-side templating systems, as server-side systems may not escape output in a way that is safe for client-side use.\\n\\n❌ Do Not Encode Before Storing\\n\\nAvoid encoding user input before storing it in a database. The encoding should be applied when the data is output, not before storage, to ensure that it is encoded appropriately for its context.\\n\\nHere's an example of using Go’s \`html/template\` package to safely render HTML content:\\n\\n\`\`\`go\\nimport (\\n \\"html/template\\"\\n \\"os\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n // Define a template with a function to safely render HTML\\n testTemplate, err := template.New(\\"testTemplate\\").Funcs(template.FuncMap{\\n \\"SafeHTML\\": func() template.HTML {\\n const safeHTML = \\"
hardcoded, safe html
\\"\\n return template.HTML(safeHTML)\\n },\\n }).Parse(\`{{ SafeHTML }}\`)\\n\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Execute the template and ensure proper encoding\\n if err := testTemplate.Execute(os.Stdout, nil); err != nil {\\n log.Fatal(err)\\n }\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)\\n- [Go html/template Documentation](https://pkg.go.dev/html/template)\\n- [CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')](https://cwe.mitre.org/data/definitions/79.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_injection_template_injection", + "line_number": 27, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 27, + "end": 27, + "column": { + "start": 12, + "end": 28 + } + }, + "sink": { + "start": 27, + "end": 27, + "column": { + "start": 12, + "end": 28 + }, + "content": "template.HTML(a)" + }, + "parent_line_number": 27, + "snippet": "template.HTML(a)", + "fingerprint": "681dfcec8a0252d95ac5e3fdfd0130f4_0", + "old_fingerprint": "264c42ced6ea2219364b42acf3794d16_0", + "code_extract": "\\t\\t\\"Body\\": template.HTML(a)," + }, + { + "cwe_ids": [ + "79" + ], + "id": "go_gosec_injection_template_injection", + "title": "Improper neutralization of input during web page generation ('Cross-site Scripting')", + "description": "## Description\\n\\nCross-Site Scripting (XSS) is a vulnerability that allows attackers to run malicious scripts in the context of a trusted web application. This can happen when an application includes untrusted data without proper validation or escaping. There are several contexts where XSS can occur, each requiring specific encoding strategies to mitigate the risk.\\n\\n## Remediations\\n\\nTo defend against XSS attacks, consider the following measures:\\n\\n✅ Encode Based on Context\\n\\nWhen user input is reflected back in HTML, ensure it is encoded based on the context in which it is used (e.g., HTML content, HTML attributes, JavaScript context, CSS context, etc.).\\n\\n✅ Template Safely\\n\\nUtilize templating engines that automatically encode data based on context, and be cautious not to override these safeguards.\\n\\n✅ Sanitize Data\\n\\nUse libraries or functions designed to sanitize user input, particularly when inserting content into a web page.\\n\\n✅ Separate Data from Code\\n\\nAvoid inline scripting and event handlers, and instead use separate JavaScript files to handle events. This reduces the risk of script injection through event attributes.\\n\\n✅ Avoid Mixing Templating Systems\\n\\nDo not mix server-side and client-side templating systems, as server-side systems may not escape output in a way that is safe for client-side use.\\n\\n❌ Do Not Encode Before Storing\\n\\nAvoid encoding user input before storing it in a database. The encoding should be applied when the data is output, not before storage, to ensure that it is encoded appropriately for its context.\\n\\nHere's an example of using Go’s \`html/template\` package to safely render HTML content:\\n\\n\`\`\`go\\nimport (\\n \\"html/template\\"\\n \\"os\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n // Define a template with a function to safely render HTML\\n testTemplate, err := template.New(\\"testTemplate\\").Funcs(template.FuncMap{\\n \\"SafeHTML\\": func() template.HTML {\\n const safeHTML = \\"
hardcoded, safe html
\\"\\n return template.HTML(safeHTML)\\n },\\n }).Parse(\`{{ SafeHTML }}\`)\\n\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Execute the template and ensure proper encoding\\n if err := testTemplate.Execute(os.Stdout, nil); err != nil {\\n log.Fatal(err)\\n }\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)\\n- [Go html/template Documentation](https://pkg.go.dev/html/template)\\n- [CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')](https://cwe.mitre.org/data/definitions/79.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_injection_template_injection", + "line_number": 36, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 36, + "end": 36, + "column": { + "start": 12, + "end": 26 + } + }, + "sink": { + "start": 36, + "end": 36, + "column": { + "start": 12, + "end": 26 + }, + "content": "template.JS(a)" + }, + "parent_line_number": 36, + "snippet": "template.JS(a)", + "fingerprint": "681dfcec8a0252d95ac5e3fdfd0130f4_1", + "old_fingerprint": "264c42ced6ea2219364b42acf3794d16_1", + "code_extract": "\\t\\t\\"Body\\": template.JS(a)," + }, + { + "cwe_ids": [ + "79" + ], + "id": "go_gosec_injection_template_injection", + "title": "Improper neutralization of input during web page generation ('Cross-site Scripting')", + "description": "## Description\\n\\nCross-Site Scripting (XSS) is a vulnerability that allows attackers to run malicious scripts in the context of a trusted web application. This can happen when an application includes untrusted data without proper validation or escaping. There are several contexts where XSS can occur, each requiring specific encoding strategies to mitigate the risk.\\n\\n## Remediations\\n\\nTo defend against XSS attacks, consider the following measures:\\n\\n✅ Encode Based on Context\\n\\nWhen user input is reflected back in HTML, ensure it is encoded based on the context in which it is used (e.g., HTML content, HTML attributes, JavaScript context, CSS context, etc.).\\n\\n✅ Template Safely\\n\\nUtilize templating engines that automatically encode data based on context, and be cautious not to override these safeguards.\\n\\n✅ Sanitize Data\\n\\nUse libraries or functions designed to sanitize user input, particularly when inserting content into a web page.\\n\\n✅ Separate Data from Code\\n\\nAvoid inline scripting and event handlers, and instead use separate JavaScript files to handle events. This reduces the risk of script injection through event attributes.\\n\\n✅ Avoid Mixing Templating Systems\\n\\nDo not mix server-side and client-side templating systems, as server-side systems may not escape output in a way that is safe for client-side use.\\n\\n❌ Do Not Encode Before Storing\\n\\nAvoid encoding user input before storing it in a database. The encoding should be applied when the data is output, not before storage, to ensure that it is encoded appropriately for its context.\\n\\nHere's an example of using Go’s \`html/template\` package to safely render HTML content:\\n\\n\`\`\`go\\nimport (\\n \\"html/template\\"\\n \\"os\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n // Define a template with a function to safely render HTML\\n testTemplate, err := template.New(\\"testTemplate\\").Funcs(template.FuncMap{\\n \\"SafeHTML\\": func() template.HTML {\\n const safeHTML = \\"
hardcoded, safe html
\\"\\n return template.HTML(safeHTML)\\n },\\n }).Parse(\`{{ SafeHTML }}\`)\\n\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Execute the template and ensure proper encoding\\n if err := testTemplate.Execute(os.Stdout, nil); err != nil {\\n log.Fatal(err)\\n }\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)\\n- [Go html/template Documentation](https://pkg.go.dev/html/template)\\n- [CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')](https://cwe.mitre.org/data/definitions/79.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_injection_template_injection", + "line_number": 45, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 45, + "end": 45, + "column": { + "start": 12, + "end": 27 + } + }, + "sink": { + "start": 45, + "end": 45, + "column": { + "start": 12, + "end": 27 + }, + "content": "template.URL(a)" + }, + "parent_line_number": 45, + "snippet": "template.URL(a)", + "fingerprint": "681dfcec8a0252d95ac5e3fdfd0130f4_2", + "old_fingerprint": "264c42ced6ea2219364b42acf3794d16_2", + "code_extract": "\\t\\t\\"Body\\": template.URL(a)," + } + ] +}" +`; diff --git a/tests/go/gosec/injection/template_injection/test.js b/tests/go/gosec/injection/template_injection/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/injection/template_injection/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/injection/template_injection/testdata/main.go b/tests/go/gosec/injection/template_injection/testdata/main.go new file mode 100644 index 000000000..f0d97a68e --- /dev/null +++ b/tests/go/gosec/injection/template_injection/testdata/main.go @@ -0,0 +1,44 @@ +package main + +import ( + "html/template" + "os" +) + +const tmpl = "" + +func foo1() { + t := template.Must(template.New("ex").Parse(tmpl)) + v := map[string]interface{}{ + "Title": "Test World", + "Body": template.HTML(""), + } + t.Execute(os.Stdout, v) +} + +func foo2(a string) { + t := template.Must(template.New("ex").Parse(tmpl)) + v := map[string]interface{}{ + "Title": "Test World", + "Body": template.HTML(a), + } + t.Execute(os.Stdout, v) +} + +func foo3(a string) { + t := template.Must(template.New("ex").Parse(tmpl)) + v := map[string]interface{}{ + "Title": "Test World", + "Body": template.JS(a), + } + t.Execute(os.Stdout, v) +} + +func foo4(a string) { + t := template.Must(template.New("ex").Parse(tmpl)) + v := map[string]interface{}{ + "Title": "Test World", + "Body": template.URL(a), + } + t.Execute(os.Stdout, v) +} diff --git a/tests/go/gosec/leak/pprof_endpoint/__snapshots__/test.js.snap b/tests/go/gosec/leak/pprof_endpoint/__snapshots__/test.js.snap new file mode 100644 index 000000000..5f93f242d --- /dev/null +++ b/tests/go/gosec/leak/pprof_endpoint/__snapshots__/test.js.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_leak_pprof_endpoint test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "918" + ], + "id": "go_gosec_leak_pprof_endpoint", + "title": "Active debug code (pprof enabled)", + "description": "## Description\\n\\nGo's standard library includes a profiling tool that can be enabled by importing \`net/http/pprof\`. This tool provides a \`/debug/pprof\` endpoint that exposes runtime profiling data over HTTP. When enabled in a production environment, it can present a significant security risk as it lacks authentication controls and can potentially leak sensitive information about the application's runtime state and environment.\\n\\n## Remediations\\n\\nTo prevent unintended exposure of profiling information:\\n\\n✅ Remove \`net/http/pprof\` in Production\\n\\nBefore deploying your application to a production environment, remove any import statements for \`net/http/pprof\` from your codebase. Ensure that the profiling endpoint is not available in the live environment.\\n\\n\`\`\`go\\n// +build !production\\n\\npackage main\\n\\nimport (\\n _ \\"net/http/pprof\\" // Ensure this line is not present in your production builds\\n \\"net/http\\"\\n)\\n\\nfunc main() {\\n // ... your application code ...\\n\\n // Start the server (omit the pprof import and handler in production)\\n log.Println(http.ListenAndServe(\\"localhost:6060\\", nil))\\n}\\n\`\`\`\\n\\n✅ Conditional Compilation\\n\\nUse build tags to include profiling only in non-production builds.\\n\\n✅ Use Environment Configurations\\n\\nConfigure environment-specific settings to conditionally enable or disable the profiling endpoints.\\n\\n✅ Implement Authentication\\n\\nIf profiling is necessary in a controlled production scenario, secure the endpoint with strong authentication mechanisms.\\n\\n## Resources\\n\\n- [Go net/http/pprof Package Documentation](https://pkg.go.dev/net/http/pprof)\\n- [Go Build Constraints Documentation](https://pkg.go.dev/go/build#hdr-Build_Constraints)\\n- [OWASP Security by Design Principles](https://owasp.org/www-project-security-by-design/)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_leak_pprof_endpoint", + "line_number": 7, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 7, + "end": 12, + "column": { + "start": 1, + "end": 2 + } + }, + "sink": { + "start": 7, + "end": 12, + "column": { + "start": 1, + "end": 2 + }, + "content": "import (\\n\\t\\"fmt\\"\\n\\t\\"log\\"\\n\\t\\"net/http\\"\\n\\t_ \\"net/http/pprof\\"\\n)" + }, + "parent_line_number": 7, + "snippet": "import (\\n\\t\\"fmt\\"\\n\\t\\"log\\"\\n\\t\\"net/http\\"\\n\\t_ \\"net/http/pprof\\"\\n)", + "fingerprint": "76d7f307921424da0f234602dea9aaa7_0", + "old_fingerprint": "402bf06eb23c86740dd7ee778fce3c5f_0", + "code_extract": "import (\\n\\t\\"fmt\\"\\n\\t\\"log\\"\\n\\t\\"net/http\\"\\n\\t_ \\"net/http/pprof\\"\\n)" + } + ] +}" +`; diff --git a/tests/go/gosec/leak/pprof_endpoint/test.js b/tests/go/gosec/leak/pprof_endpoint/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/leak/pprof_endpoint/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/leak/pprof_endpoint/testdata/main.go b/tests/go/gosec/leak/pprof_endpoint/testdata/main.go new file mode 100644 index 000000000..91845b558 --- /dev/null +++ b/tests/go/gosec/leak/pprof_endpoint/testdata/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" + "log" + "net/http" + _ "net/http/pprof" +) + +func foo1() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello World!") + }) + log.Fatal(http.ListenAndServe(":8080", nil)) +} + +func foo2() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello World!") + }) + log.Fatal(http.ListenAndServe(":8080", nil)) +} diff --git a/tests/go/gosec/memory/integer_overflow/__snapshots__/test.js.snap b/tests/go/gosec/memory/integer_overflow/__snapshots__/test.js.snap new file mode 100644 index 000000000..c7a13cb5b --- /dev/null +++ b/tests/go/gosec/memory/integer_overflow/__snapshots__/test.js.snap @@ -0,0 +1,76 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_memory_integer_overflow test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "190" + ], + "id": "go_gosec_memory_integer_overflow", + "title": "Integer overflow or wraparound", + "description": "## Description\\n\\nIn Go, the size of the \`int\` type varies with the system architecture: it's 32 bits on a 32-bit system and 64 bits on a 64-bit system. This variability can lead to integer overflow issues when a value returned from \`strconv.Atoi\` is cast to a smaller integer type, such as \`int32\` or \`int16\`, and the original number exceeds the maximum value that can be stored in the smaller type. Integer overflow can cause erratic behavior and potentially serious bugs.\\n\\n## Remediations\\n\\nTo prevent integer overflow and ensure safe type conversion:\\n\\n✅ Check Values Before Conversion\\n\\nBefore casting an \`int\` to a smaller type, compare it against the maximum values that the target type can hold.\\n\\n\`\`\`go\\nimport (\\n \\"strconv\\"\\n \\"fmt\\"\\n \\"log\\"\\n \\"math\\"\\n)\\n\\nfunc main() {\\n // Convert the string to an int\\n bigValue, err := strconv.Atoi(\\"32768\\")\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Ensure the value does not exceed int16's maximum limit\\n if bigValue > math.MaxInt16 {\\n log.Fatal(\\"value too large to fit in int16\\")\\n }\\n\\n // Safely convert to int16\\n value := int16(bigValue)\\n fmt.Println(value)\\n}\\n\`\`\`\\n\\n✅ Use Appropriate Types\\n\\nWhere possible, use fixed-size types like \`int32\` or \`int64\` to avoid overflow issues related to architecture-dependent sizes.\\n\\n✅ Handle Errors\\n\\nAlways handle errors returned from conversion functions like \`strconv.Atoi\` to detect and manage conversion issues immediately.\\n\\n## Resources\\n\\n- [Go math package for integer limits](https://pkg.go.dev/math#pkg-constants)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_memory_integer_overflow", + "line_number": 14, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 14, + "end": 14, + "column": { + "start": 11, + "end": 26 + } + }, + "sink": { + "start": 14, + "end": 14, + "column": { + "start": 11, + "end": 26 + }, + "content": "int32(bigValue)" + }, + "parent_line_number": 14, + "snippet": "int32(bigValue)", + "fingerprint": "b5f27376bfb10d4674975a3b925e7fa8_0", + "old_fingerprint": "eb680ca9957c3112000a5ec4865bdca7_0", + "code_extract": "\\tvalue := int32(bigValue)" + }, + { + "cwe_ids": [ + "190" + ], + "id": "go_gosec_memory_integer_overflow", + "title": "Integer overflow or wraparound", + "description": "## Description\\n\\nIn Go, the size of the \`int\` type varies with the system architecture: it's 32 bits on a 32-bit system and 64 bits on a 64-bit system. This variability can lead to integer overflow issues when a value returned from \`strconv.Atoi\` is cast to a smaller integer type, such as \`int32\` or \`int16\`, and the original number exceeds the maximum value that can be stored in the smaller type. Integer overflow can cause erratic behavior and potentially serious bugs.\\n\\n## Remediations\\n\\nTo prevent integer overflow and ensure safe type conversion:\\n\\n✅ Check Values Before Conversion\\n\\nBefore casting an \`int\` to a smaller type, compare it against the maximum values that the target type can hold.\\n\\n\`\`\`go\\nimport (\\n \\"strconv\\"\\n \\"fmt\\"\\n \\"log\\"\\n \\"math\\"\\n)\\n\\nfunc main() {\\n // Convert the string to an int\\n bigValue, err := strconv.Atoi(\\"32768\\")\\n if err != nil {\\n log.Fatal(err)\\n }\\n\\n // Ensure the value does not exceed int16's maximum limit\\n if bigValue > math.MaxInt16 {\\n log.Fatal(\\"value too large to fit in int16\\")\\n }\\n\\n // Safely convert to int16\\n value := int16(bigValue)\\n fmt.Println(value)\\n}\\n\`\`\`\\n\\n✅ Use Appropriate Types\\n\\nWhere possible, use fixed-size types like \`int32\` or \`int64\` to avoid overflow issues related to architecture-dependent sizes.\\n\\n✅ Handle Errors\\n\\nAlways handle errors returned from conversion functions like \`strconv.Atoi\` to detect and manage conversion issues immediately.\\n\\n## Resources\\n\\n- [Go math package for integer limits](https://pkg.go.dev/math#pkg-constants)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_memory_integer_overflow", + "line_number": 20, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 20, + "end": 20, + "column": { + "start": 5, + "end": 20 + } + }, + "sink": { + "start": 20, + "end": 20, + "column": { + "start": 5, + "end": 20 + }, + "content": "int16(bigValue)" + }, + "parent_line_number": 20, + "snippet": "int16(bigValue)", + "fingerprint": "b5f27376bfb10d4674975a3b925e7fa8_1", + "old_fingerprint": "eb680ca9957c3112000a5ec4865bdca7_1", + "code_extract": "\\tif int16(bigValue) < 0 {" + } + ] +}" +`; diff --git a/tests/go/gosec/memory/integer_overflow/test.js b/tests/go/gosec/memory/integer_overflow/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/memory/integer_overflow/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/memory/integer_overflow/testdata/main.go b/tests/go/gosec/memory/integer_overflow/testdata/main.go new file mode 100644 index 000000000..41cc79c94 --- /dev/null +++ b/tests/go/gosec/memory/integer_overflow/testdata/main.go @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + "strconv" +) + +func foo1() { + bigValue, _ := strconv.Atoi("2147483648") + value := int32(bigValue) + fmt.Println(value) +} + +func foo2() { + bigValue, _ := strconv.Atoi("32768") + if int16(bigValue) < 0 { + fmt.Println(bigValue) + } +} + +func foo3() { + bigValue, err := strconv.Atoi("2147483648") + if err != nil { + panic(err) + } + fmt.Println(bigValue) +} + +func foo4() { + bigValue, err := strconv.Atoi("2147483648") + if err != nil { + panic(err) + } + fmt.Println(bigValue) + test() +} + +func test() { + bigValue := 30 + value := int32(bigValue) + fmt.Println(value) +} + +func foo5() { + value := 10 + if value == 10 { + value, _ := strconv.Atoi("2147483648") + fmt.Println(value) + } + v := int32(value) + fmt.Println(v) +} diff --git a/tests/go/gosec/memory/math_big_rat/__snapshots__/test.js.snap b/tests/go/gosec/memory/math_big_rat/__snapshots__/test.js.snap new file mode 100644 index 000000000..cde24f874 --- /dev/null +++ b/tests/go/gosec/memory/math_big_rat/__snapshots__/test.js.snap @@ -0,0 +1,76 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_memory_math_big_rat test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "190" + ], + "id": "go_gosec_memory_math_big_rat", + "title": "Integer Overflow or Wraparound", + "description": "## Description\\n\\nWhen converting strings to integers using \`strconv.Atoi\` in Go, there's a risk of integer overflow if the result is assigned to a smaller integer type such as \`int16\` or \`int32\`. The size of the default \`int\` type in Go is platform-dependent—64 bits on a 64-bit system and 32 bits on a 32-bit system. Overflow can occur when the value returned from \`strconv.Atoi\` exceeds the range of the target integer type.\\n\\n## Remediations\\n\\n✅ Check Before Conversion\\n\\nAlways verify that the value returned from \`strconv.Atoi\` is within the range of the target type before conversion.\\n\\n\`\`\`go\\nif intValue, err := strconv.Atoi(stringValue); err == nil {\\n if intValue >= math.MinInt16 && intValue <= math.MaxInt16 {\\n int16Value := int16(intValue)\\n // Use int16Value safely\\n }\\n}\\n\`\`\`\\n\\n✅ Use Specific Type Conversion Functions\\n\\nUse type-specific parsing functions such as \`strconv.ParseInt\` with the appropriate bit size to directly obtain the desired type.\\n\\n\`\`\`go\\nif int64Value, err := strconv.ParseInt(stringValue, 10, 16); err == nil {\\n int16Value := int16(int64Value)\\n // Use int16Value safely\\n}\\n\`\`\`\\n\\n❌ Avoid Blind Type Casting\\n\\nDo not cast the result of \`strconv.Atoi\` to a smaller integer type without validating that the value fits within the smaller type's range.\\n\\n❌ Don't Ignore Errors\\n\\nNever ignore the error returned by \`strconv.Atoi\`. Always handle it to catch conversion issues, including potential overflows.\\n\\n## Resources\\n\\n- [Go strconv package](https://pkg.go.dev/strconv)\\n- [Go math package for min/max constants](https://pkg.go.dev/math#pkg-constants)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_memory_math_big_rat", + "line_number": 10, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 10, + "end": 10, + "column": { + "start": 2, + "end": 40 + } + }, + "sink": { + "start": 10, + "end": 10, + "column": { + "start": 2, + "end": 40 + }, + "content": "r.SetString(\\"13e-9223372036854775808\\")" + }, + "parent_line_number": 10, + "snippet": "r.SetString(\\"13e-9223372036854775808\\")", + "fingerprint": "c052fe4b2e2d5b3e93126e805db0f49b_0", + "old_fingerprint": "9eef1553c0a08adba87fa4949d1507b3_0", + "code_extract": "\\tr.SetString(\\"13e-9223372036854775808\\")" + }, + { + "cwe_ids": [ + "190" + ], + "id": "go_gosec_memory_math_big_rat", + "title": "Integer Overflow or Wraparound", + "description": "## Description\\n\\nWhen converting strings to integers using \`strconv.Atoi\` in Go, there's a risk of integer overflow if the result is assigned to a smaller integer type such as \`int16\` or \`int32\`. The size of the default \`int\` type in Go is platform-dependent—64 bits on a 64-bit system and 32 bits on a 32-bit system. Overflow can occur when the value returned from \`strconv.Atoi\` exceeds the range of the target integer type.\\n\\n## Remediations\\n\\n✅ Check Before Conversion\\n\\nAlways verify that the value returned from \`strconv.Atoi\` is within the range of the target type before conversion.\\n\\n\`\`\`go\\nif intValue, err := strconv.Atoi(stringValue); err == nil {\\n if intValue >= math.MinInt16 && intValue <= math.MaxInt16 {\\n int16Value := int16(intValue)\\n // Use int16Value safely\\n }\\n}\\n\`\`\`\\n\\n✅ Use Specific Type Conversion Functions\\n\\nUse type-specific parsing functions such as \`strconv.ParseInt\` with the appropriate bit size to directly obtain the desired type.\\n\\n\`\`\`go\\nif int64Value, err := strconv.ParseInt(stringValue, 10, 16); err == nil {\\n int16Value := int16(int64Value)\\n // Use int16Value safely\\n}\\n\`\`\`\\n\\n❌ Avoid Blind Type Casting\\n\\nDo not cast the result of \`strconv.Atoi\` to a smaller integer type without validating that the value fits within the smaller type's range.\\n\\n❌ Don't Ignore Errors\\n\\nNever ignore the error returned by \`strconv.Atoi\`. Always handle it to catch conversion issues, including potential overflows.\\n\\n## Resources\\n\\n- [Go strconv package](https://pkg.go.dev/strconv)\\n- [Go math package for min/max constants](https://pkg.go.dev/math#pkg-constants)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_memory_math_big_rat", + "line_number": 16, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 16, + "end": 16, + "column": { + "start": 2, + "end": 20 + } + }, + "sink": { + "start": 16, + "end": 16, + "column": { + "start": 2, + "end": 20 + }, + "content": "r.SetString(input)" + }, + "parent_line_number": 16, + "snippet": "r.SetString(input)", + "fingerprint": "c052fe4b2e2d5b3e93126e805db0f49b_1", + "old_fingerprint": "9eef1553c0a08adba87fa4949d1507b3_1", + "code_extract": "\\tr.SetString(input)" + } + ] +}" +`; diff --git a/tests/go/gosec/memory/math_big_rat/test.js b/tests/go/gosec/memory/math_big_rat/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/memory/math_big_rat/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/memory/math_big_rat/testdata/main.go b/tests/go/gosec/memory/math_big_rat/testdata/main.go new file mode 100644 index 000000000..30fd3dd99 --- /dev/null +++ b/tests/go/gosec/memory/math_big_rat/testdata/main.go @@ -0,0 +1,18 @@ +package main + +import ( + "fmt" + "math/big" +) + +func bad1() { + r := big.Rat{} + r.SetString("13e-9223372036854775808") + fmt.Println(r) +} + +func bad2(input string) { + r := big.Rat{} + r.SetString(input) + fmt.Println(r) +} diff --git a/tests/go/gosec/memory/memory_aliasing/__snapshots__/test.js.snap b/tests/go/gosec/memory/memory_aliasing/__snapshots__/test.js.snap new file mode 100644 index 000000000..8a8510375 --- /dev/null +++ b/tests/go/gosec/memory/memory_aliasing/__snapshots__/test.js.snap @@ -0,0 +1,76 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_memory_memory_aliasing test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "118" + ], + "id": "go_gosec_memory_memory_aliasing", + "title": "Incorrect access of indexable resource ('Range Error')", + "description": "## Description\\n\\nGo's \`for ... range\` constructs allocate a single iteration variable for the loop's duration, which can cause confusion when addresses of this variable are stored or used beyond a single iteration. Since the iteration variable's address remains constant, subsequent iterations overwrite the previously referenced values, leading to unexpected results, particularly when using go routines or deferred functions within the loop.\\n\\n## Remediations\\n\\n✅ Create a New Variable Inside the Loop\\n\\nDeclare a new local variable within the loop's scope to hold the iteration value. This ensures a unique address is used for each iteration.\\n\\n\`\`\`go\\nfor _, n := range []someStruct{{1}, {2}, {3}, {4}} {\\n localVar := n\\n // Use localVar instead of n\\n}\\n\`\`\`\\n\\n✅ Use Indexed Addressing\\n\\nInstead of the iteration variable, directly reference the indexed element within the array or slice.\\n\\n\`\`\`go\\nfor i := range mySlice {\\n // Use &mySlice[i] to obtain a stable address\\n}\\n\`\`\`\\n\\n❌ Do Not Store the Address of the Iteration Variable\\n\\nAvoid taking the address of the iteration variable and storing it, as it leads to all references pointing to the same memory location.\\n\\n❌ Avoid Using the Iteration Variable's Address in Goroutines\\n\\nUsing the iteration variable's address directly in goroutines can cause race conditions or logical errors, as the variable's value may change before the goroutine accesses it.\\n\\n## Resources\\n\\n- [Go For Statements](https://go.dev/ref/spec#For_statements)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_memory_memory_aliasing", + "line_number": 27, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 27, + "end": 27, + "column": { + "start": 16, + "end": 21 + } + }, + "sink": { + "start": 27, + "end": 27, + "column": { + "start": 16, + "end": 21 + }, + "content": "&item" + }, + "parent_line_number": 27, + "snippet": "&item", + "fingerprint": "48f24a9e016ac12602af8f93ed78cac9_0", + "old_fingerprint": "f4242521fe214000ea523c0029908b3d_0", + "code_extract": "\\t\\tappendVector(&item)" + }, + { + "cwe_ids": [ + "118" + ], + "id": "go_gosec_memory_memory_aliasing", + "title": "Incorrect access of indexable resource ('Range Error')", + "description": "## Description\\n\\nGo's \`for ... range\` constructs allocate a single iteration variable for the loop's duration, which can cause confusion when addresses of this variable are stored or used beyond a single iteration. Since the iteration variable's address remains constant, subsequent iterations overwrite the previously referenced values, leading to unexpected results, particularly when using go routines or deferred functions within the loop.\\n\\n## Remediations\\n\\n✅ Create a New Variable Inside the Loop\\n\\nDeclare a new local variable within the loop's scope to hold the iteration value. This ensures a unique address is used for each iteration.\\n\\n\`\`\`go\\nfor _, n := range []someStruct{{1}, {2}, {3}, {4}} {\\n localVar := n\\n // Use localVar instead of n\\n}\\n\`\`\`\\n\\n✅ Use Indexed Addressing\\n\\nInstead of the iteration variable, directly reference the indexed element within the array or slice.\\n\\n\`\`\`go\\nfor i := range mySlice {\\n // Use &mySlice[i] to obtain a stable address\\n}\\n\`\`\`\\n\\n❌ Do Not Store the Address of the Iteration Variable\\n\\nAvoid taking the address of the iteration variable and storing it, as it leads to all references pointing to the same memory location.\\n\\n❌ Avoid Using the Iteration Variable's Address in Goroutines\\n\\nUsing the iteration variable's address directly in goroutines can cause race conditions or logical errors, as the variable's value may change before the goroutine accesses it.\\n\\n## Resources\\n\\n- [Go For Statements](https://go.dev/ref/spec#For_statements)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_memory_memory_aliasing", + "line_number": 54, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 54, + "end": 54, + "column": { + "start": 27, + "end": 29 + } + }, + "sink": { + "start": 54, + "end": 54, + "column": { + "start": 27, + "end": 29 + }, + "content": "&s" + }, + "parent_line_number": 54, + "snippet": "&s", + "fingerprint": "48f24a9e016ac12602af8f93ed78cac9_1", + "old_fingerprint": "f4242521fe214000ea523c0029908b3d_1", + "code_extract": "\\t\\tfmt.Println(\\"%d %p\\", i, &s)" + } + ] +}" +`; diff --git a/tests/go/gosec/memory/memory_aliasing/test.js b/tests/go/gosec/memory/memory_aliasing/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/memory/memory_aliasing/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/memory/memory_aliasing/testdata/main.go b/tests/go/gosec/memory/memory_aliasing/testdata/main.go new file mode 100644 index 000000000..64c7f296e --- /dev/null +++ b/tests/go/gosec/memory/memory_aliasing/testdata/main.go @@ -0,0 +1,56 @@ +package main + +import "fmt" + +var vector []*string + +func appendVector(s *string) { + vector = append(vector, s) +} + +func printVector() { + for _, item := range vector { + fmt.Printf("%s", *item) + } + fmt.Println() +} + +func foo() (int, **string, *string) { + for _, item := range vector { + return 0, &item, item + } + return 0, nil, nil +} + +func appendrange() { + for _, item := range []string{"A", "B", "C"} { + appendVector(&item) + } + + printVector() + + zero, c_star, c := foo() + fmt.Printf("%d %v %s", zero, c_star, c) +} + +func saferange() { + sampleMap := map[string]string{} + sampleString := "A string" + for sampleString, _ = range sampleMap { + fmt.Println(sampleString) + } +} + +func shouldNotBeReported() { + array := []string{"a", "b"} + for _, s := range array { + fmt.Println(s) + } +} + +func shouldBeReported() { + array := []string{"a", "b"} + for i, s := range array { + fmt.Println("%d %p", i, &s) + } +} diff --git a/tests/go/gosec/network/bind_to_all_interfaces/__snapshots__/test.js.snap b/tests/go/gosec/network/bind_to_all_interfaces/__snapshots__/test.js.snap new file mode 100644 index 000000000..4cce35dde --- /dev/null +++ b/tests/go/gosec/network/bind_to_all_interfaces/__snapshots__/test.js.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_network_bind_to_all_interfaces test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "200" + ], + "id": "go_gosec_network_bind_to_all_interfaces", + "title": "Exposure of sensitive information to an unauthorized actor", + "description": "## Description\\n\\nBinding to \\"0.0.0.0\\" allows a service to accept connections on all network interfaces. While this can be useful for services meant to be widely accessible, it can also unintentionally expose the service on network interfaces that are not secure or intended for such traffic, potentially leading to security vulnerabilities.\\n\\n## Remediations\\n\\nTo mitigate the risks associated with binding to all network interfaces:\\n\\n✅ Bind to a Specific Interface\\n\\nConfigure your service to listen on a specific IP address or network interface. This can be controlled through:\\n\\n- **Environment Variable**: Use an environment variable to specify the IP address, making the configuration more flexible and secure.\\n- **Configuration File**: Define the IP address in a configuration file which the application reads at startup.\\n- **Programmatic Identification**: Programmatically determine the appropriate network interface and bind the service to its IP address.\\n\\n\`\`\`go\\nimport (\\n \\"net\\"\\n \\"os\\"\\n \\"log\\"\\n)\\n\\nfunc main() {\\n // Retrieve the IP address from an environment variable\\n addr := os.Getenv(\\"IP_ADDRESS\\")\\n\\n // Listen on the specified interface\\n listener, err := net.Listen(\\"tcp\\", addr)\\n if err != nil {\\n log.Fatalf(\\"Failed to listen on %s: %v\\", addr, err)\\n }\\n\\n // Continue to set up your server (e.g., http.Serve(listener, handler))\\n}\\n\`\`\`\\n\\n✅ **Security Best Practices**: Always follow security best practices when configuring network services, such as using firewalls to restrict access and encrypting traffic with TLS.\\n\\n## Resources\\n\\n- [Go net package](https://pkg.go.dev/net)\\n- [Go os package for environment variables](https://pkg.go.dev/os)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_network_bind_to_all_interfaces", + "line_number": 9, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 9, + "end": 9, + "column": { + "start": 12, + "end": 45 + } + }, + "sink": { + "start": 9, + "end": 9, + "column": { + "start": 12, + "end": 45 + }, + "content": "net.Listen(\\"tcp\\", \\"0.0.0.0:2000\\")" + }, + "parent_line_number": 9, + "snippet": "net.Listen(\\"tcp\\", \\"0.0.0.0:2000\\")", + "fingerprint": "d05a76cad778494c80db77eefff794f0_0", + "old_fingerprint": "88d3e0294f3fbf9ccbfc760132ed18ca_0", + "code_extract": "\\tl, err := net.Listen(\\"tcp\\", \\"0.0.0.0:2000\\")" + } + ] +}" +`; diff --git a/tests/go/gosec/network/bind_to_all_interfaces/test.js b/tests/go/gosec/network/bind_to_all_interfaces/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/network/bind_to_all_interfaces/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/network/bind_to_all_interfaces/testdata/main.go b/tests/go/gosec/network/bind_to_all_interfaces/testdata/main.go new file mode 100644 index 000000000..4ace82cf2 --- /dev/null +++ b/tests/go/gosec/network/bind_to_all_interfaces/testdata/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "log" + "net" +) + +func main() { + l, err := net.Listen("tcp", "0.0.0.0:2000") + if err != nil { + log.Fatal(err) + } + defer l.Close() +} diff --git a/tests/go/gosec/secrets/secrets/__snapshots__/test.js.snap b/tests/go/gosec/secrets/secrets/__snapshots__/test.js.snap new file mode 100644 index 000000000..bd956f48d --- /dev/null +++ b/tests/go/gosec/secrets/secrets/__snapshots__/test.js.snap @@ -0,0 +1,110 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_secrets_secrets test 1`] = ` +"{ + "high": [ + { + "cwe_ids": [ + "798" + ], + "id": "go_gosec_secrets_secrets", + "title": "Use of hard-coded password", + "description": "## Description\\n\\nStoring sensitive information such as secret keys, passwords, or API tokens directly in source code can lead to security vulnerabilities. This practice makes it easy for malicious actors to access these secrets if the codebase is exposed or improperly accessed.\\n\\n## Remediations\\n\\nTo protect sensitive information:\\n\\n✅ Dynamic Secret Retrieval\\n\\nImplement mechanisms to retrieve secrets dynamically at runtime from a secure source rather than hardcoding them in the source files.\\n\\n✅ Environment Variables\\n\\nUse environment variables to inject secrets into the application at runtime, keeping them out of the codebase.\\n\\n✅ Secrets Management Systems\\n\\nUtilize dedicated secrets management tools and services that securely store and manage sensitive information.\\n\\n✅ Encrypted Configuration Files\\n\\nStore secrets in configuration files that are encrypted and decrypt them at runtime within the application.\\n\\n✅ Access Control\\n\\nEnsure that the storage location for secrets has strict access controls to prevent unauthorized access.\\n\\n✅ Audit and Rotate Secrets\\n\\nRegularly audit access to secrets and rotate them to minimize the risk if they are compromised.\\n\\n## Resources\\n\\n- [OWASP: Use of Hard-coded Passwords](https://owasp.org/www-community/vulnerabilities/Use_of_hard-coded_password)\\n- [OWASP: Secrets Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html#21-high-availability)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_secrets_secrets", + "line_number": 11, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 11, + "end": 11, + "column": { + "start": 2, + "end": 59 + } + }, + "sink": { + "start": 11, + "end": 11, + "column": { + "start": 2, + "end": 59 + }, + "content": "var password = \\"f62e5bcda4fae4f82370da0c6f20697b8f8447ef\\"" + }, + "parent_line_number": 11, + "snippet": "var password = \\"f62e5bcda4fae4f82370da0c6f20697b8f8447ef\\"", + "fingerprint": "9e734ddc60109624107631b0392de18d_0", + "old_fingerprint": "ed5cd88d67a7df70d3590175a0307c35_0", + "code_extract": "\\tvar password = \\"f62e5bcda4fae4f82370da0c6f20697b8f8447ef\\"" + }, + { + "cwe_ids": [ + "798" + ], + "id": "go_gosec_secrets_secrets", + "title": "Use of hard-coded password", + "description": "## Description\\n\\nStoring sensitive information such as secret keys, passwords, or API tokens directly in source code can lead to security vulnerabilities. This practice makes it easy for malicious actors to access these secrets if the codebase is exposed or improperly accessed.\\n\\n## Remediations\\n\\nTo protect sensitive information:\\n\\n✅ Dynamic Secret Retrieval\\n\\nImplement mechanisms to retrieve secrets dynamically at runtime from a secure source rather than hardcoding them in the source files.\\n\\n✅ Environment Variables\\n\\nUse environment variables to inject secrets into the application at runtime, keeping them out of the codebase.\\n\\n✅ Secrets Management Systems\\n\\nUtilize dedicated secrets management tools and services that securely store and manage sensitive information.\\n\\n✅ Encrypted Configuration Files\\n\\nStore secrets in configuration files that are encrypted and decrypt them at runtime within the application.\\n\\n✅ Access Control\\n\\nEnsure that the storage location for secrets has strict access controls to prevent unauthorized access.\\n\\n✅ Audit and Rotate Secrets\\n\\nRegularly audit access to secrets and rotate them to minimize the risk if they are compromised.\\n\\n## Resources\\n\\n- [OWASP: Use of Hard-coded Passwords](https://owasp.org/www-community/vulnerabilities/Use_of_hard-coded_password)\\n- [OWASP: Secrets Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html#21-high-availability)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_secrets_secrets", + "line_number": 19, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 19, + "end": 19, + "column": { + "start": 2, + "end": 55 + } + }, + "sink": { + "start": 19, + "end": 19, + "column": { + "start": 2, + "end": 55 + }, + "content": "password = \\"f62e5bcda4fae4f82370da0c6f20697b8f8447ef\\"" + }, + "parent_line_number": 19, + "snippet": "password = \\"f62e5bcda4fae4f82370da0c6f20697b8f8447ef\\"", + "fingerprint": "9e734ddc60109624107631b0392de18d_1", + "old_fingerprint": "ed5cd88d67a7df70d3590175a0307c35_1", + "code_extract": "\\tpassword = \\"f62e5bcda4fae4f82370da0c6f20697b8f8447ef\\"" + }, + { + "cwe_ids": [ + "798" + ], + "id": "go_gosec_secrets_secrets", + "title": "Use of hard-coded password", + "description": "## Description\\n\\nStoring sensitive information such as secret keys, passwords, or API tokens directly in source code can lead to security vulnerabilities. This practice makes it easy for malicious actors to access these secrets if the codebase is exposed or improperly accessed.\\n\\n## Remediations\\n\\nTo protect sensitive information:\\n\\n✅ Dynamic Secret Retrieval\\n\\nImplement mechanisms to retrieve secrets dynamically at runtime from a secure source rather than hardcoding them in the source files.\\n\\n✅ Environment Variables\\n\\nUse environment variables to inject secrets into the application at runtime, keeping them out of the codebase.\\n\\n✅ Secrets Management Systems\\n\\nUtilize dedicated secrets management tools and services that securely store and manage sensitive information.\\n\\n✅ Encrypted Configuration Files\\n\\nStore secrets in configuration files that are encrypted and decrypt them at runtime within the application.\\n\\n✅ Access Control\\n\\nEnsure that the storage location for secrets has strict access controls to prevent unauthorized access.\\n\\n✅ Audit and Rotate Secrets\\n\\nRegularly audit access to secrets and rotate them to minimize the risk if they are compromised.\\n\\n## Resources\\n\\n- [OWASP: Use of Hard-coded Passwords](https://owasp.org/www-community/vulnerabilities/Use_of_hard-coded_password)\\n- [OWASP: Secrets Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html#21-high-availability)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_secrets_secrets", + "line_number": 26, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 26, + "end": 26, + "column": { + "start": 2, + "end": 56 + } + }, + "sink": { + "start": 26, + "end": 26, + "column": { + "start": 2, + "end": 56 + }, + "content": "password := \\"f62e5bcda4fae4f82370da0c6f20697b8f8447ef\\"" + }, + "parent_line_number": 26, + "snippet": "password := \\"f62e5bcda4fae4f82370da0c6f20697b8f8447ef\\"", + "fingerprint": "9e734ddc60109624107631b0392de18d_2", + "old_fingerprint": "ed5cd88d67a7df70d3590175a0307c35_2", + "code_extract": "\\tpassword := \\"f62e5bcda4fae4f82370da0c6f20697b8f8447ef\\"" + } + ] +}" +`; diff --git a/tests/go/gosec/secrets/secrets/test.js b/tests/go/gosec/secrets/secrets/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/secrets/secrets/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/secrets/secrets/testdata/main.go b/tests/go/gosec/secrets/secrets/testdata/main.go new file mode 100644 index 000000000..bd9be3fad --- /dev/null +++ b/tests/go/gosec/secrets/secrets/testdata/main.go @@ -0,0 +1,25 @@ +package main + +import "fmt" + +func bad1() { + username := "admin" + var password = "f62e5bcda4fae4f82370da0c6f20697b8f8447ef" + + fmt.Println("Doing something with: ", username, password) +} + +func bad2() { + username := "admin" + var password string + password = "f62e5bcda4fae4f82370da0c6f20697b8f8447ef" + + fmt.Println("Doing something with: ", username, password) +} + +func bad3() { + username := "admin" + password := "f62e5bcda4fae4f82370da0c6f20697b8f8447ef" + + fmt.Println("Doing something with: ", username, password) +} diff --git a/tests/go/gosec/sql/concat_sqli/__snapshots__/test.js.snap b/tests/go/gosec/sql/concat_sqli/__snapshots__/test.js.snap new file mode 100644 index 000000000..89d3ed992 --- /dev/null +++ b/tests/go/gosec/sql/concat_sqli/__snapshots__/test.js.snap @@ -0,0 +1,321 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_sql_concat_sqli test 1`] = ` +"{ + "high": [ + { + "cwe_ids": [ + "89" + ], + "id": "go_gosec_sql_concat_sqli", + "title": "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')", + "description": "## Description\\n\\nSQL Injection represents a severe vulnerability that can culminate in the compromise of data or entire systems. When SQL query strings are crafted dynamically based on user inputs, there's potential for malicious users to manipulate the logic of the SQL statement. Such tampering could provide adversaries unauthorized access to sensitive data or even allow them to execute system-level operations or code.\\n\\n## Remediations\\n\\n✅ Use Parameterized Queries\\n\\nAlways opt for parameterized queries over dynamically generated SQL queries to prevent SQL injection.\\n\\n\`\`\`go\\nrows, err := db.Query(\\"SELECT * FROM users WHERE userName = ?\\", userName)\\nif err != nil {\\n return nil, err\\n}\\ndefer rows.Close()\\nfor rows.Next() {\\n // ... process rows\\n}\\n\`\`\`\\n\\n✅ Avoid Direct User Input in Dynamic Queries\\n\\nIf there's an absolute need to formulate dynamic queries, ensure that direct user input is never utilized. Instead, leverage a map or dictionary containing valid values and determine them through a user-provided key.\\n\\nFor instance, certain database drivers do not support parameterized queries for operators like \`>\` or \`<\`. Instead of directly using user-input values, allow users to provide substitutes like \`gt\` for \`>\` and \`lt\` for \`<\`. Subsequently, use these alphabetical inputs to retrieve the actual operators for your query. Implement a similar approach for queries requiring non-parameterizable column or table names.\\n\\n## Resources\\n\\n- [OWASP SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_sql_concat_sqli", + "line_number": 18, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 18, + "end": 18, + "column": { + "start": 15, + "end": 71 + } + }, + "sink": { + "start": 18, + "end": 18, + "column": { + "start": 15, + "end": 71 + }, + "content": "db.Query(\\"SELECT * FROM foo WHERE name = \\" + os.Args[1])" + }, + "parent_line_number": 18, + "snippet": "db.Query(\\"SELECT * FROM foo WHERE name = \\" + os.Args[1])", + "fingerprint": "ca1a3415f7df18be8dec85c9d74eb890_0", + "old_fingerprint": "3350ddd230bfd667e45a89b42b9b1b39_0", + "code_extract": "\\trows, err := db.Query(\\"SELECT * FROM foo WHERE name = \\" + os.Args[1])" + }, + { + "cwe_ids": [ + "89" + ], + "id": "go_gosec_sql_concat_sqli", + "title": "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')", + "description": "## Description\\n\\nSQL Injection represents a severe vulnerability that can culminate in the compromise of data or entire systems. When SQL query strings are crafted dynamically based on user inputs, there's potential for malicious users to manipulate the logic of the SQL statement. Such tampering could provide adversaries unauthorized access to sensitive data or even allow them to execute system-level operations or code.\\n\\n## Remediations\\n\\n✅ Use Parameterized Queries\\n\\nAlways opt for parameterized queries over dynamically generated SQL queries to prevent SQL injection.\\n\\n\`\`\`go\\nrows, err := db.Query(\\"SELECT * FROM users WHERE userName = ?\\", userName)\\nif err != nil {\\n return nil, err\\n}\\ndefer rows.Close()\\nfor rows.Next() {\\n // ... process rows\\n}\\n\`\`\`\\n\\n✅ Avoid Direct User Input in Dynamic Queries\\n\\nIf there's an absolute need to formulate dynamic queries, ensure that direct user input is never utilized. Instead, leverage a map or dictionary containing valid values and determine them through a user-provided key.\\n\\nFor instance, certain database drivers do not support parameterized queries for operators like \`>\` or \`<\`. Instead of directly using user-input values, allow users to provide substitutes like \`gt\` for \`>\` and \`lt\` for \`<\`. Subsequently, use these alphabetical inputs to retrieve the actual operators for your query. Implement a similar approach for queries requiring non-parameterizable column or table names.\\n\\n## Resources\\n\\n- [OWASP SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_sql_concat_sqli", + "line_number": 30, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 30, + "end": 30, + "column": { + "start": 15, + "end": 71 + } + }, + "sink": { + "start": 30, + "end": 30, + "column": { + "start": 15, + "end": 71 + }, + "content": "db.Query(\\"select * from foo where name = \\" + os.Args[1])" + }, + "parent_line_number": 30, + "snippet": "db.Query(\\"select * from foo where name = \\" + os.Args[1])", + "fingerprint": "ca1a3415f7df18be8dec85c9d74eb890_1", + "old_fingerprint": "3350ddd230bfd667e45a89b42b9b1b39_1", + "code_extract": "\\trows, err := db.Query(\\"select * from foo where name = \\" + os.Args[1])" + }, + { + "cwe_ids": [ + "89" + ], + "id": "go_gosec_sql_concat_sqli", + "title": "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')", + "description": "## Description\\n\\nSQL Injection represents a severe vulnerability that can culminate in the compromise of data or entire systems. When SQL query strings are crafted dynamically based on user inputs, there's potential for malicious users to manipulate the logic of the SQL statement. Such tampering could provide adversaries unauthorized access to sensitive data or even allow them to execute system-level operations or code.\\n\\n## Remediations\\n\\n✅ Use Parameterized Queries\\n\\nAlways opt for parameterized queries over dynamically generated SQL queries to prevent SQL injection.\\n\\n\`\`\`go\\nrows, err := db.Query(\\"SELECT * FROM users WHERE userName = ?\\", userName)\\nif err != nil {\\n return nil, err\\n}\\ndefer rows.Close()\\nfor rows.Next() {\\n // ... process rows\\n}\\n\`\`\`\\n\\n✅ Avoid Direct User Input in Dynamic Queries\\n\\nIf there's an absolute need to formulate dynamic queries, ensure that direct user input is never utilized. Instead, leverage a map or dictionary containing valid values and determine them through a user-provided key.\\n\\nFor instance, certain database drivers do not support parameterized queries for operators like \`>\` or \`<\`. Instead of directly using user-input values, allow users to provide substitutes like \`gt\` for \`>\` and \`lt\` for \`<\`. Subsequently, use these alphabetical inputs to retrieve the actual operators for your query. Implement a similar approach for queries requiring non-parameterizable column or table names.\\n\\n## Resources\\n\\n- [OWASP SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_sql_concat_sqli", + "line_number": 42, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 42, + "end": 42, + "column": { + "start": 15, + "end": 98 + } + }, + "sink": { + "start": 42, + "end": 42, + "column": { + "start": 15, + "end": 98 + }, + "content": "db.QueryContext(context.Background(), \\"select * from foo where name = \\"+os.Args[1])" + }, + "parent_line_number": 42, + "snippet": "db.QueryContext(context.Background(), \\"select * from foo where name = \\"+os.Args[1])", + "fingerprint": "ca1a3415f7df18be8dec85c9d74eb890_2", + "old_fingerprint": "3350ddd230bfd667e45a89b42b9b1b39_2", + "code_extract": "\\trows, err := db.QueryContext(context.Background(), \\"select * from foo where name = \\"+os.Args[1])" + }, + { + "cwe_ids": [ + "89" + ], + "id": "go_gosec_sql_concat_sqli", + "title": "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')", + "description": "## Description\\n\\nSQL Injection represents a severe vulnerability that can culminate in the compromise of data or entire systems. When SQL query strings are crafted dynamically based on user inputs, there's potential for malicious users to manipulate the logic of the SQL statement. Such tampering could provide adversaries unauthorized access to sensitive data or even allow them to execute system-level operations or code.\\n\\n## Remediations\\n\\n✅ Use Parameterized Queries\\n\\nAlways opt for parameterized queries over dynamically generated SQL queries to prevent SQL injection.\\n\\n\`\`\`go\\nrows, err := db.Query(\\"SELECT * FROM users WHERE userName = ?\\", userName)\\nif err != nil {\\n return nil, err\\n}\\ndefer rows.Close()\\nfor rows.Next() {\\n // ... process rows\\n}\\n\`\`\`\\n\\n✅ Avoid Direct User Input in Dynamic Queries\\n\\nIf there's an absolute need to formulate dynamic queries, ensure that direct user input is never utilized. Instead, leverage a map or dictionary containing valid values and determine them through a user-provided key.\\n\\nFor instance, certain database drivers do not support parameterized queries for operators like \`>\` or \`<\`. Instead of directly using user-input values, allow users to provide substitutes like \`gt\` for \`>\` and \`lt\` for \`<\`. Subsequently, use these alphabetical inputs to retrieve the actual operators for your query. Implement a similar approach for queries requiring non-parameterizable column or table names.\\n\\n## Resources\\n\\n- [OWASP SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_sql_concat_sqli", + "line_number": 59, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 59, + "end": 59, + "column": { + "start": 15, + "end": 98 + } + }, + "sink": { + "start": 59, + "end": 59, + "column": { + "start": 15, + "end": 98 + }, + "content": "tx.QueryContext(context.Background(), \\"select * from foo where name = \\"+os.Args[1])" + }, + "parent_line_number": 59, + "snippet": "tx.QueryContext(context.Background(), \\"select * from foo where name = \\"+os.Args[1])", + "fingerprint": "ca1a3415f7df18be8dec85c9d74eb890_3", + "old_fingerprint": "3350ddd230bfd667e45a89b42b9b1b39_3", + "code_extract": "\\trows, err := tx.QueryContext(context.Background(), \\"select * from foo where name = \\"+os.Args[1])" + }, + { + "cwe_ids": [ + "89" + ], + "id": "go_gosec_sql_concat_sqli", + "title": "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')", + "description": "## Description\\n\\nSQL Injection represents a severe vulnerability that can culminate in the compromise of data or entire systems. When SQL query strings are crafted dynamically based on user inputs, there's potential for malicious users to manipulate the logic of the SQL statement. Such tampering could provide adversaries unauthorized access to sensitive data or even allow them to execute system-level operations or code.\\n\\n## Remediations\\n\\n✅ Use Parameterized Queries\\n\\nAlways opt for parameterized queries over dynamically generated SQL queries to prevent SQL injection.\\n\\n\`\`\`go\\nrows, err := db.Query(\\"SELECT * FROM users WHERE userName = ?\\", userName)\\nif err != nil {\\n return nil, err\\n}\\ndefer rows.Close()\\nfor rows.Next() {\\n // ... process rows\\n}\\n\`\`\`\\n\\n✅ Avoid Direct User Input in Dynamic Queries\\n\\nIf there's an absolute need to formulate dynamic queries, ensure that direct user input is never utilized. Instead, leverage a map or dictionary containing valid values and determine them through a user-provided key.\\n\\nFor instance, certain database drivers do not support parameterized queries for operators like \`>\` or \`<\`. Instead of directly using user-input values, allow users to provide substitutes like \`gt\` for \`>\` and \`lt\` for \`<\`. Subsequently, use these alphabetical inputs to retrieve the actual operators for your query. Implement a similar approach for queries requiring non-parameterizable column or table names.\\n\\n## Resources\\n\\n- [OWASP SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_sql_concat_sqli", + "line_number": 74, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 74, + "end": 74, + "column": { + "start": 15, + "end": 75 + } + }, + "sink": { + "start": 74, + "end": 74, + "column": { + "start": 15, + "end": 75 + }, + "content": "db.Query(\\"SELECT * FROM foo\\" + \\"WHERE name = \\" + os.Args[1])" + }, + "parent_line_number": 74, + "snippet": "db.Query(\\"SELECT * FROM foo\\" + \\"WHERE name = \\" + os.Args[1])", + "fingerprint": "ca1a3415f7df18be8dec85c9d74eb890_4", + "old_fingerprint": "3350ddd230bfd667e45a89b42b9b1b39_4", + "code_extract": "\\trows, err := db.Query(\\"SELECT * FROM foo\\" + \\"WHERE name = \\" + os.Args[1])" + } + ] +}" +`; + +exports[`go_gosec_sql_concat_sqli test 2 1`] = ` +"{ + "high": [ + { + "cwe_ids": [ + "89" + ], + "id": "go_gosec_sql_concat_sqli", + "title": "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')", + "description": "## Description\\n\\nSQL Injection represents a severe vulnerability that can culminate in the compromise of data or entire systems. When SQL query strings are crafted dynamically based on user inputs, there's potential for malicious users to manipulate the logic of the SQL statement. Such tampering could provide adversaries unauthorized access to sensitive data or even allow them to execute system-level operations or code.\\n\\n## Remediations\\n\\n✅ Use Parameterized Queries\\n\\nAlways opt for parameterized queries over dynamically generated SQL queries to prevent SQL injection.\\n\\n\`\`\`go\\nrows, err := db.Query(\\"SELECT * FROM users WHERE userName = ?\\", userName)\\nif err != nil {\\n return nil, err\\n}\\ndefer rows.Close()\\nfor rows.Next() {\\n // ... process rows\\n}\\n\`\`\`\\n\\n✅ Avoid Direct User Input in Dynamic Queries\\n\\nIf there's an absolute need to formulate dynamic queries, ensure that direct user input is never utilized. Instead, leverage a map or dictionary containing valid values and determine them through a user-provided key.\\n\\nFor instance, certain database drivers do not support parameterized queries for operators like \`>\` or \`<\`. Instead of directly using user-input values, allow users to provide substitutes like \`gt\` for \`>\` and \`lt\` for \`<\`. Subsequently, use these alphabetical inputs to retrieve the actual operators for your query. Implement a similar approach for queries requiring non-parameterizable column or table names.\\n\\n## Resources\\n\\n- [OWASP SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_sql_concat_sqli", + "line_number": 18, + "full_filename": "/tmp/bearer-scan/main2.go", + "filename": ".", + "source": { + "start": 18, + "end": 18, + "column": { + "start": 15, + "end": 26 + } + }, + "sink": { + "start": 18, + "end": 18, + "column": { + "start": 15, + "end": 26 + }, + "content": "db.Query(q)" + }, + "parent_line_number": 18, + "snippet": "db.Query(q)", + "fingerprint": "ca1a3415f7df18be8dec85c9d74eb890_0", + "old_fingerprint": "a93104166356c0a2709944f5e9659abf_0", + "code_extract": "\\trows, err := db.Query(q)" + }, + { + "cwe_ids": [ + "89" + ], + "id": "go_gosec_sql_concat_sqli", + "title": "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')", + "description": "## Description\\n\\nSQL Injection represents a severe vulnerability that can culminate in the compromise of data or entire systems. When SQL query strings are crafted dynamically based on user inputs, there's potential for malicious users to manipulate the logic of the SQL statement. Such tampering could provide adversaries unauthorized access to sensitive data or even allow them to execute system-level operations or code.\\n\\n## Remediations\\n\\n✅ Use Parameterized Queries\\n\\nAlways opt for parameterized queries over dynamically generated SQL queries to prevent SQL injection.\\n\\n\`\`\`go\\nrows, err := db.Query(\\"SELECT * FROM users WHERE userName = ?\\", userName)\\nif err != nil {\\n return nil, err\\n}\\ndefer rows.Close()\\nfor rows.Next() {\\n // ... process rows\\n}\\n\`\`\`\\n\\n✅ Avoid Direct User Input in Dynamic Queries\\n\\nIf there's an absolute need to formulate dynamic queries, ensure that direct user input is never utilized. Instead, leverage a map or dictionary containing valid values and determine them through a user-provided key.\\n\\nFor instance, certain database drivers do not support parameterized queries for operators like \`>\` or \`<\`. Instead of directly using user-input values, allow users to provide substitutes like \`gt\` for \`>\` and \`lt\` for \`<\`. Subsequently, use these alphabetical inputs to retrieve the actual operators for your query. Implement a similar approach for queries requiring non-parameterizable column or table names.\\n\\n## Resources\\n\\n- [OWASP SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_sql_concat_sqli", + "line_number": 31, + "full_filename": "/tmp/bearer-scan/main2.go", + "filename": ".", + "source": { + "start": 31, + "end": 31, + "column": { + "start": 15, + "end": 26 + } + }, + "sink": { + "start": 31, + "end": 31, + "column": { + "start": 15, + "end": 26 + }, + "content": "db.Query(q)" + }, + "parent_line_number": 31, + "snippet": "db.Query(q)", + "fingerprint": "ca1a3415f7df18be8dec85c9d74eb890_1", + "old_fingerprint": "a93104166356c0a2709944f5e9659abf_1", + "code_extract": "\\trows, err := db.Query(q)" + }, + { + "cwe_ids": [ + "89" + ], + "id": "go_gosec_sql_concat_sqli", + "title": "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')", + "description": "## Description\\n\\nSQL Injection represents a severe vulnerability that can culminate in the compromise of data or entire systems. When SQL query strings are crafted dynamically based on user inputs, there's potential for malicious users to manipulate the logic of the SQL statement. Such tampering could provide adversaries unauthorized access to sensitive data or even allow them to execute system-level operations or code.\\n\\n## Remediations\\n\\n✅ Use Parameterized Queries\\n\\nAlways opt for parameterized queries over dynamically generated SQL queries to prevent SQL injection.\\n\\n\`\`\`go\\nrows, err := db.Query(\\"SELECT * FROM users WHERE userName = ?\\", userName)\\nif err != nil {\\n return nil, err\\n}\\ndefer rows.Close()\\nfor rows.Next() {\\n // ... process rows\\n}\\n\`\`\`\\n\\n✅ Avoid Direct User Input in Dynamic Queries\\n\\nIf there's an absolute need to formulate dynamic queries, ensure that direct user input is never utilized. Instead, leverage a map or dictionary containing valid values and determine them through a user-provided key.\\n\\nFor instance, certain database drivers do not support parameterized queries for operators like \`>\` or \`<\`. Instead of directly using user-input values, allow users to provide substitutes like \`gt\` for \`>\` and \`lt\` for \`<\`. Subsequently, use these alphabetical inputs to retrieve the actual operators for your query. Implement a similar approach for queries requiring non-parameterizable column or table names.\\n\\n## Resources\\n\\n- [OWASP SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_sql_concat_sqli", + "line_number": 44, + "full_filename": "/tmp/bearer-scan/main2.go", + "filename": ".", + "source": { + "start": 44, + "end": 44, + "column": { + "start": 15, + "end": 55 + } + }, + "sink": { + "start": 44, + "end": 44, + "column": { + "start": 15, + "end": 55 + }, + "content": "db.QueryContext(context.Background(), q)" + }, + "parent_line_number": 44, + "snippet": "db.QueryContext(context.Background(), q)", + "fingerprint": "ca1a3415f7df18be8dec85c9d74eb890_2", + "old_fingerprint": "a93104166356c0a2709944f5e9659abf_2", + "code_extract": "\\trows, err := db.QueryContext(context.Background(), q)" + }, + { + "cwe_ids": [ + "89" + ], + "id": "go_gosec_sql_concat_sqli", + "title": "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')", + "description": "## Description\\n\\nSQL Injection represents a severe vulnerability that can culminate in the compromise of data or entire systems. When SQL query strings are crafted dynamically based on user inputs, there's potential for malicious users to manipulate the logic of the SQL statement. Such tampering could provide adversaries unauthorized access to sensitive data or even allow them to execute system-level operations or code.\\n\\n## Remediations\\n\\n✅ Use Parameterized Queries\\n\\nAlways opt for parameterized queries over dynamically generated SQL queries to prevent SQL injection.\\n\\n\`\`\`go\\nrows, err := db.Query(\\"SELECT * FROM users WHERE userName = ?\\", userName)\\nif err != nil {\\n return nil, err\\n}\\ndefer rows.Close()\\nfor rows.Next() {\\n // ... process rows\\n}\\n\`\`\`\\n\\n✅ Avoid Direct User Input in Dynamic Queries\\n\\nIf there's an absolute need to formulate dynamic queries, ensure that direct user input is never utilized. Instead, leverage a map or dictionary containing valid values and determine them through a user-provided key.\\n\\nFor instance, certain database drivers do not support parameterized queries for operators like \`>\` or \`<\`. Instead of directly using user-input values, allow users to provide substitutes like \`gt\` for \`>\` and \`lt\` for \`<\`. Subsequently, use these alphabetical inputs to retrieve the actual operators for your query. Implement a similar approach for queries requiring non-parameterizable column or table names.\\n\\n## Resources\\n\\n- [OWASP SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_sql_concat_sqli", + "line_number": 62, + "full_filename": "/tmp/bearer-scan/main2.go", + "filename": ".", + "source": { + "start": 62, + "end": 62, + "column": { + "start": 15, + "end": 55 + } + }, + "sink": { + "start": 62, + "end": 62, + "column": { + "start": 15, + "end": 55 + }, + "content": "tx.QueryContext(context.Background(), q)" + }, + "parent_line_number": 62, + "snippet": "tx.QueryContext(context.Background(), q)", + "fingerprint": "ca1a3415f7df18be8dec85c9d74eb890_3", + "old_fingerprint": "a93104166356c0a2709944f5e9659abf_3", + "code_extract": "\\trows, err := tx.QueryContext(context.Background(), q)" + } + ] +}" +`; diff --git a/tests/go/gosec/sql/concat_sqli/test.js b/tests/go/gosec/sql/concat_sqli/test.js new file mode 100644 index 000000000..feb638403 --- /dev/null +++ b/tests/go/gosec/sql/concat_sqli/test.js @@ -0,0 +1,16 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) + + test("test 2", () => { + const testCase = "main2.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/sql/concat_sqli/testdata/main.go b/tests/go/gosec/sql/concat_sqli/testdata/main.go new file mode 100644 index 000000000..c4b38d2e6 --- /dev/null +++ b/tests/go/gosec/sql/concat_sqli/testdata/main.go @@ -0,0 +1,129 @@ +package main + +import ( + "context" + "database/sql" + "os" +) + +func foo1() { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + panic(err) + } + rows, err := db.Query("SELECT * FROM foo WHERE name = " + os.Args[1]) + if err != nil { + panic(err) + } + defer rows.Close() +} + +func foo2() { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + panic(err) + } + rows, err := db.Query("select * from foo where name = " + os.Args[1]) + if err != nil { + panic(err) + } + defer rows.Close() +} + +func foo3() { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + panic(err) + } + rows, err := db.QueryContext(context.Background(), "select * from foo where name = "+os.Args[1]) + if err != nil { + panic(err) + } + defer rows.Close() +} + +func foo4() { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + panic(err) + } + tx, err := db.Begin() + if err != nil { + panic(err) + } + defer tx.Rollback() + rows, err := tx.QueryContext(context.Background(), "select * from foo where name = "+os.Args[1]) + if err != nil { + panic(err) + } + defer rows.Close() + if err := tx.Commit(); err != nil { + panic(err) + } +} + +func foo5() { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + panic(err) + } + rows, err := db.Query("SELECT * FROM foo" + "WHERE name = " + os.Args[1]) + if err != nil { + panic(err) + } + defer rows.Close() +} + +func foo6() { + var staticQuery1 = "SELECT * FROM foo WHERE age < " + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + panic(err) + } + rows, err := db.Query(staticQuery1 + "32") + if err != nil { + panic(err) + } + defer rows.Close() +} + +const age = "32" +const gender = "M" + +func foo7() { + var staticQuery2 = "SELECT * FROM foo WHERE age < " + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + panic(err) + } + rows, err := db.Query(staticQuery2 + age) + if err != nil { + panic(err) + } + defer rows.Close() +} + +func foo8() { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + panic(err) + } + rows, err := db.Query("SELECT * FROM foo WHERE gender = " + gender) + if err != nil { + panic(err) + } + defer rows.Close() +} + +func foo9() { + gender := os.Args[0] + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + panic(err) + } + rows, err := db.Query("SELECT * FROM foo WHERE gender = ?", gender) + if err != nil { + panic(err) + } + defer rows.Close() +} diff --git a/tests/go/gosec/sql/concat_sqli/testdata/main2.go b/tests/go/gosec/sql/concat_sqli/testdata/main2.go new file mode 100644 index 000000000..d7e291197 --- /dev/null +++ b/tests/go/gosec/sql/concat_sqli/testdata/main2.go @@ -0,0 +1,127 @@ +package main + +import ( + "context" + "database/sql" + "fmt" + "os" + + "github.com/lib/pq" +) + +func foo1() { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + panic(err) + } + q := fmt.Sprintf("SELECT * FROM foo where name = '%s'", os.Args[1]) + rows, err := db.Query(q) + if err != nil { + panic(err) + } + defer rows.Close() +} + +func foo2() { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + panic(err) + } + q := fmt.Sprintf("select * from foo where name = '%s'", os.Args[1]) + rows, err := db.Query(q) + if err != nil { + panic(err) + } + defer rows.Close() +} + +func foo3() { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + panic(err) + } + q := fmt.Sprintf("select * from foo where name = '%s'", os.Args[1]) + rows, err := db.QueryContext(context.Background(), q) + if err != nil { + panic(err) + } + defer rows.Close() +} + +func foo4() { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + panic(err) + } + tx, err := db.Begin() + if err != nil { + panic(err) + } + defer tx.Rollback() + q := fmt.Sprintf("select * from foo where name = '%s'", os.Args[1]) + rows, err := tx.QueryContext(context.Background(), q) + if err != nil { + panic(err) + } + defer rows.Close() + if err := tx.Commit(); err != nil { + panic(err) + } +} + +func foo5() { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + panic(err) + } + q := fmt.Sprintf("SELECT * FROM foo where id = %d", os.Args[1]) + rows, err := db.Query(q) + if err != nil { + panic(err) + } + defer rows.Close() +} + +func foo6() { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + panic(err) + } + rows, err := db.Query(staticQuery) + if err != nil { + panic(err) + } + defer rows.Close() +} + +func foo7() { + db, err := sql.Open("postgres", "localhost") + if err != nil { + panic(err) + } + q := fmt.Sprintf("SELECT * FROM %s where id = 1", pq.QuoteIdentifier(os.Args[1])) + rows, err := db.Query(q) + if err != nil { + panic(err) + } + defer rows.Close() +} + +const Table = "foo" + +func foo8() { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + panic(err) + } + q := fmt.Sprintf("SELECT * FROM %s where id = 1", Table) + rows, err := db.Query(q) + if err != nil { + panic(err) + } + defer rows.Close() +} + +func main() { + fmt.Sprintln() +} diff --git a/tests/go/gosec/subproc/subproc/__snapshots__/test.js.snap b/tests/go/gosec/subproc/subproc/__snapshots__/test.js.snap new file mode 100644 index 000000000..3ee5e6d4a --- /dev/null +++ b/tests/go/gosec/subproc/subproc/__snapshots__/test.js.snap @@ -0,0 +1,280 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_subproc_subproc test 1`] = ` +"{ + "high": [ + { + "cwe_ids": [ + "95" + ], + "id": "go_gosec_subproc_subproc", + "title": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", + "description": "## Description\\n\\nOS command injection is a perilous vulnerability that has the potential to lead to full system compromise. Adversaries may exploit this flaw by feeding arbitrary commands or arguments intended for execution. This opens the door for unchecked operations, which could wreak havoc on the system or reveal sensitive information.\\n\\n## Remediations\\n\\n✅ Avoid User Input in OS Commands\\n\\nAlways steer clear of incorporating user input when formulating commands or their arguments, especially for functions responsible for OS command execution. This includes, but is not limited to, filenames provided during user uploads/downloads.\\n\\n✅ Hardcoded Argument Set\\n\\nEnsure your application exclusively uses a hardcoded set of arguments for OS command executions. If filenames are being passed to such functions, consider adopting a hash of the filename or another distinctive identifier.\\n\\n✅ Opt for Native Libraries\\n\\nDue to the inherent risks associated with third-party commands and the possibility of undisclosed attack vectors, prefer using native libraries that offer the same capabilities as opposed to resorting to OS system commands.\\n\\n✅ Specify Full Path in Windows\\n\\nIf the environment is Windows-based, always provide the complete path information when denoting the OS command. This circumvents potential vulnerabilities stemming from untrusted search paths (CWE-426).\\n\\n\`\`\`go\\nuserData := []byte(\\"user data\\")\\n// create a temporary file in the application-specific directory\\nf, err := ioutil.TempFile(\\"/var/app/restricted\\", \\"temp-*.dat\\")\\nif err != nil {\\n log.Fatal(err)\\n}\\n\\nif _, err := f.Write(userData); err != nil {\\n log.Fatal(err)\\n}\\n\\nif err := f.Close(); err != nil {\\n log.Fatal(err)\\n}\\n\\n// use the absolute path to the binary and the name of the temporary file\\n// steering clear of any user-provided filenames\\nout, err := exec.Command(\\"/bin/cat\\", f.Name()).Output()\\nif err != nil {\\n log.Fatal(err)\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_subproc_subproc", + "line_number": 25, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 25, + "end": 25, + "column": { + "start": 9, + "end": 67 + } + }, + "sink": { + "start": 25, + "end": 25, + "column": { + "start": 9, + "end": 67 + }, + "content": "exec.CommandContext(context.Background(), os.Args[0], \\"5\\")" + }, + "parent_line_number": 25, + "snippet": "exec.CommandContext(context.Background(), os.Args[0], \\"5\\")", + "fingerprint": "9f7b927d8c9e1a6c92e17fb2d6db3b18_0", + "old_fingerprint": "c7b747c46d0e283c15b7386a8c801ea8_0", + "code_extract": "\\terr := exec.CommandContext(context.Background(), os.Args[0], \\"5\\").Run()" + }, + { + "cwe_ids": [ + "95" + ], + "id": "go_gosec_subproc_subproc", + "title": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", + "description": "## Description\\n\\nOS command injection is a perilous vulnerability that has the potential to lead to full system compromise. Adversaries may exploit this flaw by feeding arbitrary commands or arguments intended for execution. This opens the door for unchecked operations, which could wreak havoc on the system or reveal sensitive information.\\n\\n## Remediations\\n\\n✅ Avoid User Input in OS Commands\\n\\nAlways steer clear of incorporating user input when formulating commands or their arguments, especially for functions responsible for OS command execution. This includes, but is not limited to, filenames provided during user uploads/downloads.\\n\\n✅ Hardcoded Argument Set\\n\\nEnsure your application exclusively uses a hardcoded set of arguments for OS command executions. If filenames are being passed to such functions, consider adopting a hash of the filename or another distinctive identifier.\\n\\n✅ Opt for Native Libraries\\n\\nDue to the inherent risks associated with third-party commands and the possibility of undisclosed attack vectors, prefer using native libraries that offer the same capabilities as opposed to resorting to OS system commands.\\n\\n✅ Specify Full Path in Windows\\n\\nIf the environment is Windows-based, always provide the complete path information when denoting the OS command. This circumvents potential vulnerabilities stemming from untrusted search paths (CWE-426).\\n\\n\`\`\`go\\nuserData := []byte(\\"user data\\")\\n// create a temporary file in the application-specific directory\\nf, err := ioutil.TempFile(\\"/var/app/restricted\\", \\"temp-*.dat\\")\\nif err != nil {\\n log.Fatal(err)\\n}\\n\\nif _, err := f.Write(userData); err != nil {\\n log.Fatal(err)\\n}\\n\\nif err := f.Close(); err != nil {\\n log.Fatal(err)\\n}\\n\\n// use the absolute path to the binary and the name of the temporary file\\n// steering clear of any user-provided filenames\\nout, err := exec.Command(\\"/bin/cat\\", f.Name()).Output()\\nif err != nil {\\n log.Fatal(err)\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_subproc_subproc", + "line_number": 34, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 34, + "end": 34, + "column": { + "start": 9, + "end": 31 + } + }, + "sink": { + "start": 34, + "end": 34, + "column": { + "start": 9, + "end": 31 + }, + "content": "exec.Command(run, \\"5\\")" + }, + "parent_line_number": 34, + "snippet": "exec.Command(run, \\"5\\")", + "fingerprint": "9f7b927d8c9e1a6c92e17fb2d6db3b18_1", + "old_fingerprint": "c7b747c46d0e283c15b7386a8c801ea8_1", + "code_extract": "\\tcmd := exec.Command(run, \\"5\\")" + }, + { + "cwe_ids": [ + "95" + ], + "id": "go_gosec_subproc_subproc", + "title": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", + "description": "## Description\\n\\nOS command injection is a perilous vulnerability that has the potential to lead to full system compromise. Adversaries may exploit this flaw by feeding arbitrary commands or arguments intended for execution. This opens the door for unchecked operations, which could wreak havoc on the system or reveal sensitive information.\\n\\n## Remediations\\n\\n✅ Avoid User Input in OS Commands\\n\\nAlways steer clear of incorporating user input when formulating commands or their arguments, especially for functions responsible for OS command execution. This includes, but is not limited to, filenames provided during user uploads/downloads.\\n\\n✅ Hardcoded Argument Set\\n\\nEnsure your application exclusively uses a hardcoded set of arguments for OS command executions. If filenames are being passed to such functions, consider adopting a hash of the filename or another distinctive identifier.\\n\\n✅ Opt for Native Libraries\\n\\nDue to the inherent risks associated with third-party commands and the possibility of undisclosed attack vectors, prefer using native libraries that offer the same capabilities as opposed to resorting to OS system commands.\\n\\n✅ Specify Full Path in Windows\\n\\nIf the environment is Windows-based, always provide the complete path information when denoting the OS command. This circumvents potential vulnerabilities stemming from untrusted search paths (CWE-426).\\n\\n\`\`\`go\\nuserData := []byte(\\"user data\\")\\n// create a temporary file in the application-specific directory\\nf, err := ioutil.TempFile(\\"/var/app/restricted\\", \\"temp-*.dat\\")\\nif err != nil {\\n log.Fatal(err)\\n}\\n\\nif _, err := f.Write(userData); err != nil {\\n log.Fatal(err)\\n}\\n\\nif err := f.Close(); err != nil {\\n log.Fatal(err)\\n}\\n\\n// use the absolute path to the binary and the name of the temporary file\\n// steering clear of any user-provided filenames\\nout, err := exec.Command(\\"/bin/cat\\", f.Name()).Output()\\nif err != nil {\\n log.Fatal(err)\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_subproc_subproc", + "line_number": 45, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 45, + "end": 45, + "column": { + "start": 9, + "end": 35 + } + }, + "sink": { + "start": 45, + "end": 45, + "column": { + "start": 9, + "end": 35 + }, + "content": "exec.Command(command, \\"5\\")" + }, + "parent_line_number": 45, + "snippet": "exec.Command(command, \\"5\\")", + "fingerprint": "9f7b927d8c9e1a6c92e17fb2d6db3b18_2", + "old_fingerprint": "c7b747c46d0e283c15b7386a8c801ea8_2", + "code_extract": "\\tcmd := exec.Command(command, \\"5\\")" + }, + { + "cwe_ids": [ + "95" + ], + "id": "go_gosec_subproc_subproc", + "title": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", + "description": "## Description\\n\\nOS command injection is a perilous vulnerability that has the potential to lead to full system compromise. Adversaries may exploit this flaw by feeding arbitrary commands or arguments intended for execution. This opens the door for unchecked operations, which could wreak havoc on the system or reveal sensitive information.\\n\\n## Remediations\\n\\n✅ Avoid User Input in OS Commands\\n\\nAlways steer clear of incorporating user input when formulating commands or their arguments, especially for functions responsible for OS command execution. This includes, but is not limited to, filenames provided during user uploads/downloads.\\n\\n✅ Hardcoded Argument Set\\n\\nEnsure your application exclusively uses a hardcoded set of arguments for OS command executions. If filenames are being passed to such functions, consider adopting a hash of the filename or another distinctive identifier.\\n\\n✅ Opt for Native Libraries\\n\\nDue to the inherent risks associated with third-party commands and the possibility of undisclosed attack vectors, prefer using native libraries that offer the same capabilities as opposed to resorting to OS system commands.\\n\\n✅ Specify Full Path in Windows\\n\\nIf the environment is Windows-based, always provide the complete path information when denoting the OS command. This circumvents potential vulnerabilities stemming from untrusted search paths (CWE-426).\\n\\n\`\`\`go\\nuserData := []byte(\\"user data\\")\\n// create a temporary file in the application-specific directory\\nf, err := ioutil.TempFile(\\"/var/app/restricted\\", \\"temp-*.dat\\")\\nif err != nil {\\n log.Fatal(err)\\n}\\n\\nif _, err := f.Write(userData); err != nil {\\n log.Fatal(err)\\n}\\n\\nif err := f.Close(); err != nil {\\n log.Fatal(err)\\n}\\n\\n// use the absolute path to the binary and the name of the temporary file\\n// steering clear of any user-provided filenames\\nout, err := exec.Command(\\"/bin/cat\\", f.Name()).Output()\\nif err != nil {\\n log.Fatal(err)\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_subproc_subproc", + "line_number": 59, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 59, + "end": 59, + "column": { + "start": 9, + "end": 24 + } + }, + "sink": { + "start": 59, + "end": 59, + "column": { + "start": 9, + "end": 24 + }, + "content": "exec.Command(c)" + }, + "parent_line_number": 59, + "snippet": "exec.Command(c)", + "fingerprint": "9f7b927d8c9e1a6c92e17fb2d6db3b18_3", + "old_fingerprint": "c7b747c46d0e283c15b7386a8c801ea8_3", + "code_extract": "\\tcmd := exec.Command(c)" + }, + { + "cwe_ids": [ + "95" + ], + "id": "go_gosec_subproc_subproc", + "title": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", + "description": "## Description\\n\\nOS command injection is a perilous vulnerability that has the potential to lead to full system compromise. Adversaries may exploit this flaw by feeding arbitrary commands or arguments intended for execution. This opens the door for unchecked operations, which could wreak havoc on the system or reveal sensitive information.\\n\\n## Remediations\\n\\n✅ Avoid User Input in OS Commands\\n\\nAlways steer clear of incorporating user input when formulating commands or their arguments, especially for functions responsible for OS command execution. This includes, but is not limited to, filenames provided during user uploads/downloads.\\n\\n✅ Hardcoded Argument Set\\n\\nEnsure your application exclusively uses a hardcoded set of arguments for OS command executions. If filenames are being passed to such functions, consider adopting a hash of the filename or another distinctive identifier.\\n\\n✅ Opt for Native Libraries\\n\\nDue to the inherent risks associated with third-party commands and the possibility of undisclosed attack vectors, prefer using native libraries that offer the same capabilities as opposed to resorting to OS system commands.\\n\\n✅ Specify Full Path in Windows\\n\\nIf the environment is Windows-based, always provide the complete path information when denoting the OS command. This circumvents potential vulnerabilities stemming from untrusted search paths (CWE-426).\\n\\n\`\`\`go\\nuserData := []byte(\\"user data\\")\\n// create a temporary file in the application-specific directory\\nf, err := ioutil.TempFile(\\"/var/app/restricted\\", \\"temp-*.dat\\")\\nif err != nil {\\n log.Fatal(err)\\n}\\n\\nif _, err := f.Write(userData); err != nil {\\n log.Fatal(err)\\n}\\n\\nif err := f.Close(); err != nil {\\n log.Fatal(err)\\n}\\n\\n// use the absolute path to the binary and the name of the temporary file\\n// steering clear of any user-provided filenames\\nout, err := exec.Command(\\"/bin/cat\\", f.Name()).Output()\\nif err != nil {\\n log.Fatal(err)\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_subproc_subproc", + "line_number": 66, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 66, + "end": 66, + "column": { + "start": 8, + "end": 23 + } + }, + "sink": { + "start": 66, + "end": 66, + "column": { + "start": 8, + "end": 23 + }, + "content": "exec.Command(a)" + }, + "parent_line_number": 66, + "snippet": "exec.Command(a)", + "fingerprint": "9f7b927d8c9e1a6c92e17fb2d6db3b18_4", + "old_fingerprint": "c7b747c46d0e283c15b7386a8c801ea8_4", + "code_extract": "\\tcmd = exec.Command(a)" + }, + { + "cwe_ids": [ + "95" + ], + "id": "go_gosec_subproc_subproc", + "title": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", + "description": "## Description\\n\\nOS command injection is a perilous vulnerability that has the potential to lead to full system compromise. Adversaries may exploit this flaw by feeding arbitrary commands or arguments intended for execution. This opens the door for unchecked operations, which could wreak havoc on the system or reveal sensitive information.\\n\\n## Remediations\\n\\n✅ Avoid User Input in OS Commands\\n\\nAlways steer clear of incorporating user input when formulating commands or their arguments, especially for functions responsible for OS command execution. This includes, but is not limited to, filenames provided during user uploads/downloads.\\n\\n✅ Hardcoded Argument Set\\n\\nEnsure your application exclusively uses a hardcoded set of arguments for OS command executions. If filenames are being passed to such functions, consider adopting a hash of the filename or another distinctive identifier.\\n\\n✅ Opt for Native Libraries\\n\\nDue to the inherent risks associated with third-party commands and the possibility of undisclosed attack vectors, prefer using native libraries that offer the same capabilities as opposed to resorting to OS system commands.\\n\\n✅ Specify Full Path in Windows\\n\\nIf the environment is Windows-based, always provide the complete path information when denoting the OS command. This circumvents potential vulnerabilities stemming from untrusted search paths (CWE-426).\\n\\n\`\`\`go\\nuserData := []byte(\\"user data\\")\\n// create a temporary file in the application-specific directory\\nf, err := ioutil.TempFile(\\"/var/app/restricted\\", \\"temp-*.dat\\")\\nif err != nil {\\n log.Fatal(err)\\n}\\n\\nif _, err := f.Write(userData); err != nil {\\n log.Fatal(err)\\n}\\n\\nif err := f.Close(); err != nil {\\n log.Fatal(err)\\n}\\n\\n// use the absolute path to the binary and the name of the temporary file\\n// steering clear of any user-provided filenames\\nout, err := exec.Command(\\"/bin/cat\\", f.Name()).Output()\\nif err != nil {\\n log.Fatal(err)\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_subproc_subproc", + "line_number": 87, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 87, + "end": 87, + "column": { + "start": 12, + "end": 54 + } + }, + "sink": { + "start": 87, + "end": 87, + "column": { + "start": 12, + "end": 54 + }, + "content": "syscall.ForkExec(command, []string{}, nil)" + }, + "parent_line_number": 87, + "snippet": "syscall.ForkExec(command, []string{}, nil)", + "fingerprint": "9f7b927d8c9e1a6c92e17fb2d6db3b18_5", + "old_fingerprint": "c7b747c46d0e283c15b7386a8c801ea8_5", + "code_extract": "\\t_, err := syscall.ForkExec(command, []string{}, nil)" + }, + { + "cwe_ids": [ + "95" + ], + "id": "go_gosec_subproc_subproc", + "title": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", + "description": "## Description\\n\\nOS command injection is a perilous vulnerability that has the potential to lead to full system compromise. Adversaries may exploit this flaw by feeding arbitrary commands or arguments intended for execution. This opens the door for unchecked operations, which could wreak havoc on the system or reveal sensitive information.\\n\\n## Remediations\\n\\n✅ Avoid User Input in OS Commands\\n\\nAlways steer clear of incorporating user input when formulating commands or their arguments, especially for functions responsible for OS command execution. This includes, but is not limited to, filenames provided during user uploads/downloads.\\n\\n✅ Hardcoded Argument Set\\n\\nEnsure your application exclusively uses a hardcoded set of arguments for OS command executions. If filenames are being passed to such functions, consider adopting a hash of the filename or another distinctive identifier.\\n\\n✅ Opt for Native Libraries\\n\\nDue to the inherent risks associated with third-party commands and the possibility of undisclosed attack vectors, prefer using native libraries that offer the same capabilities as opposed to resorting to OS system commands.\\n\\n✅ Specify Full Path in Windows\\n\\nIf the environment is Windows-based, always provide the complete path information when denoting the OS command. This circumvents potential vulnerabilities stemming from untrusted search paths (CWE-426).\\n\\n\`\`\`go\\nuserData := []byte(\\"user data\\")\\n// create a temporary file in the application-specific directory\\nf, err := ioutil.TempFile(\\"/var/app/restricted\\", \\"temp-*.dat\\")\\nif err != nil {\\n log.Fatal(err)\\n}\\n\\nif _, err := f.Write(userData); err != nil {\\n log.Fatal(err)\\n}\\n\\nif err := f.Close(); err != nil {\\n log.Fatal(err)\\n}\\n\\n// use the absolute path to the binary and the name of the temporary file\\n// steering clear of any user-provided filenames\\nout, err := exec.Command(\\"/bin/cat\\", f.Name()).Output()\\nif err != nil {\\n log.Fatal(err)\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_subproc_subproc", + "line_number": 98, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 98, + "end": 98, + "column": { + "start": 15, + "end": 61 + } + }, + "sink": { + "start": 98, + "end": 98, + "column": { + "start": 15, + "end": 61 + }, + "content": "syscall.StartProcess(command, []string{}, nil)" + }, + "parent_line_number": 98, + "snippet": "syscall.StartProcess(command, []string{}, nil)", + "fingerprint": "9f7b927d8c9e1a6c92e17fb2d6db3b18_6", + "old_fingerprint": "c7b747c46d0e283c15b7386a8c801ea8_6", + "code_extract": "\\t_, _, err := syscall.StartProcess(command, []string{}, nil)" + }, + { + "cwe_ids": [ + "95" + ], + "id": "go_gosec_subproc_subproc", + "title": "Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')", + "description": "## Description\\n\\nOS command injection is a perilous vulnerability that has the potential to lead to full system compromise. Adversaries may exploit this flaw by feeding arbitrary commands or arguments intended for execution. This opens the door for unchecked operations, which could wreak havoc on the system or reveal sensitive information.\\n\\n## Remediations\\n\\n✅ Avoid User Input in OS Commands\\n\\nAlways steer clear of incorporating user input when formulating commands or their arguments, especially for functions responsible for OS command execution. This includes, but is not limited to, filenames provided during user uploads/downloads.\\n\\n✅ Hardcoded Argument Set\\n\\nEnsure your application exclusively uses a hardcoded set of arguments for OS command executions. If filenames are being passed to such functions, consider adopting a hash of the filename or another distinctive identifier.\\n\\n✅ Opt for Native Libraries\\n\\nDue to the inherent risks associated with third-party commands and the possibility of undisclosed attack vectors, prefer using native libraries that offer the same capabilities as opposed to resorting to OS system commands.\\n\\n✅ Specify Full Path in Windows\\n\\nIf the environment is Windows-based, always provide the complete path information when denoting the OS command. This circumvents potential vulnerabilities stemming from untrusted search paths (CWE-426).\\n\\n\`\`\`go\\nuserData := []byte(\\"user data\\")\\n// create a temporary file in the application-specific directory\\nf, err := ioutil.TempFile(\\"/var/app/restricted\\", \\"temp-*.dat\\")\\nif err != nil {\\n log.Fatal(err)\\n}\\n\\nif _, err := f.Write(userData); err != nil {\\n log.Fatal(err)\\n}\\n\\nif err := f.Close(); err != nil {\\n log.Fatal(err)\\n}\\n\\n// use the absolute path to the binary and the name of the temporary file\\n// steering clear of any user-provided filenames\\nout, err := exec.Command(\\"/bin/cat\\", f.Name()).Output()\\nif err != nil {\\n log.Fatal(err)\\n}\\n\`\`\`\\n\\n## Resources\\n\\n- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_subproc_subproc", + "line_number": 121, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 121, + "end": 121, + "column": { + "start": 9, + "end": 67 + } + }, + "sink": { + "start": 121, + "end": 121, + "column": { + "start": 9, + "end": 67 + }, + "content": "exec.CommandContext(context.Background(), os.Args[0], \\"5\\")" + }, + "parent_line_number": 121, + "snippet": "exec.CommandContext(context.Background(), os.Args[0], \\"5\\")", + "fingerprint": "9f7b927d8c9e1a6c92e17fb2d6db3b18_7", + "old_fingerprint": "c7b747c46d0e283c15b7386a8c801ea8_7", + "code_extract": "\\terr := exec.CommandContext(context.Background(), os.Args[0], \\"5\\").Run()" + } + ] +}" +`; diff --git a/tests/go/gosec/subproc/subproc/test.js b/tests/go/gosec/subproc/subproc/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/subproc/subproc/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/subproc/subproc/testdata/main.go b/tests/go/gosec/subproc/subproc/testdata/main.go new file mode 100644 index 000000000..8e9727b8d --- /dev/null +++ b/tests/go/gosec/subproc/subproc/testdata/main.go @@ -0,0 +1,122 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + "os/exec" + "syscall" +) + +func foo1() { + err := exec.CommandContext(context.Background(), "git", "rev-parse", "--show-toplavel").Run() + if err != nil { + log.Fatal(err) + } + log.Printf("Command finished with error: %v", err) +} + +func foo2() { + err := exec.CommandContext(context.Background(), os.Args[0], "5").Run() + if err != nil { + log.Fatal(err) + } + log.Printf("Command finished with error: %v", err) +} + +func foo3() { + run := "sleep" + os.Getenv("SOMETHING") + cmd := exec.Command(run, "5") + err := cmd.Start() + if err != nil { + log.Fatal(err) + } + log.Printf("Waiting for command to finish...") + err = cmd.Wait() + log.Printf("Command finished with error: %v", err) +} + +func foo4(command string) { + cmd := exec.Command(command, "5") + err := cmd.Start() + if err != nil { + log.Fatal(err) + } + log.Printf("Waiting for command to finish...") + err = cmd.Wait() +} + +func foo5() { + foo4("sleep") +} + +func foo6(a string, c string) { + cmd := exec.Command(c) + err := cmd.Start() + if err != nil { + log.Fatal(err) + } + log.Printf("Waiting for command to finish...") + err = cmd.Wait() + cmd = exec.Command(a) + err = cmd.Start() + if err != nil { + log.Fatal(err) + } + log.Printf("Waiting for command to finish...") + err = cmd.Wait() +} + +func foo7() { + foo6("ll", "ls") +} + +func foo8() { + err := syscall.Exec("/bin/cat", []string{"/etc/passwd"}, nil) + if err != nil { + fmt.Printf("Error: %v\n", err) + } +} + +func foo9(command string) { + _, err := syscall.ForkExec(command, []string{}, nil) + if err != nil { + fmt.Printf("Error: %v\n", err) + } +} + +func foo10() { + foo9("sleep") +} + +func foo11(command string) { + _, _, err := syscall.StartProcess(command, []string{}, nil) + if err != nil { + fmt.Printf("Error: %v\n", err) + } +} + +func foo12() { + foo11("sleep") +} + +func foo13() { + run := "sleep" + cmd := exec.Command(run, "5") + err := cmd.Start() + if err != nil { + log.Fatal(err) + } + log.Printf("Waiting for command to finish...") + err = cmd.Wait() + log.Printf("Command finished with error: %v", err) +} + +func foo14() { + err := exec.CommandContext(context.Background(), os.Args[0], "5").Run() + if err != nil { + log.Fatal(err) + } + log.Printf("Command finished with error: %v", err) +} diff --git a/tests/go/gosec/unsafe/unsafe/__snapshots__/test.js.snap b/tests/go/gosec/unsafe/unsafe/__snapshots__/test.js.snap new file mode 100644 index 000000000..a45a22e22 --- /dev/null +++ b/tests/go/gosec/unsafe/unsafe/__snapshots__/test.js.snap @@ -0,0 +1,76 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`go_gosec_unsafe_unsafe test 1`] = ` +"{ + "low": [ + { + "cwe_ids": [ + "242" + ], + "id": "go_gosec_unsafe_unsafe", + "title": "Use of inherently dangerous function (unsafe package)", + "description": "## Description\\n\\nThe Go programming language features the \`unsafe\` package which grants low-level memory management capabilities, inclusive of direct memory access and pointer manipulation. Though the \`unsafe\` package can be quite potent, its usage sidesteps the Go compiler's type safety checks. This can lead to an array of security vulnerabilities and unpredictable system behavior.\\n\\n## Remediations\\n\\n✅ Avoid \`unsafe\` Unless Absolutely Necessary\\n\\nThe overarching guidance here is to steer clear of the \`unsafe\` package unless there's an absolute necessity for its functions. When opting for low-level memory operations, ensure that their implications are well-understood and that their deployment is preceded by rigorous testing.\\n\\n✅ Be Wary of Buffer Overflows\\n\\nDirect manipulation of memory can lead to buffer overflows, potentially enabling unauthorized code execution. Ensure buffer boundaries are always respected.\\n\\n✅ Avoid Use After Free\\n\\nAccessing memory that has already been freed can result in unintended code execution or unpredictable behaviors. Ensure that once memory has been freed, it isn't accessed further.\\n\\n✅ Prevent Information/Memory Leaks\\n\\nUnintended memory retention or unintended disclosure of information in memory can occur when using unsafe functions. This can compromise other security defenses or lead to system failures due to exhausted memory. Regularly review and audit your code to check for such leaks.\\n\\n## Resources\\n\\n- [Buffer Overflows - OWASP](https://owasp.org/www-community/vulnerabilities/Buffer_Overflow)\\n- [Using Freed Memory - OWASP](https://owasp.org/www-community/vulnerabilities/Using_freed_memory)\\n- [Memory Leaks - OWASP](https://owasp.org/www-community/vulnerabilities/Memory_leak)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_unsafe_unsafe", + "line_number": 23, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 23, + "end": 23, + "column": { + "start": 53, + "end": 79 + } + }, + "sink": { + "start": 23, + "end": 23, + "column": { + "start": 27, + "end": 49 + }, + "content": "unsafe.Pointer(intPtr)" + }, + "parent_line_number": 23, + "snippet": "unsafe.Pointer(intPtr)", + "fingerprint": "db1c9d5a44e2bb335daa53a4b208a682_0", + "old_fingerprint": "0787e01535ffb4d4e7052dfb097bcfdb_0", + "code_extract": "\\taddressHolder := uintptr(unsafe.Pointer(intPtr)) + unsafe.Sizeof(intArray[0])" + }, + { + "cwe_ids": [ + "242" + ], + "id": "go_gosec_unsafe_unsafe", + "title": "Use of inherently dangerous function (unsafe package)", + "description": "## Description\\n\\nThe Go programming language features the \`unsafe\` package which grants low-level memory management capabilities, inclusive of direct memory access and pointer manipulation. Though the \`unsafe\` package can be quite potent, its usage sidesteps the Go compiler's type safety checks. This can lead to an array of security vulnerabilities and unpredictable system behavior.\\n\\n## Remediations\\n\\n✅ Avoid \`unsafe\` Unless Absolutely Necessary\\n\\nThe overarching guidance here is to steer clear of the \`unsafe\` package unless there's an absolute necessity for its functions. When opting for low-level memory operations, ensure that their implications are well-understood and that their deployment is preceded by rigorous testing.\\n\\n✅ Be Wary of Buffer Overflows\\n\\nDirect manipulation of memory can lead to buffer overflows, potentially enabling unauthorized code execution. Ensure buffer boundaries are always respected.\\n\\n✅ Avoid Use After Free\\n\\nAccessing memory that has already been freed can result in unintended code execution or unpredictable behaviors. Ensure that once memory has been freed, it isn't accessed further.\\n\\n✅ Prevent Information/Memory Leaks\\n\\nUnintended memory retention or unintended disclosure of information in memory can occur when using unsafe functions. This can compromise other security defenses or lead to system failures due to exhausted memory. Regularly review and audit your code to check for such leaks.\\n\\n## Resources\\n\\n- [Buffer Overflows - OWASP](https://owasp.org/www-community/vulnerabilities/Buffer_Overflow)\\n- [Using Freed Memory - OWASP](https://owasp.org/www-community/vulnerabilities/Using_freed_memory)\\n- [Memory Leaks - OWASP](https://owasp.org/www-community/vulnerabilities/Memory_leak)\\n", + "documentation_url": "https://docs.bearer.com/reference/rules/go_gosec_unsafe_unsafe", + "line_number": 24, + "full_filename": "/tmp/bearer-scan/main.go", + "filename": ".", + "source": { + "start": 24, + "end": 24, + "column": { + "start": 18, + "end": 47 + } + }, + "sink": { + "start": 24, + "end": 24, + "column": { + "start": 18, + "end": 47 + }, + "content": "unsafe.Pointer(addressHolder)" + }, + "parent_line_number": 24, + "snippet": "unsafe.Pointer(addressHolder)", + "fingerprint": "db1c9d5a44e2bb335daa53a4b208a682_2", + "old_fingerprint": "0787e01535ffb4d4e7052dfb097bcfdb_2", + "code_extract": "\\tintPtr = (*int)(unsafe.Pointer(addressHolder))" + } + ] +}" +`; diff --git a/tests/go/gosec/unsafe/unsafe/test.js b/tests/go/gosec/unsafe/unsafe/test.js new file mode 100644 index 000000000..12471991f --- /dev/null +++ b/tests/go/gosec/unsafe/unsafe/test.js @@ -0,0 +1,11 @@ +const { createInvoker, getEnvironment } = require("../../../../helper.js") +const { ruleId, ruleFile, testBase } = getEnvironment(__dirname) + +describe(ruleId, () => { + const invoke = createInvoker(ruleId, ruleFile, testBase) + + test("test", () => { + const testCase = "main.go" + expect(invoke(testCase)).toMatchSnapshot() + }) +}) diff --git a/tests/go/gosec/unsafe/unsafe/testdata/main.go b/tests/go/gosec/unsafe/unsafe/testdata/main.go new file mode 100644 index 000000000..8717aa1c8 --- /dev/null +++ b/tests/go/gosec/unsafe/unsafe/testdata/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" + "unsafe" +) + +type Fake struct{} + +func (Fake) Good() {} + +func main() { + unsafeM := Fake{} + unsafeM.Good() + intArray := [...]int{1, 2} + fmt.Printf("\nintArray: %v\n", intArray) + intPtr := &intArray[0] + fmt.Printf("\nintPtr=%p, *intPtr=%d.\n", intPtr, *intPtr) + addressHolder := uintptr(unsafe.Pointer(intPtr)) + unsafe.Sizeof(intArray[0]) + intPtr = (*int)(unsafe.Pointer(addressHolder)) + fmt.Printf("\nintPtr=%p, *intPtr=%d.\n\n", intPtr, *intPtr) +} diff --git a/tests/helper.js b/tests/helper.js index 295c170dd..5fec4a2a7 100644 --- a/tests/helper.js +++ b/tests/helper.js @@ -1,15 +1,58 @@ -const { execSync } = require('child_process'); +const { execSync } = require("child_process") -exports.getEnvironment = (path) => ({ - ruleId: path.split("/").slice(-3).join("_"), +exports.getEnvironment = (path) => { + const slicedPath = path.split("/") + + let processedPath + if (slicedPath[slicedPath.length - 3] === "gosec") { + processedPath = slicedPath.slice(-4) + } else { + processedPath = slicedPath.slice(-3) + } + + const ruleId = processedPath.join("_") + const ruleFile = `./rules/${processedPath.join("/")}.yml` + + return { + ruleId, testBase: `${path}/testdata/`, - ruleFile: `./rules/${path.split("/").slice(-3).join("/")}.yml` - }) + ruleFile, + } +} exports.createInvoker = (ruleId, ruleFile, testBase) => { return (testCase) => { - const out = execSync(`./scripts/invoke.sh ${ruleFile} ${testBase}${testCase} ${ruleId}`).toString() - return JSON.stringify(JSON.parse(out), null, 2) + const out = execSync( + `./scripts/invoke.sh ${ruleFile} ${testBase}${testCase} ${ruleId}` + ).toString() + + results = JSON.parse(out) + let findings = [] + for (const [key, values] of Object.entries(results)) { + for (const [value] in values) { + findings.push({ + // severity: key, + line_number: values[value]["line_number"], + }) + } + } + + console.log( + "[%s]\n\t%d findings for %s using (%s)\n%s\n\n%s", + ruleId, + findings.length, + ruleFile, + testBase, + JSON.stringify(findings, null, 2), + `Run the following command if you need to debug + +(if running bearer develop) +go run cmd/bearer/main.go scan ${testBase} --only-rule ${ruleId} --log-level trace + +(if running binary) +bearer scan ${testBase} --only-rule ${ruleId} --log-level trace` + ) + + return JSON.stringify(results, null, 2) } } -