From 5bfe249b5063ea028d66a3783e60e80db35e48e7 Mon Sep 17 00:00:00 2001 From: berney Date: Tue, 12 Nov 2024 19:43:14 +1100 Subject: [PATCH 01/10] Allow OWASP Top 10 references from Kubernetes and LLM Top 10 (#3499) Co-authored-by: Berne Campbell <3227426+berney@users.noreply.github.com> Co-authored-by: Pieter De Cremer (Semgrep) --- yaml/semgrep/metadata-owasp.test.yaml | 18 ++++++++++++++++++ yaml/semgrep/metadata-owasp.yaml | 8 ++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/yaml/semgrep/metadata-owasp.test.yaml b/yaml/semgrep/metadata-owasp.test.yaml index 0f1946b24f..b7d264c4db 100644 --- a/yaml/semgrep/metadata-owasp.test.yaml +++ b/yaml/semgrep/metadata-owasp.test.yaml @@ -15,6 +15,22 @@ rules: metadata: # ok: metadata-owasp owasp: A05:2021 - Security Misconfiguration + - id: example-k8s-1 + message: Example + severity: ERROR + languages: [json, yaml] + pattern: "..." + metadata: + # ok: metadata-owasp + owasp: "K1: Insecure Workload Configurations" + - id: example-k8s-1b + message: Example + severity: ERROR + languages: [json, yaml] + pattern: "..." + metadata: + # ok: metadata-owasp + owasp: K01:2022 - Insecure Workload Configurations - id: example-bad-zero message: Example severity: ERROR @@ -75,6 +91,8 @@ rules: - A05:2021 - Security Misconfiguration # ok: metadata-owasp - A06:2017 - Security Misconfiguration + # ok: metadata-owasp + - K01:2022 - Insecure Workload Configurations - id: example-bad-list message: Example severity: ERROR diff --git a/yaml/semgrep/metadata-owasp.yaml b/yaml/semgrep/metadata-owasp.yaml index a0dec878cc..510a3018ee 100644 --- a/yaml/semgrep/metadata-owasp.yaml +++ b/yaml/semgrep/metadata-owasp.yaml @@ -2,7 +2,7 @@ rules: - id: metadata-owasp message: >- The `owasp` tag in Semgrep rule metadata should start with the format "A00:YYYY", - where A00 is the OWASP top ten number and YYYY is the OWASP top ten year. + where A00 is the OWASP Top 10 number and YYYY is the OWASP Top 10 year. severity: ERROR languages: [json, yaml] patterns: @@ -13,13 +13,13 @@ rules: # If there's a year, need leading zero, e.g. `A01:2021 blah` rather than `A1:2021 blah`. - patterns: - pattern: 'owasp: "..."' - - pattern-not: 'owasp: "=~/^A(0?[1-9]|10):\s+.+$/"' - - pattern-not: 'owasp: "=~/^A(0[1-9]|10):([0-9]{4})?\s+.+$/"' + - pattern-not: 'owasp: "=~/^(A|K|LLM)(0?[1-9]|10):\s+.+$/"' + - pattern-not: 'owasp: "=~/^(A|K|LLM)(0[1-9]|10):([0-9]{4})?\s+.+$/"' # A list, must have the year, e.g. `- A01:2021 blah` - patterns: - pattern-inside: "owasp: [...]" - pattern: '"$ANYTHING"' - - pattern-not-regex: .*A(0[1-9]|10):[0-9]{4}\s+.* + - pattern-not-regex: .*(A|K|LLM)(0[1-9]|10):[0-9]{4}\s+.* - pattern-not-regex: "owasp:" metadata: category: best-practice From aaf727cea513fbbb1b79d746846dc6b31a7ad29e Mon Sep 17 00:00:00 2001 From: QU35T-code <51704860+QU35T-code@users.noreply.github.com> Date: Tue, 12 Nov 2024 09:54:03 +0100 Subject: [PATCH 02/10] Add literal pattern (#3507) Co-authored-by: Pieter De Cremer (Semgrep) --- .../security/audit/sequelize-raw-query.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/javascript/sequelize/security/audit/sequelize-raw-query.yaml b/javascript/sequelize/security/audit/sequelize-raw-query.yaml index 368c91473c..176dfe59a3 100644 --- a/javascript/sequelize/security/audit/sequelize-raw-query.yaml +++ b/javascript/sequelize/security/audit/sequelize-raw-query.yaml @@ -40,3 +40,15 @@ rules: $QUERY = $SQL + $VALUE ... $DATABASE.sequelize.query($QUERY, ...) + - pattern: | + Sequelize.literal(`...${...}...`) + - pattern: | + $QUERY = `...${...}...` + ... + Sequelize.literal($QUERY) + - pattern: | + Sequelize.literal($SQL + $VALUE) + - pattern: | + $QUERY = $SQL + $VALUE + ... + Sequelize.literal($QUERY) From 495df89cd2b3595c41f1e34abad7c465010d30cd Mon Sep 17 00:00:00 2001 From: berney Date: Wed, 13 Nov 2024 19:26:52 +1100 Subject: [PATCH 03/10] Filter exceptions from tainted-sql-string (#3501) Co-authored-by: Berne Campbell <3227426+berney@users.noreply.github.com> Co-authored-by: Pieter De Cremer (Semgrep) --- scala/lang/security/audit/tainted-sql-string.scala | 5 +++++ scala/lang/security/audit/tainted-sql-string.yaml | 1 + 2 files changed, 6 insertions(+) diff --git a/scala/lang/security/audit/tainted-sql-string.scala b/scala/lang/security/audit/tainted-sql-string.scala index 59bd99623c..d1a12cff09 100644 --- a/scala/lang/security/audit/tainted-sql-string.scala +++ b/scala/lang/security/audit/tainted-sql-string.scala @@ -94,4 +94,9 @@ object Smth { logWarning(s"Create user $name") } } + + def throwException(name: String) = { + // ok: tainted-sql-string + throw new IllegalArgumentException(s"Can't create a ${name}") + } } diff --git a/scala/lang/security/audit/tainted-sql-string.yaml b/scala/lang/security/audit/tainted-sql-string.yaml index c02debe264..24805b5acd 100644 --- a/scala/lang/security/audit/tainted-sql-string.yaml +++ b/scala/lang/security/audit/tainted-sql-string.yaml @@ -73,6 +73,7 @@ rules: - pattern-regex: | .*\b(?i)(select|delete|insert|create|update|alter|drop)\b.* - pattern-not-inside: println(...) + - pattern-not-inside: throw new $EXCEPTION(...) pattern-sanitizers: - pattern-either: - patterns: From 3b05904d887fb274c494b2e19f5f47dc1d1933c6 Mon Sep 17 00:00:00 2001 From: lfama Date: Mon, 18 Nov 2024 09:41:57 +0100 Subject: [PATCH 04/10] Add flask-cors-misconfiguration rule (#3506) * Add flas-cors-misconfiguration rule * Fixed comment issue --------- Co-authored-by: Pieter De Cremer (Semgrep) --- .../audit/flask-cors-misconfiguration.py | 39 +++++++++++++++++++ .../audit/flask-cors-misconfiguration.yaml | 36 +++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 python/flask/security/audit/flask-cors-misconfiguration.py create mode 100644 python/flask/security/audit/flask-cors-misconfiguration.yaml diff --git a/python/flask/security/audit/flask-cors-misconfiguration.py b/python/flask/security/audit/flask-cors-misconfiguration.py new file mode 100644 index 0000000000..1fe28cebc0 --- /dev/null +++ b/python/flask/security/audit/flask-cors-misconfiguration.py @@ -0,0 +1,39 @@ +from flask import Flask, jsonify +from flask_cors import CORS, cross_origin + +app = Flask(__name__) + +# Enable global CORS for all origins and allow credentials +# ruleid: flask-cors-misconfiguration +CORS(app, supports_credentials=True, origins="*") + +# Enable global CORS for all origins and allow credentials using "resources" dictionary +# ruleid: flask-cors-misconfiguration +cors = CORS(app, resources={ + r"/*": {"origins": "*", "supports_credentials": True}}) + + +@app.route('/data', methods=['GET']) +def get_data(): + # This route uses the global CORS configuration + return jsonify({"message": "CORS is enabled for all origins with credentials support (global config)!"}) + + +@app.route('/special-data', methods=['GET']) +# CORS applied only to this route +# ruleid: flask-cors-misconfiguration +@cross_origin(supports_credentials=True, origins="*") +def get_special_data(): + # This route uses the CORS decorator for route-specific CORS settings + return jsonify({"message": "CORS is enabled with credentials (route-specific config)!"}) + + +@app.route('/safe-route', methods=['GET']) +# ok: flask-cors-misconfiguration +@cross_origin(supports_credentials=True, origins=["https://foo.com", "https://bar.com"]) +def safe_route(): + return jsonify({"message": "CORS is enabled only for specific origins!"}) + + +if __name__ == '__main__': + app.run() diff --git a/python/flask/security/audit/flask-cors-misconfiguration.yaml b/python/flask/security/audit/flask-cors-misconfiguration.yaml new file mode 100644 index 0000000000..4dc989a23c --- /dev/null +++ b/python/flask/security/audit/flask-cors-misconfiguration.yaml @@ -0,0 +1,36 @@ +rules: + - id: flask-cors-misconfiguration + message: >- + Setting 'support_credentials=True' together with 'origin="*"' is a CORS + misconfiguration that can allow third party origins to read sensitive + data. Using this configuration, flask_cors will dynamically reflects the + Origin of each request in the Access-Control-Allow-Origin header, allowing + all origins and allowing cookies and credentials to be sent along with + request. It is recommended to specify allowed origins instead of using "*" + when setting 'support_credentials=True'. + languages: + - python + severity: WARNING + patterns: + - pattern-either: + - pattern: | + @cross_origin(..., origins="*", supports_credentials=True, ...) + - pattern: | + CORS(..., supports_credentials=True, origins="*", ...) + - pattern: | + CORS(..., resources={"...": {...,"origins": "*", + "supports_credentials": True,...}}) + metadata: + category: security + subcategory: + - audit + cwe: + - "CWE 942: Permissive Cross-domain Policy with Untrusted Domains" + confidence: HIGH + likelihood: LOW + impact: HIGH + technology: + - flask + references: + - https://pypi.org/project/Flask-Cors/ + - https://flask-cors.readthedocs.io/en/latest/index.html From e8a1345b93741a1bd2a9502e69cc053b8ac4c2a6 Mon Sep 17 00:00:00 2001 From: Mohamed AboElKheir Date: Tue, 19 Nov 2024 03:14:47 -0500 Subject: [PATCH 05/10] added a rule to detect usage of uuid version 1 in python (#3517) * added a rule to detect usage of uuid version 1 in python * Applying suggestion to move url from message to references Co-authored-by: Pieter De Cremer (Semgrep) --------- Co-authored-by: Pieter De Cremer (Semgrep) --- python/lang/security/insecure-uuid-version.py | 19 +++++++++++ .../lang/security/insecure-uuid-version.yaml | 33 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 python/lang/security/insecure-uuid-version.py create mode 100644 python/lang/security/insecure-uuid-version.yaml diff --git a/python/lang/security/insecure-uuid-version.py b/python/lang/security/insecure-uuid-version.py new file mode 100644 index 0000000000..95ce6cee86 --- /dev/null +++ b/python/lang/security/insecure-uuid-version.py @@ -0,0 +1,19 @@ +import uuid +def example_1(): + # ruleid:insecure-uuid-version + uuid = uuid.uuid1() + +from uuid import uuid1 +def example_2(): + # ruleid:insecure-uuid-version + uuid = uuid1() + +from uuid import * +def example_3(): + # ruleid:insecure-uuid-version + uuid = uuid1() + +import uuid +def unrelated_function(): + # ok:insecure-uuid-version + uuid = uuid4() diff --git a/python/lang/security/insecure-uuid-version.yaml b/python/lang/security/insecure-uuid-version.yaml new file mode 100644 index 0000000000..b5a8514b5a --- /dev/null +++ b/python/lang/security/insecure-uuid-version.yaml @@ -0,0 +1,33 @@ +rules: + - id: insecure-uuid-version + patterns: + - pattern: uuid.uuid1(...) + message: | + Using UUID version 1 for UUID generation can lead to predictable UUIDs based on system information (e.g., MAC address, timestamp). This may lead to security risks such as the sandwich attack. Consider using `uuid.uuid4()` instead for better randomness and security. + metadata: + references: + - https://www.landh.tech/blog/20230811-sandwich-attack/ + cwe: + - 'CWE-330: Use of Insufficiently Random Values' + owasp: + - A02:2021 - Cryptographic Failures + asvs: + section: V6 Stored Cryptography Verification Requirements + control_id: 6.3.2 Insecure UUID Generation + control_url: https://github.com/OWASP/ASVS/blob/master/4.0/en/0x14-V6-Cryptography.md#v63-random-values + version: '4' + category: security + technology: + - python + subcategory: + - audit + likelihood: LOW + impact: MEDIUM + confidence: MEDIUM + languages: + - python + severity: WARNING + fix-regex: + regex: uuid1 + replacement: uuid4 + \ No newline at end of file From 0632f695527d18cd1bcac2e39b6d1ff1bb486688 Mon Sep 17 00:00:00 2001 From: Iago Abal Date: Tue, 19 Nov 2024 19:37:57 +0100 Subject: [PATCH 06/10] Fix test code for javascript.express.security.audit.xss.direct-response-write (#3519) --- javascript/express/security/audit/xss/direct-response-write.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/express/security/audit/xss/direct-response-write.js b/javascript/express/security/audit/xss/direct-response-write.js index f0718cd16f..7f0b46f6dc 100644 --- a/javascript/express/security/audit/xss/direct-response-write.js +++ b/javascript/express/security/audit/xss/direct-response-write.js @@ -54,7 +54,7 @@ app.get('/3', function (req, res) { app.get('/2', function (req, res) { var user = { user: req.query.name }; // ruleid: direct-response-write - res.send('Response
' + user.name); + res.send('Response
' + user.user); }); From d27718c7bd82c159384f3a2dfeed7c1aa3d872f8 Mon Sep 17 00:00:00 2001 From: Iago Abal Date: Wed, 20 Nov 2024 12:04:46 +0100 Subject: [PATCH 07/10] Fix rule express-xml2json-xxe-event after Pro PR 2587 (#3513) Follows semgrep/semgrep-proprietary#2587 --- .../express/security/audit/express-xml2json-xxe-event.js | 6 ++++-- .../express/security/audit/express-xml2json-xxe-event.yaml | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/javascript/express/security/audit/express-xml2json-xxe-event.js b/javascript/express/security/audit/express-xml2json-xxe-event.js index c30f6b3fc3..cbdebc56ab 100644 --- a/javascript/express/security/audit/express-xml2json-xxe-event.js +++ b/javascript/express/security/audit/express-xml2json-xxe-event.js @@ -10,7 +10,8 @@ function test1() { req.on('data', function (chunk) { buf += chunk }); - // ruleid: express-xml2json-xxe-event + // The rule isn't written in a way that it can find this + // todoruleid: express-xml2json-xxe-event req.on('end', function () { req.body = expat.toJson(buf, {coerce: true, object: true}); next(); @@ -29,7 +30,8 @@ function test2() { req.on('data', function (chunk) { buf += chunk }); - // ruleid: express-xml2json-xxe-event + // The rule isn't written in a way that it can find this + // todoruleid: express-xml2json-xxe-event req.on('end', function () { req.body = expat.toJson(buf, {coerce: true, object: true}); next(); diff --git a/javascript/express/security/audit/express-xml2json-xxe-event.yaml b/javascript/express/security/audit/express-xml2json-xxe-event.yaml index fe2537298c..c0e513da24 100644 --- a/javascript/express/security/audit/express-xml2json-xxe-event.yaml +++ b/javascript/express/security/audit/express-xml2json-xxe-event.yaml @@ -69,3 +69,4 @@ rules: import 'xml2json'; ... - pattern: $REQ.on('...', function(...) { ... $EXPAT.toJson($INPUT,...); ... }) + - focus-metavariable: $INPUT From a5fbe2ef09dd67f7aab1e6dfb432f6ccb56db77f Mon Sep 17 00:00:00 2001 From: Claudio Date: Wed, 20 Nov 2024 15:16:33 +0100 Subject: [PATCH 08/10] Update insecure-uuid-version.yaml (#3520) --- python/lang/security/insecure-uuid-version.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/lang/security/insecure-uuid-version.yaml b/python/lang/security/insecure-uuid-version.yaml index b5a8514b5a..00d78f516e 100644 --- a/python/lang/security/insecure-uuid-version.yaml +++ b/python/lang/security/insecure-uuid-version.yaml @@ -2,7 +2,7 @@ rules: - id: insecure-uuid-version patterns: - pattern: uuid.uuid1(...) - message: | + message: >- Using UUID version 1 for UUID generation can lead to predictable UUIDs based on system information (e.g., MAC address, timestamp). This may lead to security risks such as the sandwich attack. Consider using `uuid.uuid4()` instead for better randomness and security. metadata: references: @@ -30,4 +30,4 @@ rules: fix-regex: regex: uuid1 replacement: uuid4 - \ No newline at end of file + From 2a5cbdab4b0c2c80a2b2b9795fcb64db74556621 Mon Sep 17 00:00:00 2001 From: Claudio Date: Mon, 2 Dec 2024 11:34:17 +0100 Subject: [PATCH 09/10] Update flask-cors-misconfiguration.yaml (#3523) --- python/flask/security/audit/flask-cors-misconfiguration.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/flask/security/audit/flask-cors-misconfiguration.yaml b/python/flask/security/audit/flask-cors-misconfiguration.yaml index 4dc989a23c..106a01099a 100644 --- a/python/flask/security/audit/flask-cors-misconfiguration.yaml +++ b/python/flask/security/audit/flask-cors-misconfiguration.yaml @@ -25,7 +25,9 @@ rules: subcategory: - audit cwe: - - "CWE 942: Permissive Cross-domain Policy with Untrusted Domains" + - "CWE-942: Permissive Cross-domain Policy with Untrusted Domains" + owasp: + - A07:2021 - Identification and Authentication Failures confidence: HIGH likelihood: LOW impact: HIGH From 4c96619c66869ddc97d4a41da550b11259cf1c92 Mon Sep 17 00:00:00 2001 From: Claudio Date: Mon, 2 Dec 2024 11:36:06 +0100 Subject: [PATCH 10/10] Improve FPs in react-insecure-request (#3524) * Improve FPs in react-insecure-request * Update react-insecure-request.tsx * Update react-insecure-request.jsx --- .../react/security/react-insecure-request.jsx | 3 + .../react/security/react-insecure-request.tsx | 3 + .../security/react-insecure-request.yaml | 70 +++++++++---------- 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/typescript/react/security/react-insecure-request.jsx b/typescript/react/security/react-insecure-request.jsx index b36619c4c5..a8c33d43f2 100644 --- a/typescript/react/security/react-insecure-request.jsx +++ b/typescript/react/security/react-insecure-request.jsx @@ -34,3 +34,6 @@ const options = { url: 'https://www.example.com', }; axios(options); + +// ok: react-insecure-request +axios.get('http://localhost/foo'); diff --git a/typescript/react/security/react-insecure-request.tsx b/typescript/react/security/react-insecure-request.tsx index b36619c4c5..a8c33d43f2 100644 --- a/typescript/react/security/react-insecure-request.tsx +++ b/typescript/react/security/react-insecure-request.tsx @@ -34,3 +34,6 @@ const options = { url: 'https://www.example.com', }; axios(options); + +// ok: react-insecure-request +axios.get('http://localhost/foo'); diff --git a/typescript/react/security/react-insecure-request.yaml b/typescript/react/security/react-insecure-request.yaml index 7933cd1625..dc6356a1d8 100644 --- a/typescript/react/security/react-insecure-request.yaml +++ b/typescript/react/security/react-insecure-request.yaml @@ -23,39 +23,39 @@ rules: - typescript - javascript severity: ERROR - pattern-either: - - patterns: + patterns: - pattern-either: - - pattern-inside: | - import $AXIOS from 'axios'; - ... - $AXIOS.$METHOD(...) - - pattern-inside: | - $AXIOS = require('axios'); - ... - $AXIOS.$METHOD(...) - - pattern-either: - - pattern: $AXIOS.get("=~/[Hh][Tt][Tt][Pp]:\/\/.*/",...) - - pattern: $AXIOS.post("=~/[Hh][Tt][Tt][Pp]:\/\/.*/",...) - - pattern: $AXIOS.delete("=~/[Hh][Tt][Tt][Pp]:\/\/.*/",...) - - pattern: $AXIOS.head("=~/[Hh][Tt][Tt][Pp]:\/\/.*/",...) - - pattern: $AXIOS.patch("=~/[Hh][Tt][Tt][Pp]:\/\/.*/",...) - - pattern: $AXIOS.put("=~/[Hh][Tt][Tt][Pp]:\/\/.*/",...) - - pattern: $AXIOS.options("=~/[Hh][Tt][Tt][Pp]:\/\/.*/",...) - - patterns: - - pattern-either: - - pattern-inside: | - import $AXIOS from 'axios'; - ... - $AXIOS(...) - - pattern-inside: | - $AXIOS = require('axios'); - ... - $AXIOS(...) - - pattern-either: - - pattern: '$AXIOS({url: "=~/[Hh][Tt][Tt][Pp]:\/\/.*/"}, ...)' - - pattern: | - $OPTS = {url: "=~/[Hh][Tt][Tt][Pp]:\/\/.*/"} - ... - $AXIOS($OPTS, ...) - - pattern: fetch("=~/[Hh][Tt][Tt][Pp]:\/\/.*/", ...) + - patterns: + - pattern-either: + - pattern-inside: | + import $AXIOS from 'axios'; + ... + $AXIOS.$METHOD(...) + - pattern-inside: | + $AXIOS = require('axios'); + ... + $AXIOS.$METHOD(...) + - pattern: $AXIOS.$VERB("$URL",...) + - metavariable-regex: + metavariable: $VERB + regex: ^(get|post|delete|head|patch|put|options) + - patterns: + - pattern-either: + - pattern-inside: | + import $AXIOS from 'axios'; + ... + $AXIOS(...) + - pattern-inside: | + $AXIOS = require('axios'); + ... + $AXIOS(...) + - pattern-either: + - pattern: '$AXIOS({url: "$URL"}, ...)' + - pattern: | + $OPTS = {url: "$URL"} + ... + $AXIOS($OPTS, ...) + - pattern: fetch("$URL", ...) + - metavariable-regex: + metavariable: $URL + regex: ^([Hh][Tt][Tt][Pp]:\/\/(?!localhost).*)