diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml new file mode 100644 index 000000000..f637f3772 --- /dev/null +++ b/.github/workflows/semgrep.yml @@ -0,0 +1,18 @@ +name: Semgrep + +on: + pull_request: {} + # Skipping push for now since it would run against the entire code base. + # push: + +jobs: + semgrep: + name: Semgrep Scan + runs-on: ubuntu-latest + env: + SEMGREP_SEND_METRICS: 0 + # Skip any PR created by dependabot to avoid permission issues + if: (github.actor != 'dependabot[bot]') + steps: + - uses: actions/checkout@v2 + - uses: returntocorp/semgrep-action@v1 diff --git a/.semgrep/changelog.yml b/.semgrep/changelog.yml new file mode 100644 index 000000000..83d5e6f4b --- /dev/null +++ b/.semgrep/changelog.yml @@ -0,0 +1,18 @@ +rules: + # Check `release-note` in changelog entries. + # https://semgrep.dev/s/DyRW + - id: "changelog-release-note" + patterns: + - pattern: "```$CHANGE_TYPE" + - pattern-not-inside: "```release-note" + # This is probably the right way to write this rule, but semgrep doesn't + # like it: https://github.com/returntocorp/semgrep/issues/4565 + # - pattern-not: "```release-note:..." + message: "Missing `relelease-note`" + languages: + - "generic" + severity: "ERROR" + fix: "```release-note:$CHANGE_TYPE" + paths: + include: + - ".changelog" diff --git a/.semgrep/go_tests.yml b/.semgrep/go_tests.yml new file mode 100644 index 000000000..20f2797c4 --- /dev/null +++ b/.semgrep/go_tests.yml @@ -0,0 +1,116 @@ +rules: + # Check `require` or `assert` testify overrides. + # https://semgrep.dev/s/PgAq + - id: "tests-no-testify-override" + patterns: + - pattern-either: + - pattern: "assert := assert.New(($T : *testing.T))" + - pattern: "require := require.New(($T : *testing.T))" + message: "Override of testify package" + languages: + - "go" + severity: "WARNING" + # TODO(luiz): figure out how to do a 'delete line' fix. + fix: " " + paths: + include: + - "*_test.go" + + # Check `assert` and `require` calls without `t` as first argument. + # https://semgrep.dev/s/wZW0 + - id: "tests-no-assert-without-t" + patterns: + - pattern: "assert.$FUNC($...ARGS)" + - pattern-not: "assert.$FUNC($T,...)" + - pattern-inside: | + func $TEST_FUNC($T *testing.T) { + ... + } + # Nested functions have a different signature, so they are tested in a + # different rule. + - pattern-not-inside: | + $T.Run(..., func(...) { + ... + }) + - metavariable-pattern: + metavariable: $FUNC + patterns: + # Calls to `New` are checked in `tests-no-testify-override`. + - pattern-not: New + message: "Calling `assert.$FUNC` without `$T`" + languages: + - "go" + severity: "WARNING" + fix: "assert.$FUNC($T, $...ARGS)" + paths: + include: + - "*_test.go" + - id: "tests-no-assert-without-t-nested" + patterns: + - pattern: "assert.$FUNC($...ARGS)" + - pattern-not: "assert.$FUNC($T,...)" + - pattern-inside: | + ($T_ROOT : *testing.T).Run(..., func($T *testing.T) { + ... + }) + - metavariable-pattern: + metavariable: $FUNC + patterns: + # Calls to `New` are checked in `tests-no-testify-override`. + - pattern-not: New + message: "Calling `assert.$FUNC` without `$T`" + languages: + - "go" + severity: "WARNING" + fix: "assert.$FUNC($T, $...ARGS)" + paths: + include: + - "*_test.go" + - id: "tests-no-require-without-t" + patterns: + - pattern: "require.$FUNC($...ARGS)" + - pattern-not: "require.$FUNC($T,...)" + - pattern-inside: | + func $TEST_FUNC($T *testing.T) { + ... + } + # Nested functions have a different signature, so they are tested in a + # different rule. + - pattern-not-inside: | + $T.Run(..., func(...) { + ... + }) + - metavariable-pattern: + metavariable: $FUNC + patterns: + # Calls to `New` are checked in `tests-no-testify-override`. + - pattern-not: New + message: "Calling `require.$FUNC` without `$T`" + languages: + - "go" + severity: "WARNING" + fix: "require.$FUNC($T, $...ARGS)" + paths: + include: + - "*_test.go" + - id: "tests-no-require-without-t-nested" + patterns: + - pattern: "require.$FUNC($...ARGS)" + - pattern-not: "require.$FUNC($T,...)" + - pattern-inside: | + ($T_ROOT : *testing.T).Run(..., func($T *testing.T) { + ... + }) + - metavariable-pattern: + metavariable: "$FUNC" + patterns: + # Calls to `New` are checked in `tests-no-testify-override`. + - pattern-not: "New" + message: "Calling `require.$FUNC` without `$T`" + languages: + - "go" + severity: "WARNING" + fix: "require.$FUNC($T, $...ARGS)" + paths: + include: + - "*_test.go"