diff --git a/.codemapignore b/.codemapignore index 0d77320097..4d725e3c70 100644 --- a/.codemapignore +++ b/.codemapignore @@ -10,7 +10,6 @@ !libsonnet/ !scripts/ !stats/ -# restore also fingerprints/ ? trusted_python/ ? # do not skip the rules ![a-z]*/**/*.yaml diff --git a/.github/workflows/semgrep-rule-lints.yaml b/.github/workflows/semgrep-rule-lints.yaml index 9114b485ac..034af3014c 100644 --- a/.github/workflows/semgrep-rule-lints.yaml +++ b/.github/workflows/semgrep-rule-lints.yaml @@ -45,5 +45,4 @@ jobs: --exclude *.test.yaml \ --exclude contrib/ \ --exclude stats/ \ - --exclude fingerprints/ \ --exclude yaml/semgrep/ diff --git a/.github/workflows/semgrep-rules-test-develop.yml b/.github/workflows/semgrep-rules-test-develop.yml index 190bb44484..2a759cc2d3 100644 --- a/.github/workflows/semgrep-rules-test-develop.yml +++ b/.github/workflows/semgrep-rules-test-develop.yml @@ -1,3 +1,6 @@ +# Running the tests in the repo using `semgrep test` (osemgrep) and +# the semgrep/semgrep:pro-develop docker image (the bleeding edge!). + name: semgrep-rules-test-develop on: pull_request: @@ -9,26 +12,22 @@ on: - develop - release jobs: - # Note: if you change this test there will likely need to be a - # corresponding change in returntocorp/semgrep test-develop: name: rules-test-develop + # alt: use directly the semgrep/semgrep:pro-develop container here so we + # don't need the calls to 'docker run ...' below runs-on: ubuntu-20.04 + # TODO: remove the with: path: below to simplify steps: - uses: actions/checkout@v2 with: path: semgrep-rules - - name: delete stats directory - run: rm -rf semgrep-rules/stats - - name: delete fingerprints directory - run: rm -rf semgrep-rules/fingerprints - - name: delete rules requiring Semgrep Pro - run: rm -rf semgrep-rules/apex semgrep-rules/elixir - - name: validate rules - run: | - export SEMGREP="docker run --rm -w /src -v ${GITHUB_WORKSPACE}/semgrep-rules:/src returntocorp/semgrep:develop semgrep" - make -C "$GITHUB_WORKSPACE"/semgrep-rules validate - - name: test with semgrep develop branch - run: | - export SEMGREP="docker run --rm -w /src -v ${GITHUB_WORKSPACE}/semgrep-rules:/src returntocorp/semgrep:develop semgrep" - make -C "$GITHUB_WORKSPACE"/semgrep-rules test-only + # alt: call 'make validate' but would require 'make' in the docker image + # alt: export SEMGREP="docker run --rm -w ... semgrep" + # make -C "$GITHUB_WORKSPACE"/semgrep-rules validate + #TODO: this actually currently fails because of errors in stats/ but GHA + # still continue, weird + - name: run osemgrep validate --pro + run: docker run --rm -w /src -v ${GITHUB_WORKSPACE}/semgrep-rules:/src semgrep/semgrep:pro-develop semgrep validate --pro . + - name: run osemgrep test --pro + run: docker run --rm -w /src -v ${GITHUB_WORKSPACE}/semgrep-rules:/src semgrep/semgrep:pro-develop semgrep test --pro . diff --git a/.github/workflows/semgrep-rules-test-historical.yml b/.github/workflows/semgrep-rules-test-historical.yml index 55ecab1ac3..071ef8271f 100644 --- a/.github/workflows/semgrep-rules-test-historical.yml +++ b/.github/workflows/semgrep-rules-test-historical.yml @@ -32,8 +32,6 @@ jobs: run: pip3 install semgrep - name: delete stats directory run: rm -rf semgrep-rules/stats - - name: delete fingerprints directory - run: rm -rf semgrep-rules/fingerprints - name: delete rules requiring Semgrep Pro run: rm -rf semgrep-rules/apex semgrep-rules/elixir # TODO: remove this in the future, there was a regression in semgrep that diff --git a/.github/workflows/semgrep-rules-test.yml b/.github/workflows/semgrep-rules-test.yml index dccca89c1e..5cd02cec82 100644 --- a/.github/workflows/semgrep-rules-test.yml +++ b/.github/workflows/semgrep-rules-test.yml @@ -17,19 +17,13 @@ jobs: - uses: actions/setup-python@v2 with: python-version: 3.9.2 - - name: install semgrep + - name: install semgrep via pip run: pip3 install semgrep - name: remove stats directory run: rm -rf stats - - name: remove fingerprints from testing - run: rm -rf fingerprints - - name: remove .github from testing - run: rm -rf .github - - name: remove pre-commit-config.yaml - run: rm -f .pre-commit-config.yaml - name: remove rules requiring Semgrep Pro run: rm -rf apex elixir - name: validate rules - run: semgrep --validate --config . - - name: run semgrep - run: semgrep --test --test-ignore-todo + run: semgrep validate . + - name: run semgrep test + run: semgrep test . diff --git a/.github/workflows/trigger-semgrep-scanner-initiate-scan.yaml b/.github/workflows/trigger-semgrep-scanner-initiate-scan.yaml index 91aff30daa..c2449ab91b 100644 --- a/.github/workflows/trigger-semgrep-scanner-initiate-scan.yaml +++ b/.github/workflows/trigger-semgrep-scanner-initiate-scan.yaml @@ -21,7 +21,7 @@ jobs: env: HEAD_REF: ${{ github.head_ref }} run: | - CHANGED_FILES=$(git diff --name-only origin/develop origin/$HEAD_REF | xargs -0 | sed '/^$/d' | sed -r '/^(.github|bash|contrib|fingerprints|generic|json|problem-based-packs|scripts|stats|trusted_python|yaml)/d' | sed -n '/.*yaml/p' | tr '\n' ' ') + CHANGED_FILES=$(git diff --name-only origin/develop origin/$HEAD_REF | xargs -0 | sed '/^$/d' | sed -r '/^(.github|bash|generic|json|problem-based-packs|scripts|stats|trusted_python|yaml)/d' | sed -n '/.*yaml/p' | tr '\n' ' ') echo "changed_files=$CHANGED_FILES" >> $GITHUB_ENV - id: print-changed-files name: debugging step - print changed files @@ -52,8 +52,6 @@ jobs: HEAD_REF: ${{ github.head_ref }} REPO_NAME: ${{ github.event.repository.name }} PR_HEAD_SHA: ${{github.event.pull_request.head.sha}} - if: | - github.event_name == 'pull_request' && - env.changed_lang_count > 0 + if: github.event_name == 'pull_request' && env.changed_lang_count > 0 run: | curl -X POST https://argoworkflows-dev2.corp.r2c.dev/api/v1/events/security-research/initiate-scan-argo -H "Authorization: ${{ secrets.ARGO_WORKFLOWS_TOKEN }}" -d "{\"branch\" : \"$HEAD_REF\", \"repo\" : \"$REPO_NAME\", \"commit\" : \"$PR_HEAD_SHA\", \"changed_files\" : \"$CHANGED_FILES\" , \"langs\" : \"$CHANGED_LANGS\"}" diff --git a/Makefile b/Makefile index a6e9378e30..5d359b692a 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,65 @@ # # Check rule validity and check that semgrep finds the expected findings. +# See https://semgrep.dev/docs/writing-rules/testing-rules for more info. # -# The semgrep repo also runs this as part of its CI for consistency. +# The semgrep repo (and now semgrep-pro repo) also runs those tests as part +# of its CI for consistency. # .PHONY: test test: $(MAKE) validate $(MAKE) test-only -.PHONY: validate -validate: - ./scripts/run-tests validate +# Use the SEMGREP env variable to specify a non-standard semgrep command +SEMGREP ?= semgrep .PHONY: test-only +#old: pysemgrep --test was also using flags below but not needed +# --test-ignore-todo --strict --disable-version-check --metrics=off --verbose test-only: - ./scripts/run-tests test + $(SEMGREP) test --pro . + +# TODO: semgrep validate use a different targeting than 'semgrep test' +# so we unfortunately need this whitelist of dirs because it reports +# errors on stats/ and scripts/ (and .github/workflows/) files otherwise +# (we also skip libsonnet/ and trusted_python/ which do not contain rules) +LANG_DIRS=\ + bash \ + c \ + clojure \ + csharp \ + dockerfile \ + generic \ + go \ + html \ + java \ + javascript \ + json \ + kotlin \ + ocaml \ + php \ + python \ + ruby \ + rust \ + scala \ + solidity \ + swift \ + terraform \ + typescript \ + yaml +PRO_DIRS=apex elixir +OTHER_DIRS=ai problem-based-packs +DIRS=$(LANG_DIRS) $(PRO_DIRS) $(OTHER_DIRS) + +.PHONY: validate +#old: pysemgrep --validate was also using the flags below but not needed +# --strict --disable-version-check --metrics=off --verbose +validate: + $(SEMGREP) validate --pro $(DIRS) + +.PHONY: test-oss-only +test-oss-only: + @for dir in $(LANG_DIRS) $(OTHER_DIRS); do \ + echo "processing $$dir"; \ + $(SEMGREP) test $$dir; \ + done diff --git a/csharp/dotnet/security/use_ecb_mode.cs b/csharp/dotnet/security/use_ecb_mode.cs index 452a6ec5b4..2eb4ca5ed3 100644 --- a/csharp/dotnet/security/use_ecb_mode.cs +++ b/csharp/dotnet/security/use_ecb_mode.cs @@ -5,7 +5,7 @@ public class Encryption { public void EncryptWithAesEcb() { Aes key = Aes.Create(); - //ruleid: use_ecb_mode + //ruleid: deeptodoruleid: use_ecb_mode key.Mode = CipherMode.ECB; using var encryptor = key.CreateEncryptor(); byte[] msg = new byte[32]; @@ -15,13 +15,13 @@ public void EncryptWithAesEcb() { public void EncryptWithAesEcb2() { Aes key = Aes.Create(); byte[] msg = new byte[32]; - //ruleid: use_ecb_mode + //ruleid: deeptodoruleid: use_ecb_mode var cipherText = key.EncryptEcb(msg, PaddingMode.PKCS7); } public void DecryptWithAesEcb(byte[] cipherText) { Aes key = Aes.Create(); - //ruleid: use_ecb_mode + //ruleid: deeptodoruleid: use_ecb_mode key.Mode = CipherMode.ECB; using var decryptor = key.CreateDecryptor(); var msg = decryptor.TransformFinalBlock(cipherText, 0, cipherText.Length); @@ -29,13 +29,13 @@ public void DecryptWithAesEcb(byte[] cipherText) { public void DecryptWithAesEcb2(byte[] cipherText) { Aes key = Aes.Create(); - //ruleid: use_ecb_mode + //ruleid: deeptodoruleid: use_ecb_mode var msgText = key.DecryptEcb(cipherText, PaddingMode.PKCS7); } public void EncryptWith3DESEcb() { TripleDES key = TripleDES.Create(); - //ruleid: use_ecb_mode + //ruleid: deeptodoruleid: use_ecb_mode key.Mode = CipherMode.ECB; using var encryptor = key.CreateEncryptor(); byte[] msg = new byte[32]; @@ -45,13 +45,13 @@ public void EncryptWith3DESEcb() { public void EncryptWith3DESEcb2() { TripleDES key = TripleDES.Create(); byte[] msg = new byte[32]; - //ruleid: use_ecb_mode + //ruleid: deeptodoruleid: use_ecb_mode var cipherText = key.EncryptEcb(msg, PaddingMode.PKCS7); } public void DecryptWith3DESEcb(byte[] cipherText) { TripleDES key = TripleDES.Create(); - //ruleid: use_ecb_mode + //ruleid: deeptodoruleid: use_ecb_mode key.Mode = CipherMode.ECB; using var decryptor = key.CreateDecryptor(); var msg = decryptor.TransformFinalBlock(cipherText, 0, cipherText.Length); @@ -59,12 +59,12 @@ public void DecryptWith3DESEcb(byte[] cipherText) { public void DecryptWith3DESEcb2(byte[] cipherText) { TripleDES key = TripleDES.Create(); - //ruleid: use_ecb_mode + //ruleid: deeptodoruleid: use_ecb_mode var msgText = key.DecryptEcb(cipherText, PaddingMode.PKCS7); } public void EncryptWithEcb(SymmetricAlgorithm key) { - //ruleid: use_ecb_mode + //ruleid: deeptodoruleid: use_ecb_mode key.Mode = CipherMode.ECB; using var encryptor = key.CreateEncryptor(); byte[] msg = new byte[32]; @@ -73,19 +73,19 @@ public void EncryptWithEcb(SymmetricAlgorithm key) { public void EncryptWithEcb2(SymmetricAlgorithm key) { byte[] msg = new byte[32]; - //ruleid: use_ecb_mode + //ruleid: deeptodoruleid: use_ecb_mode var cipherText = key.EncryptEcb(msg, PaddingMode.PKCS7); } public void DecryptWithEcb(SymmetricAlgorithm key, byte[] cipherText) { - //ruleid: use_ecb_mode + //ruleid: deeptodoruleid: use_ecb_mode key.Mode = CipherMode.ECB; using var decryptor = key.CreateDecryptor(); var msg = decryptor.TransformFinalBlock(cipherText, 0, cipherText.Length); } public void DecryptWithEcb2(SymmetricAlgorithm key, byte[] cipherText) { - //ruleid: use_ecb_mode + //ruleid: deeptodoruleid: use_ecb_mode var msgText = key.DecryptEcb(cipherText, PaddingMode.PKCS7); } @@ -124,4 +124,4 @@ public static void Main() { Console.WriteLine("Hello World"); } -} \ No newline at end of file +} diff --git a/csharp/dotnet/security/use_weak_rng_for_keygeneration.cs b/csharp/dotnet/security/use_weak_rng_for_keygeneration.cs index cd75ffe853..0da244ef47 100644 --- a/csharp/dotnet/security/use_weak_rng_for_keygeneration.cs +++ b/csharp/dotnet/security/use_weak_rng_for_keygeneration.cs @@ -8,7 +8,7 @@ public void GenerateBadKey() { byte[] key = new byte[16]; rng.NextBytes(key); SymmetricAlgorithm cipher = Aes.Create(); - // ruleid: use_weak_rng_for_keygeneration + // ruleid: deeptodoruleid: use_weak_rng_for_keygeneration cipher.Key = key; } diff --git a/csharp/lang/security/stacktrace-disclosure.cs b/csharp/lang/security/stacktrace-disclosure.cs index 9c3bab216c..ffeb42f457 100644 --- a/csharp/lang/security/stacktrace-disclosure.cs +++ b/csharp/lang/security/stacktrace-disclosure.cs @@ -24,3 +24,10 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseExceptionHandler("/Error"); } } + +public void Configure(IApplicationBuilder app, IWebHostEnvironment env) +{ + if (env.IsDevelopment()) + // ok: stacktrace-disclosure + app.UseDeveloperExceptionPage(); +} diff --git a/csharp/lang/security/stacktrace-disclosure.yaml b/csharp/lang/security/stacktrace-disclosure.yaml index 1337b8582b..882989f288 100644 --- a/csharp/lang/security/stacktrace-disclosure.yaml +++ b/csharp/lang/security/stacktrace-disclosure.yaml @@ -4,8 +4,6 @@ rules: - pattern: $APP.UseDeveloperExceptionPage(...); - pattern-not-inside: | if ($ENV.IsDevelopment(...)) { - ... - $APP.UseDeveloperExceptionPage(...); ... } message: >- diff --git a/dockerfile/security/dockerd-socket-mount.dockerfile b/dockerfile/security/dockerd-socket-mount.dockerfile new file mode 100644 index 0000000000..a1b0574974 --- /dev/null +++ b/dockerfile/security/dockerd-socket-mount.dockerfile @@ -0,0 +1,11 @@ +FROM docker:latest + +WORKDIR /app + +# ruleid: dockerfile-dockerd-socket-mount +VOLUME /var/run/docker.sock:/var/run/docker.sock + +# ok: dockerfile-dockerd-socket-mount +VOLUME ./app/main.py:/main.py + +CMD ["docker", "images"] diff --git a/dockerfile/security/dockerd-socket-mount.yaml b/dockerfile/security/dockerd-socket-mount.yaml new file mode 100644 index 0000000000..49a3c34ebc --- /dev/null +++ b/dockerfile/security/dockerd-socket-mount.yaml @@ -0,0 +1,36 @@ +rules: + - id: dockerfile-dockerd-socket-mount + message: >- + The Dockerfile(image) mounts docker.sock to the container which may allow an attacker already inside of the container + to escape container and execute arbitrary commands on the host machine. + languages: + - dockerfile + - yaml + severity: ERROR + metadata: + cwe: + - "CWE-862: Missing Authorization" + - "CWE-269: Improper Privilege Management" + confidence: HIGH + likelihood: MEDIUM + impact: HIGH + subcategory: + - audit + technology: + - dockerfile + category: security + references: + - https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html + - https://redfoxsec.com/blog/insecure-volume-mounts-in-docker/ + - https://blog.quarkslab.com/why-is-exposing-the-docker-socket-a-really-bad-idea.html + pattern-either: + - patterns: + - pattern: VOLUME $X + - metavariable-regex: + metavariable: $X + regex: "/var/run/docker.sock" + - patterns: + - pattern-regex: '- "/var/run/docker.sock:.*"' + - pattern-inside: | + volumes: + ... diff --git a/dockerfile/security/missing-user-entrypoint.dockerfile b/dockerfile/security/missing-user-entrypoint.dockerfile index d3110f1814..f7a6c8e86d 100644 --- a/dockerfile/security/missing-user-entrypoint.dockerfile +++ b/dockerfile/security/missing-user-entrypoint.dockerfile @@ -9,6 +9,5 @@ RUN pip3 install semgrep # ruleid: missing-user-entrypoint ENTRYPOINT semgrep -f p/xss -# TODO: metavar bug -# ok: missing-user-entrypoint +# ruleid: missing-user-entrypoint ENTRYPOINT ["semgrep", "--config", "localfile", "targets"] diff --git a/dockerfile/security/missing-user-entrypoint.fixed.dockerfile b/dockerfile/security/missing-user-entrypoint.fixed.dockerfile index 170b6fd3bd..4e5ed36b01 100644 --- a/dockerfile/security/missing-user-entrypoint.fixed.dockerfile +++ b/dockerfile/security/missing-user-entrypoint.fixed.dockerfile @@ -10,6 +10,6 @@ RUN pip3 install semgrep USER non-root ENTRYPOINT semgrep -f p/xss -# TODO: metavar bug -# ok: missing-user-entrypoint +# ruleid: missing-user-entrypoint +USER non-root ENTRYPOINT ["semgrep", "--config", "localfile", "targets"] diff --git a/dockerfile/security/missing-user.dockerfile b/dockerfile/security/missing-user.dockerfile index a089ea0e8d..a5dc45a569 100644 --- a/dockerfile/security/missing-user.dockerfile +++ b/dockerfile/security/missing-user.dockerfile @@ -12,6 +12,5 @@ CMD semgrep -f p/xss # ruleid: missing-user CMD semgrep --config localfile targets -# TODO: metavar ellipses bug -# ok: missing-user +# ruleid: missing-user CMD ["semgrep", "--version"] diff --git a/dockerfile/security/missing-user.fixed.dockerfile b/dockerfile/security/missing-user.fixed.dockerfile index 90c753f54e..28756154d1 100644 --- a/dockerfile/security/missing-user.fixed.dockerfile +++ b/dockerfile/security/missing-user.fixed.dockerfile @@ -14,6 +14,6 @@ CMD semgrep -f p/xss USER non-root CMD semgrep --config localfile targets -# TODO: metavar ellipses bug -# ok: missing-user +# ruleid: missing-user +USER non-root CMD ["semgrep", "--version"] diff --git a/fingerprints/fingerprints.yaml b/fingerprints/fingerprints.yaml deleted file mode 100644 index 96472bb79a..0000000000 --- a/fingerprints/fingerprints.yaml +++ /dev/null @@ -1,826 +0,0 @@ -rules: - - id: cai-generic-technology-docker-compose - languages: - - generic - message: - Detected a docker-compose.yml file. This likely indicates that Docker and - docker-compose are in use. - metadata: - license: Semgrep Registry License - paths: - include: - - docker-compose.yml - pattern-regex: ^. - severity: INVENTORY - - id: cai-generic-technology-dockerfile - languages: - - generic - message: Detected a Dockerfile file. This likely indicates that Docker is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - Dockerfile - pattern-regex: ^. - severity: INVENTORY - - id: cai-generic-technology-kubernetes - languages: - - generic - message: - Detected a Kubernetes configuration file. This likely indicates that Kubernetes - is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.yaml" - - "*.yml" - pattern: "apiVersion: ...\nkind: ...\nmetadata:\n ...\nspec:\n ...\n" - severity: INVENTORY - - id: cai-generic-technology-travis - languages: - - generic - message: - Detected a .travis.yml file. This likely indicates that TravisCI is in - use. - metadata: - license: Semgrep Registry License - paths: - include: - - .travis.yml - pattern-regex: ^. - severity: INVENTORY - - id: cai-generic-technology-circleci - languages: - - generic - message: Detected a CircleCI file. This likely indicates that CircleCI is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - .circleci/config.yml - - circle.yml - pattern-regex: ^. - severity: INVENTORY - - id: cai-generic-technology-github-actions - languages: - - generic - message: - Detected a Github Actions file. This likely indicates that Github Actions - is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - .github/workflows/*.yaml - - .github/workflows/*.yml - pattern-regex: ^. - severity: INVENTORY - - id: cai-generic-technology-gitlab - languages: - - generic - message: Detected a GitLab file. This likely indicates that GitLab is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - .gitlab-ci.yml - pattern-regex: ^. - severity: INVENTORY - - id: cai-java-dependency-spring - languages: - - generic - message: "Detected `spring` as a dependency in a pom.xml file. - - This likely indicates that Spring framework is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - pom.xml - patterns: - - pattern: - "\n ...\n spring-...\n \ - \ ...\n\n" - - pattern-not-inside: "\n ...\n\n" - severity: INVENTORY - - id: cai-java-framework-spring - languages: - - java - message: - Detected `spring` being imported, indicating this app uses the Java spring - web framework - metadata: - license: Semgrep Registry License - pattern: "import org.springframework.$PATH; - - " - severity: INVENTORY - - id: cai-javascript-dependency-angular-core-yarn - languages: - - generic - message: "Detected `angular-core` as a dependency in a yarn.lock file. - - This likely indicates that Angular frontend framework is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - yarn.lock - pattern-either: - - pattern: "@angular/core@$VERSION" - - pattern: "@angular/core@^$VERSION" - - pattern: "@angular/core@~$VERSION" - - pattern: "@angular/core@>$VERSION" - - pattern: "@angular/core@<$VERSION" - - pattern: "@angular/core@>=$VERSION" - - pattern: "@angular/core@<=$VERSION" - severity: INVENTORY - - id: cai-javascript-dependency-angular-core - languages: - - json - message: "Detected `angular-core` as a dependency in a package.json file. - - This likely indicates that Angular frontend framework is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - package.json - patterns: - - pattern-either: - - pattern: - "{\n ...,\n \"dependencies\": {\n ...,\n \"@angular/core\"\ - : $VERSION\n }\n}\n" - - pattern: - "{\n ...,\n \"devDependencies\": {\n ...,\n \"@angular/core\"\ - : $VERSION\n }\n}\n" - severity: INVENTORY - - id: cai-javascript-dependency-angular-yarn - languages: - - generic - message: "Detected `angular` as a dependency in a yarn.lock file. - - This likely indicates that Angular frontend framework is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - yarn.lock - pattern-either: - - pattern: angular@$VERSION - - pattern: angular@^$VERSION - - pattern: angular@~$VERSION - - pattern: angular@>$VERSION - - pattern: angular@<$VERSION - - pattern: angular@>=$VERSION - - pattern: angular@<=$VERSION - severity: INVENTORY - - id: cai-javascript-dependency-angular - languages: - - json - message: "Detected `angular` as a dependency in a package.json file. - - This likely indicates that Angular frontend framework is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - package.json - patterns: - - pattern-either: - - pattern: - "{\n ...,\n \"dependencies\": {\n ...,\n \"angular\": $VERSION\n\ - \ }\n}\n" - - pattern: - "{\n ...,\n \"devDependencies\": {\n ...,\n \"angular\": $VERSION\n\ - \ }\n}\n" - severity: INVENTORY - - id: cai-javascript-dependency-vue-yarn - languages: - - generic - message: "Detected `vue` as a dependency in a yarn.lock file. - - This likely indicates that vue - a JS MVC framework. is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - yarn.lock - pattern-either: - - pattern: vue@$VERSION - - pattern: vue@^$VERSION - - pattern: vue@~$VERSION - - pattern: vue@>$VERSION - - pattern: vue@<$VERSION - - pattern: vue@>=$VERSION - - pattern: vue@<=$VERSION - severity: INVENTORY - - id: cai-javascript-dependency-vue - languages: - - json - message: "Detected `vue` as a dependency in a package.json file. - - This likely indicates that vue - a JS MVC framework. is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - package.json - patterns: - - pattern-either: - - pattern: - "{\n ...,\n \"dependencies\": {\n ...,\n \"vue\": $VERSION\n\ - \ }\n}\n" - - pattern: - "{\n ...,\n \"devDependencies\": {\n ...,\n \"vue\": $VERSION\n\ - \ }\n}\n" - severity: INVENTORY - - id: cai-javascript-dependency-expressjs-yarn - languages: - - generic - message: "Detected `expressjs` as a dependency in a yarn.lock file. - - This likely indicates that NodeJS web framework Express JS is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - yarn.lock - pattern-either: - - pattern: express@$VERSION - - pattern: express@^$VERSION - - pattern: express@~$VERSION - - pattern: express@>$VERSION - - pattern: express@<$VERSION - - pattern: express@>=$VERSION - - pattern: express@<=$VERSION - severity: INVENTORY - - id: cai-javascript-dependency-expressjs - languages: - - json - message: "Detected `expressjs` as a dependency in a package.json file. - - This likely indicates that NodeJS web framework Express JS is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - package.json - patterns: - - pattern-either: - - pattern: - "{\n ...,\n \"dependencies\": {\n ...,\n \"express\": $VERSION\n\ - \ }\n}\n" - - pattern: - "{\n ...,\n \"devDependencies\": {\n ...,\n \"express\": $VERSION\n\ - \ }\n}\n" - severity: INVENTORY - - id: cai-javascript-dependency-jsonwebtoken-yarn - languages: - - generic - message: "Detected `jsonwebtoken` as a dependency in a yarn.lock file. - - This likely indicates that JWT is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - yarn.lock - pattern-either: - - pattern: jsonwebtoken@$VERSION - - pattern: jsonwebtoken@^$VERSION - - pattern: jsonwebtoken@~$VERSION - - pattern: jsonwebtoken@>$VERSION - - pattern: jsonwebtoken@<$VERSION - - pattern: jsonwebtoken@>=$VERSION - - pattern: jsonwebtoken@<=$VERSION - severity: INVENTORY - - id: cai-javascript-dependency-jsonwebtoken - languages: - - json - message: "Detected `jsonwebtoken` as a dependency in a package.json file. - - This likely indicates that JWT is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - package.json - patterns: - - pattern-either: - - pattern: - "{\n ...,\n \"dependencies\": {\n ...,\n \"jsonwebtoken\":\ - \ $VERSION\n }\n}\n" - - pattern: - "{\n ...,\n \"devDependencies\": {\n ...,\n \"jsonwebtoken\"\ - : $VERSION\n }\n}\n" - severity: INVENTORY - - id: cai-javascript-dependency-pug-yarn - languages: - - generic - message: "Detected `pug` as a dependency in a yarn.lock file. - - This likely indicates that Pug template engine is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - yarn.lock - pattern-either: - - pattern: pug@$VERSION - - pattern: pug@^$VERSION - - pattern: pug@~$VERSION - - pattern: pug@>$VERSION - - pattern: pug@<$VERSION - - pattern: pug@>=$VERSION - - pattern: pug@<=$VERSION - severity: INVENTORY - - id: cai-javascript-dependency-pug - languages: - - json - message: "Detected `pug` as a dependency in a package.json file. - - This likely indicates that Pug template engine is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - package.json - patterns: - - pattern-either: - - pattern: - "{\n ...,\n \"dependencies\": {\n ...,\n \"pug\": $VERSION\n\ - \ }\n}\n" - - pattern: - "{\n ...,\n \"devDependencies\": {\n ...,\n \"pug\": $VERSION\n\ - \ }\n}\n" - severity: INVENTORY - - id: cai-javascript-dependency-puppeteer-yarn - languages: - - generic - message: "Detected `puppeteer` as a dependency in a yarn.lock file. - - This likely indicates that headless-browser is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - yarn.lock - pattern-either: - - pattern: puppeteer@$VERSION - - pattern: puppeteer@^$VERSION - - pattern: puppeteer@~$VERSION - - pattern: puppeteer@>$VERSION - - pattern: puppeteer@<$VERSION - - pattern: puppeteer@>=$VERSION - - pattern: puppeteer@<=$VERSION - severity: INVENTORY - - id: cai-javascript-dependency-puppeteer - languages: - - json - message: "Detected `puppeteer` as a dependency in a package.json file. - - This likely indicates that headless-browser is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - package.json - patterns: - - pattern-either: - - pattern: - "{\n ...,\n \"dependencies\": {\n ...,\n \"puppeteer\": $VERSION\n\ - \ }\n}\n" - - pattern: - "{\n ...,\n \"devDependencies\": {\n ...,\n \"puppeteer\":\ - \ $VERSION\n }\n}\n" - severity: INVENTORY - - id: cai-javascript-dependency-react-yarn - languages: - - generic - message: "Detected `react` as a dependency in a yarn.lock file. - - This likely indicates that React frontend framework is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - yarn.lock - pattern-either: - - pattern: react@$VERSION - - pattern: react@^$VERSION - - pattern: react@~$VERSION - - pattern: react@>$VERSION - - pattern: react@<$VERSION - - pattern: react@>=$VERSION - - pattern: react@<=$VERSION - severity: INVENTORY - - id: cai-javascript-dependency-react - languages: - - json - message: "Detected `react` as a dependency in a package.json file. - - This likely indicates that React frontend framework is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - package.json - patterns: - - pattern-either: - - pattern: - "{\n ...,\n \"dependencies\": {\n ...,\n \"react\": $VERSION\n\ - \ }\n}\n" - - pattern: - "{\n ...,\n \"devDependencies\": {\n ...,\n \"react\": $VERSION\n\ - \ }\n}\n" - severity: INVENTORY - - id: cai-javascript-framework-expressjs - languages: - - javascript - message: - Detected `express` being required and used, meaning this app uses the ExpressJS - web framework. - metadata: - license: Semgrep Registry License - pattern: 'var $IMPORT = require("express"); - - ... - - var $APP = $IMPORT(); - - ' - severity: INVENTORY - - id: cai-python-dependency-boto - languages: - - generic - message: "Detected `boto` as a dependency in a requirements.txt file. - - This likely indicates that AWS SDK for Python is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - requirements.txt - - requirements.in - - setup.py - pattern-regex: boto([3|core])* - severity: INVENTORY - - id: cai-python-dependency-django - languages: - - generic - message: "Detected `django` as a dependency in a requirements.txt file. - - This likely indicates that Detected `django` as a dependency in a requirements.txt - file. This likely indicates that the Python web framework Django is being used. - is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - requirements.txt - - requirements.in - - setup.py - pattern-regex: "[d|D]jango" - severity: INVENTORY - - id: cai-python-dependency-fastapi - languages: - - generic - message: "Detected `fastapi` as a dependency in a requirements.txt file. - - This likely indicates that Python web framework FastAPI is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - requirements.txt - - requirements.in - - setup.py - pattern-regex: fastapi - severity: INVENTORY - - id: cai-python-dependency-flask - languages: - - generic - message: "Detected `flask` as a dependency in a requirements.txt file. - - This likely indicates that Detected `flask` as a dependency in a requirements.txt - file. This likely indicates that the Python web framework Flask is being used. - is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - requirements.txt - - requirements.in - - setup.py - patterns: - - pattern-regex: "[Ff]lask" - - pattern-not-regex: "[Ff]lask-" - - pattern-not-regex: -[Ff]lask - severity: INVENTORY - - id: cai-python-dependency-jinja - languages: - - generic - message: "Detected `jinja` as a dependency in a requirements.txt file. - - This likely indicates that Jinja template engine is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - requirements.txt - - requirements.in - - setup.py - pattern-regex: "[Jj]inja" - severity: INVENTORY - - id: cai-python-dependency-sqlalchemy - languages: - - generic - message: "Detected `sqlalchemy` as a dependency in a requirements.txt file. - - This likely indicates that database is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - requirements.txt - - requirements.in - - setup.py - pattern-regex: "[Ss][Qq][Ll][Aa]lchemy" - severity: INVENTORY - - id: cai-python-framework-django - languages: - - python - message: - Detected `django.http` being imported, indicating this app uses the Python - Django web framework - metadata: - license: Semgrep Registry License - pattern: "import django.http.$FOO - - " - severity: INVENTORY - - id: cai-python-framework-flask - languages: - - python - message: - Detected `Flask()` being instantiated, indicating this app uses the Python - Flask web framework - metadata: - license: Semgrep Registry License - pattern: "$FLASK = Flask(...) - - " - severity: INVENTORY - - id: cai-ocaml-technology-opam - languages: - - generic - message: Detected a OPAM file. This likely indicates OCaml and OPAM are being used. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.opam" - pattern-regex: ^.* - severity: INVENTORY - - id: cai-ocaml-technology-dune - languages: - - generic - message: Detected a dune file. This likely indicates OCaml and dune are being used. - metadata: - license: Semgrep Registry License - paths: - include: - - dune - - dune-project - pattern-regex: ^.* - severity: INVENTORY - - id: cai-ruby-dependency-rails - languages: - - generic - message: "Detected `rails` as a dependency in a Gemfile file. - - This likely indicates that Ruby web framework Rails is being used." - metadata: - license: Semgrep Registry License - paths: - include: - - Gemfile - pattern-regex: gem ['"]rails - severity: INVENTORY - - id: cai-ruby-framework-rails - languages: - - ruby - message: - Detected a rails route being created, indicating this app uses the Ruby - rails web framework - metadata: - license: Semgrep Registry License - pattern: "Rails.application.routes.draw do \n ...\nend\n" - severity: INVENTORY - - id: cai-generic-technology-nginx - languages: - - generic - message: Detected a .conf file. This likely indicates that Nginx is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.conf" - pattern: "location ... {\n ...\n ...\n ...\n}\n" - severity: INVENTORY - - id: cai-hcl-technology-terraform - languages: - - generic - message: Detected a .tf file. This likely indicates that Terraform is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.tf" - pattern: "terraform {\n ...\n ...\n ...\n}\n" - severity: INVENTORY - - id: cai-shell-technology-bash - languages: - - generic - message: Detected a bash script. This likely indicates bash is being used. - metadata: - license: Semgrep Registry License - pattern-either: - - pattern: "#!/bin/bash" - - pattern: "#!/usr/bin/env ... bash" - severity: INVENTORY - - id: cai-shell-technology-sh - languages: - - generic - message: Detected a sh script. This likely indicates sh is being used. - metadata: - license: Semgrep Registry License - pattern-either: - - pattern: "#!/bin/sh" - - pattern: "#!/usr/bin/env ... sh" - severity: INVENTORY - - id: cai-c-technology-c - languages: - - generic - message: Detected a .c file. This likely indicates C is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.c" - pattern-regex: ^. - severity: INVENTORY - - id: cai-csharp-technology-csharp - languages: - - generic - message: Detected a .cs file. This likely indicates C is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.cs" - pattern-regex: ^. - severity: INVENTORY - - id: cai-go-technology-go - languages: - - generic - message: Detected a .go file. This likely indicates Go is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.go" - pattern-regex: ^. - severity: INVENTORY - - id: cai-html-technology-html - languages: - - generic - message: Detected a .html file. This likely indicates html is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.html" - pattern-regex: ^. - severity: INVENTORY - - id: cai-java-technology-java - languages: - - generic - message: Detected a .java file. This likely indicates Java is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.java" - pattern-regex: ^. - severity: INVENTORY - - id: cai-javascript-technology-javascript - languages: - - generic - message: Detected a .js file. This likely indicates JavaScript is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.js" - - "*.javascript" - pattern-regex: ^. - severity: INVENTORY - - id: cai-kotlin-technology-kotlin - languages: - - generic - message: Detected a .kt file. This likely indicates Kotlin is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.kt" - pattern-regex: ^. - severity: INVENTORY - - id: cai-ocaml-technology-ocaml - languages: - - generic - message: Detected a .ml file. This likely indicates Ocaml is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.ml" - pattern-regex: ^. - severity: INVENTORY - - id: cai-php-technology-php - languages: - - generic - message: Detected a .php file. This likely indicates PHP is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.phtml" - - "*.php" - - "*.php3" - - "*.php4" - - "*.php5" - - "*.phps" - pattern-regex: ^. - severity: INVENTORY - - id: cai-python-technology-python - languages: - - generic - message: Detected a .py file. This likely indicates py is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.py" - pattern-regex: ^. - severity: INVENTORY - - id: cai-ruby-technology-ruby - languages: - - generic - message: Detected a .rb file. This likely indicates Ruby is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.rb" - - "*.erb" - pattern-regex: ^. - severity: INVENTORY - - id: cai-scala-technology-scala - languages: - - generic - message: Detected a .scala file. This likely indicates Scala is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.scala" - - "*.sc" - pattern-regex: ^. - severity: INVENTORY - - id: cai-terraform-technology-terraform - languages: - - generic - message: Detected a .tf file. This likely indicates Terraform is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.tf" - pattern-regex: ^. - severity: INVENTORY - - id: cai-typescript-technology-typescript - languages: - - generic - message: Detected a .ts file. This likely indicates TypeScript is in use. - metadata: - license: Semgrep Registry License - paths: - include: - - "*.ts" - - "*.typescript" - pattern-regex: ^. - severity: INVENTORY diff --git a/generic/secrets/security/detected-slack-webhook.txt b/generic/secrets/security/detected-slack-webhook.txt index 6e5fde8401..aa4a91c3ae 100644 --- a/generic/secrets/security/detected-slack-webhook.txt +++ b/generic/secrets/security/detected-slack-webhook.txt @@ -2,4 +2,7 @@ https://hooks.slack.com/services/T12345678/B12345678/abcd1234efgh5678ijkl90zy # ruleid: detected-slack-webhook -https://hooks.slack.com/services/T12345678/B1234567890/abcd1234efgh5678ijkl90zy \ No newline at end of file +https://hooks.slack.com/services/T12345678/B1234567890/abcd1234efgh5678ijkl90zy + +# ok: detected-slack-webhook +https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX diff --git a/generic/secrets/security/detected-slack-webhook.yaml b/generic/secrets/security/detected-slack-webhook.yaml index 51cf6ba6d7..0f35a67a06 100644 --- a/generic/secrets/security/detected-slack-webhook.yaml +++ b/generic/secrets/security/detected-slack-webhook.yaml @@ -1,6 +1,8 @@ rules: - id: detected-slack-webhook - pattern-regex: https://hooks\.slack\.com/services/T[a-zA-Z0-9_]{8,10}/B[a-zA-Z0-9_]{8,10}/[a-zA-Z0-9_]{24} + patterns: + - pattern-regex: https://hooks\.slack\.com/services/T[a-zA-Z0-9_]{8,10}/B[a-zA-Z0-9_]{8,10}/[a-zA-Z0-9_]{24} + - pattern-not: https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX languages: [regex] message: Slack Webhook detected severity: ERROR diff --git a/go/lang/security/reverseproxy-director.go b/go/lang/security/reverseproxy-director.go new file mode 100644 index 0000000000..0a7b1762e1 --- /dev/null +++ b/go/lang/security/reverseproxy-director.go @@ -0,0 +1,65 @@ +package main + +import ( + "log" + "net/http" + "net/http/httputil" + "net/url" +) + +func NewProxy(targetHost string) (*httputil.ReverseProxy, error) { + url, err := url.Parse(targetHost) + if err != nil { + return nil, err + } + + proxy := httputil.NewSingleHostReverseProxy(url) + + originalDirector := proxy.Director + // ruleid: reverseproxy-director + proxy.Director = func(req *http.Request) { + originalDirector(req) + modifyRequest(req) + } + return proxy, nil +} + +func modifyRequest(req *http.Request) { + req.Header.Set("Extra-Header", "nice") +} + +func ProxyRequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + proxy.ServeHTTP(w, r) + } +} + +type Fake struct { + Director string +} + +func extraCases() { + rp := &httputil.ReverseProxy{ + // ruleid: reverseproxy-director + Director: func(req *http.Request) { + modifyRequest(req) + }, + } + _ = rp + + f := Fake{ + // ok: reverseproxy-director + Director: "abcd", + } + _ = f +} + +func main() { + proxy, err := NewProxy("https://example.com") + if err != nil { + panic(err) + } + + http.HandleFunc("/", ProxyRequestHandler(proxy)) + log.Fatal(http.ListenAndServe(":8080", nil)) +} diff --git a/go/lang/security/reverseproxy-director.yaml b/go/lang/security/reverseproxy-director.yaml new file mode 100644 index 0000000000..ba210b6c72 --- /dev/null +++ b/go/lang/security/reverseproxy-director.yaml @@ -0,0 +1,33 @@ +rules: +- id: reverseproxy-director + message: >- + ReverseProxy can remove headers added by Director. Consider using ReverseProxy.Rewrite + instead of ReverseProxy.Director. + languages: [go] + severity: WARNING + patterns: + - pattern-inside: | + import "net/http/httputil" + ... + - pattern-either: + - pattern: $PROXY.Director = $FUNC + - patterns: + - pattern-inside: | + httputil.ReverseProxy{ + ... + } + - pattern: | + Director: $FUNC + metadata: + cwe: + - "CWE-115: Misinterpretation of Input" + category: security + subcategory: + - audit + technology: + - go + confidence: MEDIUM + likelihood: LOW + impact: LOW + references: + - https://github.com/golang/go/issues/50580 diff --git a/go/lang/security/shared-url-struct-mutation.go b/go/lang/security/shared-url-struct-mutation.go new file mode 100644 index 0000000000..005d20075b --- /dev/null +++ b/go/lang/security/shared-url-struct-mutation.go @@ -0,0 +1,118 @@ +package main + +import ( + "net/http" + "net/url" +) + +var redirectURL, _ = url.Parse("https://example.com") + +func getRedirectToken() (string, error) { + return "abcd", nil +} + +func handler1(w http.ResponseWriter, r *http.Request) { + u := redirectURL + q := u.Query() + + // opaque process that might fail + token, err := getRedirectToken() + if err != nil { + q.Set("error", err.Error()) + } else { + q.Set("token", token) + } + // ruleid: shared-url-struct-mutation + u.RawQuery = q.Encode() + r.URL.RawQuery = q.Encode() + + http.Redirect(w, r, u.String(), http.StatusFound) +} + +func handler2(w http.ResponseWriter, r *http.Request) { + u, _ := url.Parse("https://example.com") + + q := u.Query() + + // opaque process that might fail + token, err := getRedirectToken() + if err != nil { + q.Set("error", err.Error()) + } else { + q.Set("token", token) + } + // ok: shared-url-struct-mutation + u.RawQuery = q.Encode() + + http.Redirect(w, r, u.String(), http.StatusFound) +} + +func handler3(w http.ResponseWriter, r *http.Request) { + u := url.URL{ + Scheme: "https", + Host: "example.com", + Path: "/", + } + q := u.Query() + + // opaque process that might fail + token, err := getRedirectToken() + if err != nil { + q.Set("error", err.Error()) + } else { + q.Set("token", token) + } + + u.RawQuery = q.Encode() + + http.Redirect(w, r, u.String(), http.StatusFound) +} + +func handler4(w http.ResponseWriter, r *http.Request) { + var u *url.URL + if true { + u, _ = url.Parse("https://example.com") + } + + if u != nil { + + q := u.Query() + + // opaque process that might fail + token, err := getRedirectToken() + if err != nil { + q.Set("error", err.Error()) + } else { + q.Set("token", token) + } + // ok: shared-url-struct-mutation + u.RawQuery = q.Encode() + + http.Redirect(w, r, u.String(), http.StatusFound) + } + http.Redirect(w, r, "https://google.com", http.StatusFound) +} + +func extraCases(w http.ResponseWriter, r *http.Request) { + var x struct { + y []struct { + Path string + } + } + // ok: shared-url-struct-mutation + r.URL.RawQuery = "abcd" + // ok: shared-url-struct-mutation + x.y[0].Path = "abcd" + + a, _ := url.ParseRequestURI("https://example.com") + // ok: shared-url-struct-mutation + a.RawQuery = "abcd" +} + +func main() { + http.HandleFunc("/1", handler1) + http.HandleFunc("/2", handler2) + http.HandleFunc("/3", handler3) + http.HandleFunc("/4", handler4) + http.ListenAndServe(":7777", nil) +} diff --git a/go/lang/security/shared-url-struct-mutation.yaml b/go/lang/security/shared-url-struct-mutation.yaml new file mode 100644 index 0000000000..0dcd483a15 --- /dev/null +++ b/go/lang/security/shared-url-struct-mutation.yaml @@ -0,0 +1,52 @@ +rules: +- id: shared-url-struct-mutation + message: >- + Shared URL struct may have been accidentally mutated. Ensure that + this behavior is intended. + languages: [go] + severity: WARNING + patterns: + - pattern-inside: | + import "net/url" + ... + - pattern-not-inside: | + ... = url.Parse(...) + ... + - pattern-not-inside: | + ... = url.ParseRequestURI(...) + ... + - pattern-not-inside: | + ... = url.URL{...} + ... + - pattern-not-inside: | + var $URL *$X.URL + ... + - pattern-either: + - pattern: $URL.RawQuery = ... + - pattern: $URL.Path = ... + - pattern: $URL.RawPath = ... + - pattern: $URL.Fragment = ... + - pattern: $URL.RawFragment = ... + - pattern: $URL.Scheme = ... + - pattern: $URL.Opaque = ... + - pattern: $URL.Host = ... + - pattern: $URL.User = ... + - metavariable-pattern: + metavariable: $URL + patterns: + - pattern-not: $X.$Y + - pattern-not: $X[...] + metadata: + cwe: + - "CWE-436: Interpretation Conflict" + category: security + subcategory: + - audit + technology: + - go + confidence: LOW + likelihood: LOW + impact: LOW + references: + - https://github.com/golang/go/issues/63777 + diff --git a/javascript/express/security/audit/xss/mustache/var-in-href.mustache b/javascript/express/security/audit/xss/mustache/var-in-href.mustache deleted file mode 100644 index b47e8200f0..0000000000 --- a/javascript/express/security/audit/xss/mustache/var-in-href.mustache +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - Demo Mustache.JS - - - - - - - - - -
-
-
-
- - - - - - - - - - diff --git a/javascript/express/security/audit/xss/mustache/var-in-href.yaml b/javascript/express/security/audit/xss/mustache/var-in-href.yaml deleted file mode 100644 index 6f6148dca9..0000000000 --- a/javascript/express/security/audit/xss/mustache/var-in-href.yaml +++ /dev/null @@ -1,38 +0,0 @@ -rules: -- id: var-in-href - message: >- - Detected a template variable used in an anchor tag with - the 'href' attribute. This allows a malicious actor to - input the 'javascript:' URI and is subject to cross- - site scripting (XSS) attacks. If using a relative URL, - start with a literal forward slash and concatenate the URL, - like this: href='/{{link}}'. You may also consider setting - the Content Security Policy (CSP) header. - metadata: - cwe: - - "CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')" - owasp: - - A07:2017 - Cross-Site Scripting (XSS) - - A03:2021 - Injection - references: - - https://flask.palletsprojects.com/en/1.1.x/security/#cross-site-scripting-xss#:~:text=javascript:%20URI - - https://github.com/pugjs/pug/issues/2952 - category: security - technology: - - express - cwe2022-top25: true - cwe2021-top25: true - subcategory: - - audit - likelihood: LOW - impact: MEDIUM - confidence: LOW - languages: - - regex - severity: WARNING - paths: - include: - - '*.mustache' - - '*.hbs' - - '*.html' - pattern-regex: - + The 'final' call of a Decipher object checks the authentication tag in a mode for authenticated encryption. + Failing to call 'final' will invalidate all integrity guarantees of the released ciphertext. + metadata: + cwe: + - 'CWE-310: CWE CATEGORY: Cryptographic Issues' + owasp: + - A02:2021 - Cryptographic Failures + category: security + subcategory: + - vuln + technology: + - node-crypto + likelihood: HIGH + impact: MEDIUM + confidence: HIGH + references: + - https://nodejs.org/api/crypto.html#deciphersetauthtagbuffer-encoding + - https://owasp.org/Top10/A02_2021-Cryptographic_Failures/ + languages: + - javascript + - typescript + severity: ERROR + patterns: + - pattern: | + $DECIPHER = $CRYPTO.createDecipheriv('$ALGO', ...) + ... + $DECIPHER.update(...) + - pattern-not-inside: | + $DECIPHER = $CRYPTO.createDecipheriv('$ALGO', ...) + ... + $DECIPHER.final(...) + - metavariable-regex: + metavariable: $ALGO + regex: ".*(-gcm|-ccm|-ocb|chacha20-poly1305)$" diff --git a/javascript/node-crypto/security/create-de-cipher-no-iv.js b/javascript/node-crypto/security/create-de-cipher-no-iv.js new file mode 100644 index 0000000000..e45a53fc44 --- /dev/null +++ b/javascript/node-crypto/security/create-de-cipher-no-iv.js @@ -0,0 +1,53 @@ +const crypto = require('node:crypto') +function decrypt1(ciphertext, key) { + iv = ciphertext.iv + encryptedData = ciphertext.data + + // ruleid: create-de-cipher-no-iv + const decipher = crypto.createDecipher("aes-128-gcm", key, iv); + let result = decipher.update(encryptedData); + + return result.toString("utf8"); +} + +function decrypt2(ciphertext, key) { + encryptedData = ciphertext.data + auth = ciphertext.auth + + // ruleid: create-de-cipher-no-iv + var decipher = crypto.createDecipher("aes-192-cbc", key); + let result = decipher.update(encryptedData); + + return result.toString("utf8"); +} + +const { createCipher } = require('node:crypto') +function encrypt1(plaintext, key) { + // ruleid: create-de-cipher-no-iv + const cipher = createCipher("aes-256-ccm", key, {authTagLength: 16}) + cipher.setAAD(Buffer.alloc(0), {plaintextLength: plaintext.length}) + let result = cipher.update(plaintext) + cipher.final() + + return result + cipher.getAuthTag() +} + +function decrypt3(key, ciphertext) { + let encrypted = Buffer.from(ciphertext, 'base64'); + let iv = encrypted.slice(encrypted.byteLength - 12, encrypted.byteLength); + + // ok: create-de-cipher-no-iv + let decipher = crypto.createDecipheriv("aes-256-ocb", key, iv, {authTagLength: 16}); + let decrypted = decipher.update(encrypted.slice(0, encrypted.byteLength - 16 - 12)); + + decrypted = Buffer.concat([decrypted]); + return decrypted; +} + +function encrypt2(key, plaintext) { + let iv = crypto.randomBytes(12); + + // ok: create-de-cipher-no-iv + let cipher = crypto.createCipheriv("chacha20-poly1305", key, iv); + let encrypted = Buffer.concat([cipher.update(plaintext), cipher.final(), cipher.getAuthTag()]); + return encrypted; +} diff --git a/javascript/node-crypto/security/create-de-cipher-no-iv.yaml b/javascript/node-crypto/security/create-de-cipher-no-iv.yaml new file mode 100644 index 0000000000..3cd40e1575 --- /dev/null +++ b/javascript/node-crypto/security/create-de-cipher-no-iv.yaml @@ -0,0 +1,31 @@ +rules: +- id: create-de-cipher-no-iv + message: >- + The deprecated functions 'createCipher' and 'createDecipher' generate the same initialization vector every time. + For counter modes such as CTR, GCM, or CCM this leads to break of both confidentiality and integrity, if the key is used more than once. + Other modes are still affected in their strength, though they're not completely broken. + Use 'createCipheriv' or 'createDecipheriv' instead. + metadata: + cwe: + - 'CWE-1204: Generation of Weak Initialization Vector (IV)' + category: security + subcategory: + - vuln + technology: + - node-crypto + likelihood: HIGH + impact: MEDIUM + confidence: HIGH + references: + - https://nodejs.org/api/crypto.html#cryptocreatecipheralgorithm-password-options + - https://nodejs.org/api/crypto.html#cryptocreatedecipheralgorithm-password-options + languages: + - javascript + - typescript + severity: ERROR + patterns: + - pattern-either: + - pattern: | + $CRYPTO.createCipher(...) + - pattern: | + $CRYPTO.createDecipher(...) diff --git a/javascript/node-crypto/security/gcm-no-tag-length.js b/javascript/node-crypto/security/gcm-no-tag-length.js new file mode 100644 index 0000000000..3c0cfa761e --- /dev/null +++ b/javascript/node-crypto/security/gcm-no-tag-length.js @@ -0,0 +1,84 @@ +const crypto = require('node:crypto') +function decrypt1(ciphertext, key) { + iv = ciphertext.iv + encryptedData = ciphertext.data + auth = ciphertext.auth + + algo = "aes-128-gcm" + + // ruleid: gcm-no-tag-length + const decipher = crypto.createDecipheriv(algo, key, iv); + decipher.setAuthTag(auth); + let result = decipher.update(encryptedData) + decipher.final(); + + return result.toString("utf8"); +} + +function decrypt2(ciphertext, key) { + iv = ciphertext.iv + encryptedData = ciphertext.data + auth = ciphertext.auth + // While this prevents the attack, I'm not quite sure how to capture such length checks and exclude them from the findings + assert(auth.length === 16) + + // ruleid: gcm-no-tag-length + const decipher = crypto.createDecipheriv("aes-192-gcm", key, iv); + decipher.setAuthTag(auth); + let result = decipher.update(encryptedData) + decipher.final(); + + return result.toString("utf8"); +} + +function decrypt3(ciphertext, key) { + iv = ciphertext.iv + encryptedData = ciphertext.data + auth = ciphertext.auth + + // ok: gcm-no-tag-length + var decipher = crypto.createDecipheriv("aes-128-gcm", key, iv, {authTagLength: 16}); + decipher.setAuthTag(auth); + let result = Buffer.concat([decipher.update(encryptedData), decipher.final()]); + + return result; +} + +function decrypt4(ciphertext, key) { + iv = ciphertext.iv + encryptedData = ciphertext.data + auth = ciphertext.auth + + // even though auth tag is shorter than it should be, this looks like a conscious choice + // ok: gcm-no-tag-length + var decipher = crypto.createDecipheriv("aes-256-gcm", key, iv, {authTagLength: 4}); + decipher.setAuthTag(auth); + let result = Buffer.concat([decipher.update(encryptedData), decipher.final()]); + + return result; +} + +function decrypt5(ciphertext, key) { + iv = ciphertext.iv + encryptedData = ciphertext.data + auth = ciphertext.auth + + // hard to capture whether options contain 'authTagLength', so to reduce false positives, just ignore any call with options + // ok: gcm-no-tag-length + var decipher = crypto.createDecipheriv("aes-256-gcm", key, iv, {someOption: someValue}) + decipher.setAuthTag(auth) + let result = decipher.update(encryptedData)+ decipher.final() + + return result; +} + +function decrypt6(ciphertext, key) { + iv = ciphertext.iv + encryptedData = ciphertext.data + auth = ciphertext.auth + + // ok: gcm-no-tag-length + var decipher = crypto.createDecipheriv("chacha20-poly1305", key, iv) + decipher.setAuthTag(auth) + let result = decipher.update(encryptedData)+ decipher.final() + + return result; +} diff --git a/javascript/node-crypto/security/gcm-no-tag-length.yaml b/javascript/node-crypto/security/gcm-no-tag-length.yaml new file mode 100644 index 0000000000..0cfc9bb411 --- /dev/null +++ b/javascript/node-crypto/security/gcm-no-tag-length.yaml @@ -0,0 +1,33 @@ +rules: +- id: gcm-no-tag-length + message: >- + The call to 'createDecipheriv' with the Galois Counter Mode (GCM) mode of operation is missing an expected authentication tag length. + If the expected authentication tag length is not specified or otherwise checked, the application might be tricked into verifying a shorter-than-expected authentication tag. + This can be abused by an attacker to spoof ciphertexts or recover the implicit authentication key of GCM, allowing arbitrary forgeries. + metadata: + cwe: + - 'CWE-310: CWE CATEGORY: Cryptographic Issues' + owasp: + - A02:2021 - Cryptographic Failures + category: security + subcategory: + - vuln + technology: + - node-crypto + likelihood: MEDIUM + impact: MEDIUM + confidence: MEDIUM + references: + - https://www.securesystems.de/blog/forging_ciphertexts_under_Galois_Counter_Mode_for_the_Node_js_crypto_module/ + - https://nodejs.org/api/crypto.html#cryptocreatedecipherivalgorithm-key-iv-options + - https://owasp.org/Top10/A02_2021-Cryptographic_Failures/ + languages: + - javascript + - typescript + severity: ERROR + patterns: + - pattern: | + $CRYPTO.createDecipheriv('$ALGO', $KEY, $IV) + - metavariable-regex: + metavariable: $ALGO + regex: ".*(-gcm)$" diff --git a/php/lang/security/injection/tainted-exec.php b/php/lang/security/injection/tainted-exec.php new file mode 100644 index 0000000000..9f4c123250 --- /dev/null +++ b/php/lang/security/injection/tainted-exec.php @@ -0,0 +1,21 @@ +- + User input is passed to a function that executes a shell command. This can lead to remote code execution. + metadata: + cwe: + - "CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')" + category: security + technology: + - php + owasp: + - A03:2021 - Injection + references: + - https://owasp.org/Top10/A03_2021-Injection + subcategory: + - vuln + impact: HIGH + likelihood: MEDIUM + confidence: MEDIUM + mode: taint + pattern-sources: + - patterns: + - pattern-either: + - pattern: $_GET + - pattern: $_POST + - pattern: $_COOKIE + - pattern: $_REQUEST + - pattern: file_get_contents('php://input') + pattern-sanitizers: + - patterns: + - pattern-either: + - pattern: escapeshellcmd(...) + - pattern: escapeshellarg(...) + pattern-sinks: + - patterns: + - pattern-either: + - pattern: exec(...) + - pattern: system(...) + - pattern: passthru(...) + - patterns: + - pattern: proc_open(...) + - pattern-not: proc_open([...], ...) + - pattern: popen(...) + - pattern: expect_popen(...) + - pattern: shell_exec(...) + - pattern: | + `...` + diff --git a/python/correctness/check-is-none-explicitly.py b/python/correctness/check-is-none-explicitly.py new file mode 100644 index 0000000000..fa02d4aeda --- /dev/null +++ b/python/correctness/check-is-none-explicitly.py @@ -0,0 +1,19 @@ +# ruleid: check-is-none-explicitly +if record and record == 0: + print("hello, this will never happen") + +# ok: check-is-none-explicitly +if record is not None and record == 0: + print("this is fine") + +# ruleid: check-is-none-explicitly +if record.a and record.a == 0: + print("Not reachable") + +# ruleid: check-is-none-explicitly +if record.a.get("H") and record.a["H"] == 0: + print("Not reachable") + +# ok: check-is-none-explicitly +if record.a.get("I") and record.a["J"] == 0: + print("This is also fine") \ No newline at end of file diff --git a/python/correctness/check-is-none-explicitly.yaml b/python/correctness/check-is-none-explicitly.yaml new file mode 100644 index 0000000000..93163936bb --- /dev/null +++ b/python/correctness/check-is-none-explicitly.yaml @@ -0,0 +1,19 @@ +rules: +- id: check-is-none-explicitly + pattern-either: + - pattern: $X and $X == 0 + - pattern: $X.get($FIELD) and $X[$FIELD] == 0 + fix: ($X != None) and $X == 0 + message: This expression will always return False because 0 is a false-y value. + So if $X is 0, then the first part of this expression will return False but if + it is not, the second part will return False. Perhaps you meant to check if $X + was None explicitly. + languages: + - python + severity: WARNING + metadata: + category: correctness + technology: + - none + references: + - https://www.freecodecamp.org/news/truthy-and-falsy-values-in-python/ diff --git a/python/django/security/audit/xss/template-href-var.html b/python/django/security/audit/xss/template-href-var.html deleted file mode 100644 index 8fdbfc3bec..0000000000 --- a/python/django/security/audit/xss/template-href-var.html +++ /dev/null @@ -1,22 +0,0 @@ -

From: {{ from_email }}

-

To: - {% for recipient in recipients %} - {{ recipient }}  - {% endfor %} -

-

Subject: {{subject}}

-
- {{ message }} -
- -
diff --git a/python/django/security/audit/xss/template-href-var.yaml b/python/django/security/audit/xss/template-href-var.yaml deleted file mode 100644 index 5069f3952d..0000000000 --- a/python/django/security/audit/xss/template-href-var.yaml +++ /dev/null @@ -1,41 +0,0 @@ -rules: -- id: template-href-var - message: >- - Detected a template variable used in an anchor tag with - the 'href' attribute. This allows a malicious actor to - input the 'javascript:' URI and is subject to cross- - site scripting (XSS) attacks. Use the 'url' template tag - to safely generate a URL. You may also consider setting - the Content Security Policy (CSP) header. - metadata: - cwe: - - "CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')" - owasp: - - A07:2017 - Cross-Site Scripting (XSS) - - A03:2021 - Injection - references: - - https://flask.palletsprojects.com/en/1.1.x/security/#cross-site-scripting-xss - - https://docs.djangoproject.com/en/3.1/ref/templates/builtins/#url - - https://content-security-policy.com/ - category: security - technology: - - django - cwe2022-top25: true - cwe2021-top25: true - subcategory: - - audit - likelihood: LOW - impact: MEDIUM - confidence: LOW - languages: - - generic - paths: - include: - - '*.html' - severity: WARNING - patterns: - - pattern-inside: - - pattern-either: - - pattern: href = '{{...}}' - - pattern: href = "{{...}}" - - pattern: href = {{...}} diff --git a/python/django/security/audit/xss/var-in-script-tag.html b/python/django/security/audit/xss/var-in-script-tag.html deleted file mode 100644 index 8bfb72f8d5..0000000000 --- a/python/django/security/audit/xss/var-in-script-tag.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - -
- -

{{ this_is_fine }}

-
- - - - - diff --git a/python/django/security/audit/xss/var-in-script-tag.yaml b/python/django/security/audit/xss/var-in-script-tag.yaml deleted file mode 100644 index a56724ba50..0000000000 --- a/python/django/security/audit/xss/var-in-script-tag.yaml +++ /dev/null @@ -1,43 +0,0 @@ -rules: -- id: var-in-script-tag - languages: [generic] - severity: ERROR - message: >- - Detected a template variable used in a script tag. - Although template variables are HTML escaped, HTML - escaping does not always prevent cross-site scripting (XSS) - attacks when used directly in JavaScript. If you need this - data on the rendered page, consider placing it in the HTML - portion (outside of a script tag). Alternatively, use a - JavaScript-specific encoder, such as the one available - in OWASP ESAPI. For Django, you may also consider using - the 'json_script' template tag and retrieving the data in - your script by using the element ID (e.g., `document.getElementById`). - patterns: - - pattern-inside: - - pattern: '{{ ... }}' - - pattern-not-inside: nonce = '...' - - pattern-not-inside: nonce = "..." - paths: - include: - - '*.html' - metadata: - cwe: - - "CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')" - owasp: - - A07:2017 - Cross-Site Scripting (XSS) - - A03:2021 - Injection - references: - - https://adamj.eu/tech/2020/02/18/safely-including-data-for-javascript-in-a-django-template/?utm_campaign=Django%2BNewsletter&utm_medium=rss&utm_source=Django_Newsletter_12A - - https://www.veracode.com/blog/secure-development/nodejs-template-engines-why-default-encoders-are-not-enough - - https://github.com/ESAPI/owasp-esapi-js - category: security - technology: - - django - cwe2022-top25: true - cwe2021-top25: true - subcategory: - - audit - likelihood: LOW - impact: MEDIUM - confidence: LOW diff --git a/python/flask/security/xss/audit/template-href-var.html b/python/flask/security/xss/audit/template-href-var.html deleted file mode 100644 index 34adbcf891..0000000000 --- a/python/flask/security/xss/audit/template-href-var.html +++ /dev/null @@ -1,32 +0,0 @@ -

From: {{ from_email }}

-

To: - {% for recipient in recipients %} - {{ recipient }}  - {% endfor %} -

-

Subject: {{subject}}

- -
-
diff --git a/python/flask/security/xss/audit/template-href-var.yaml b/python/flask/security/xss/audit/template-href-var.yaml deleted file mode 100644 index d8d625198f..0000000000 --- a/python/flask/security/xss/audit/template-href-var.yaml +++ /dev/null @@ -1,43 +0,0 @@ -rules: -- id: template-href-var - message: >- - Detected a template variable used in an anchor tag with - the 'href' attribute. This allows a malicious actor to - input the 'javascript:' URI and is subject to cross- - site scripting (XSS) attacks. Use 'url_for()' to safely - generate a URL. You may also consider setting the Content - Security Policy (CSP) header. - metadata: - cwe: - - "CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')" - owasp: - - A07:2017 - Cross-Site Scripting (XSS) - - A03:2021 - Injection - references: - - https://flask.palletsprojects.com/en/1.1.x/security/#cross-site-scripting-xss - - https://content-security-policy.com/ - category: security - technology: - - flask - cwe2022-top25: true - cwe2021-top25: true - subcategory: - - audit - likelihood: LOW - impact: MEDIUM - confidence: LOW - languages: - - generic - paths: - include: - - '*.html' - severity: WARNING - patterns: - - pattern-inside: - - pattern-either: - - pattern: href = {{ ... }} - - pattern: href = "{{ ... }}" - - pattern: href = '{{ ... }}' - - pattern-not-inside: href = {{ url_for(...) ... }} - - pattern-not-inside: href = "{{ url_for(...) ... }}" - - pattern-not-inside: href = '{{ url_for(...) ... }}' diff --git a/python/lang/security/audit/conn_recv.py b/python/lang/security/audit/conn_recv.py index 1a517ecde1..3de79815e8 100644 --- a/python/lang/security/audit/conn_recv.py +++ b/python/lang/security/audit/conn_recv.py @@ -12,5 +12,5 @@ output = {} connection.send(output) -# toodoruleid:multiprocessing.recv +# todoruleid:multiprocessing-recv rx = connection.recv() diff --git a/python/lang/security/audit/dangerous-subprocess-use-tainted-env-args.yaml b/python/lang/security/audit/dangerous-subprocess-use-tainted-env-args.yaml index 199e244057..c7b58315a8 100644 --- a/python/lang/security/audit/dangerous-subprocess-use-tainted-env-args.yaml +++ b/python/lang/security/audit/dangerous-subprocess-use-tainted-env-args.yaml @@ -3,6 +3,8 @@ rules: mode: taint options: symbolic_propagation: true + pattern-sanitizers: + - pattern: shlex.quote(...) pattern-sources: - patterns: - pattern-either: @@ -81,7 +83,7 @@ rules: message: >- Detected subprocess function '$FUNC' with user controlled data. A malicious actor could leverage this to perform command injection. - You may consider using 'shlex.escape()'. + You may consider using 'shlex.quote()'. metadata: owasp: - A01:2017 - Injection diff --git a/python/lang/security/audit/ftplib.py b/python/lang/security/audit/ftplib.py deleted file mode 100644 index cd131ca7cd..0000000000 --- a/python/lang/security/audit/ftplib.py +++ /dev/null @@ -1,12 +0,0 @@ -# cf. https://github.com/PyCQA/bandit/blob/d5f8fa0d89d7b11442fc6ec80ca42953974354c8/examples/ftplib.py - -from ftplib import FTP - -# ruleid:ftplib -ftp = FTP('ftp.debian.org') -ftp.login() - -ftp.cwd('debian') -ftp.retrlines('LIST') - -ftp.quit() diff --git a/python/lang/security/audit/ftplib.yaml b/python/lang/security/audit/ftplib.yaml deleted file mode 100644 index a002121899..0000000000 --- a/python/lang/security/audit/ftplib.yaml +++ /dev/null @@ -1,27 +0,0 @@ -rules: -- id: ftplib - pattern: ftplib.$ANYTHING(...) - message: >- - FTP does not encrypt communications by default. This can lead to sensitive - data being exposed. Ensure use of FTP here does not expose sensitive data. - metadata: - source-rule-url: https://github.com/PyCQA/bandit/blob/d5f8fa0d89d7b11442fc6ec80ca42953974354c8/bandit/blacklists/calls.py#L265 - cwe: - - 'CWE-319: Cleartext Transmission of Sensitive Information' - owasp: - - A03:2017 - Sensitive Data Exposure - - A02:2021 - Cryptographic Failures - bandit-code: B321 - references: - - https://docs.python.org/3/library/telnetlib.html - category: security - technology: - - ftplib - subcategory: - - audit - likelihood: LOW - impact: MEDIUM - confidence: LOW - severity: WARNING - languages: - - python diff --git a/python/lang/security/audit/insecure-transport/ftplib/use-ftp-tls.yaml b/python/lang/security/audit/insecure-transport/ftplib/use-ftp-tls.yaml index 11beea5840..ee2fbaa4b6 100644 --- a/python/lang/security/audit/insecure-transport/ftplib/use-ftp-tls.yaml +++ b/python/lang/security/audit/insecure-transport/ftplib/use-ftp-tls.yaml @@ -35,5 +35,5 @@ rules: likelihood: LOW impact: LOW confidence: LOW - severity: WARNING + severity: INFO languages: [python] diff --git a/python/lang/security/deserialization/pickle.py b/python/lang/security/deserialization/pickle.py index 436f34b45b..5a1e8655ec 100644 --- a/python/lang/security/deserialization/pickle.py +++ b/python/lang/security/deserialization/pickle.py @@ -17,9 +17,6 @@ def serialize_exploit(): # Application insecurely deserializes the attacker's serialized data def insecure_deserialization(exploit_code): - # todok: avoid-pickle - # _pickle.loads(exploit_code) - # ruleid: avoid-pickle _pickle.loads(exploit_code) diff --git a/scripts/run-tests b/scripts/run-tests deleted file mode 100755 index 05fc9be54f..0000000000 --- a/scripts/run-tests +++ /dev/null @@ -1,99 +0,0 @@ -#! /usr/bin/env bash -# -# Validate rules and run semgrep to check that it's producing the expected -# results. -# -# This was originally part of the makefile but it's gotten too complicated -# due the need for extra escaping and error codes being ignored. -# -set -eu - -usage() { - cat <&2 - exit 1 -} - -# Use the SEMGREP environment variable to specify a non-standard semgrep -# command. This is useful for calling a development version of semgrep -# e.g. -# PIPENV_PIPFILE=~/semgrep/cli/Pipfile SEMGREP='pipenv run semgrep' make test -# -if [[ -z "${SEMGREP-}" ]]; then - SEMGREP="semgrep" -fi - -# Obtain the list of folders containing Semgrep rule files -# (with a .yml suffix). -# -# This is meant to exclude folders that don't contain rule files and -# may contain .yml files that are not Semgrep rules and would result -# in errors. -# -# Skipping the "Apex" and "Elixir" folders because it will require splitting test logic -# to run Semgrep OSS and Semgrep Pro with different expected results. -# -set_rule_folders() { - rule_folders=$(find . -mindepth 1 -maxdepth 1 -type d \ - | grep -v '^./\(\..*\|stats\|trusted_python\|fingerprints\|scripts\|libsonnet\|apex\|elixir\)/\?$' \ - | sort) - if [[ -z "$rule_folders" ]]; then - error "Cannot find any rule folders to scan in $(pwd)" - fi -} - -# stdout is redirected to stderr so as to see the whole output in the correct -# order when running this from pytest. -# (pytest captures stdout and stderr separately and prints them later, -# if at all). -# -validate() { - set_rule_folders - for root in $rule_folders; do - echo "======== validate rule files in $root ========" - time -p $SEMGREP --validate \ - --strict --disable-version-check \ - --metrics=off --verbose \ - --config="./$root" - done >&2 -} - -test_only() { - set_rule_folders - for root in $rule_folders; do - echo "========= test rules in $root =========" - time -p $SEMGREP --test --test-ignore-todo \ - --strict --disable-version-check \ - --metrics=off --verbose \ - "./$root" - done >&2 -} - -if [[ $# != 1 ]]; then - usage >&2 - exit 1 -else - case "$1" in - validate) - validate - exit 0 - ;; - test) - test_only - exit 0 - ;; - *) - usage >&2 - exit 1 - esac -fi diff --git a/solidity/security/delegatecall-to-arbitrary-address.sol b/solidity/security/delegatecall-to-arbitrary-address.sol index 176801df95..71a8843486 100644 --- a/solidity/security/delegatecall-to-arbitrary-address.sol +++ b/solidity/security/delegatecall-to-arbitrary-address.sol @@ -35,8 +35,8 @@ contract Test{ } function sink(address _contract, uint256 _num) internal { - // intraprocedural tainting does not work for now... - // todoruleid: delegatecall-to-arbitrary-address + // this requires intraprocedural tainting (--pro-intrafile) + // proruleid: deeptodoruleid: delegatecall-to-arbitrary-address (bool success, bytes memory data) = _contract.delegatecall( abi.encodeWithSignature("setVars(uint256)", _num) ); diff --git a/yaml/github-actions/security/github-script-injection.yaml b/yaml/github-actions/security/github-script-injection.yaml index 3c760d6f33..2c2e9a5a7e 100644 --- a/yaml/github-actions/security/github-script-injection.yaml +++ b/yaml/github-actions/security/github-script-injection.yaml @@ -67,4 +67,6 @@ rules: - pattern: ${{ github.event.pull_request.head.repo.default_branch }} - pattern: ${{ github.head_ref }} - pattern: ${{ github.event.inputs ... }} + - pattern: ${{ github.event.discussion.title }} + - pattern: ${{ github.event.discussion.body }} severity: ERROR diff --git a/yaml/github-actions/security/run-shell-injection.test.yaml b/yaml/github-actions/security/run-shell-injection.test.yaml index cf76cdb549..72577f5619 100644 --- a/yaml/github-actions/security/run-shell-injection.test.yaml +++ b/yaml/github-actions/security/run-shell-injection.test.yaml @@ -121,3 +121,11 @@ jobs: gh api $url > "$name.zip" unzip -d "$name" "$name.zip" done + - name: Show discussion title + # ruleid: run-shell-injection + run: | + echo "${{ github.event.discussion.title }}" + - name: Show discussion body + # ruleid: run-shell-injection + run: | + echo "${{ github.event.discussion.body }}" \ No newline at end of file diff --git a/yaml/github-actions/security/run-shell-injection.yaml b/yaml/github-actions/security/run-shell-injection.yaml index af106986b5..831b10d27c 100644 --- a/yaml/github-actions/security/run-shell-injection.yaml +++ b/yaml/github-actions/security/run-shell-injection.yaml @@ -55,4 +55,6 @@ rules: - pattern: ${{ github.event.pull_request.head.repo.default_branch }} - pattern: ${{ github.head_ref }} - pattern: ${{ github.event.inputs ... }} + - pattern: ${{ github.event.discussion.title }} + - pattern: ${{ github.event.discussion.body }} severity: ERROR diff --git a/yaml/openapi/security/openai-consequential-action-false.test.yaml b/yaml/openapi/security/openai-consequential-action-false.test.yaml new file mode 100644 index 0000000000..28eb92ebea --- /dev/null +++ b/yaml/openapi/security/openai-consequential-action-false.test.yaml @@ -0,0 +1,41 @@ +openapi: 3.1.0 +info: + title: Email Service API + version: 1.0.0 + description: API for managing emails +paths: + /emails/{emailId}: + # ok: openai-consequential-action-false + get: + operationId: getEmailById + x-openai-isConsequential: false + summary: List Emails + description: Get a list of email messages. + + # ruleid: openai-consequential-action-false + delete: + operationId: deleteEmailById + x-openai-isConsequential: false + summary: Delete Email + description: Delete a specific email. + + # ruleid: openai-consequential-action-false + post: + operationId: createEmail + x-openai-isConsequential: false + summary: Create Email + description: Create a new email. + + # ruleid: openai-consequential-action-false + put: + operationId: updateEmail + x-openai-isConsequential: false + summary: Update Email + description: Update an existing email. + + # ruleid: openai-consequential-action-false + patch: + operationId: partialUpdateEmail + x-openai-isConsequential: false + summary: Partially Update Email + description: Update certain fields of an existing email. diff --git a/yaml/openapi/security/openai-consequential-action-false.yaml b/yaml/openapi/security/openai-consequential-action-false.yaml new file mode 100644 index 0000000000..45dc49c031 --- /dev/null +++ b/yaml/openapi/security/openai-consequential-action-false.yaml @@ -0,0 +1,48 @@ +rules: + - id: openai-consequential-action-false + languages: [yaml] + message: >- + Found 'x-openai-isConsequential: false' in a state-changing HTTP + method: $METHOD $PATH. This Action configuration will enable the 'Always + Allow' option for state-changing HTTP methods, such as POST, PUT, PATCH, + or DELETE. The risk of a user selecting the 'Always Allow' button is that + the agent could perform unintended actions on behalf of the user. When + working with sensitive functionality, it is always best to include a Human + In The Loop (HITL) type of control. Consider the trade-off between security + and user friction and then make a risk-based decision about this function. + severity: WARNING + pattern-either: + - pattern-inside: | + post: + ... + x-openai-isConsequential: false + - pattern-inside: | + put: + ... + x-openai-isConsequential: false + - pattern-inside: | + patch: + ... + x-openai-isConsequential: false + - pattern-inside: | + delete: + ... + x-openai-isConsequential: false + metadata: + category: security + subcategory: + - audit + technology: + - openapi + - openai + likelihood: HIGH + impact: HIGH + confidence: HIGH + cwe: "CWE-441: Unintended Proxy or Intermediary ('Confused Deputy')" + owasp: + - 'A04:2021 Insecure Design' + - 'LLM08:2023 - Excessive Agency' + references: + - https://platform.openai.com/docs/actions/consequential-flag + - https://owasp.org/Top10/A04_2021-Insecure_Design/ + - https://owasp.org/www-project-top-10-for-large-language-model-applications/assets/PDF/OWASP-Top-10-for-LLMs-2023-v1_1.pdf