diff --git a/.gitleaks.toml b/.gitleaks.toml index 36288711bc..2a04f6d0cb 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -10,6 +10,16 @@ [allowlist] description = "Exclude test fixtures, mock data, sample configs, and CI resources" paths = [ + # Go test files (commonly contain mock credentials) + '''.*_test\.go$''', + + # JS/TS test files (.spec.ts, .test.tsx, etc.) + '''.*\.spec\.(ts|tsx|js|jsx)$''', + '''.*\.test\.(ts|tsx|js|jsx)$''', + + # JS/TS test directories + '''__tests__/''', + # Go testdata directories '''testdata/''', diff --git a/semgrep.yaml b/semgrep.yaml index b5733e9301..94a3768e85 100644 --- a/semgrep.yaml +++ b/semgrep.yaml @@ -21,7 +21,7 @@ rules: # SECTION 1: GENERIC SECRETS DETECTION — Applies to all file types # ========================================================================== - - id: generic-hardcoded-secret + - id: generic-hardcoded-secret # pragma: allowlist secret languages: [generic] severity: ERROR message: | @@ -42,7 +42,7 @@ rules: If this is a test fixture or example: - Add comment: # nosemgrep: generic-hardcoded-secret - - Or use obviously fake values: password = "REPLACE_ME" + - Or use obviously fake values: password = "FAKE" patterns: - pattern-regex: |- (?i)(password|passwd|pwd|secret|token|api[_-]?key|private[_-]?key)\s*[:=]+\s*["'][^"']{8,}["'] @@ -71,13 +71,13 @@ rules: - Enable AWS CloudTrail for key usage monitoring False Positive: If this is documentation/example, replace with: - AKIAIOSFODNN7EXAMPLE (official AWS example key) + AKIA...EXAMPLE (redacted AWS example key) pattern-regex: 'AKIA[0-9A-Z]{16}' metadata: cwe: "CWE-798" category: "security" - - id: generic-aws-secret-access-key + - id: generic-aws-secret-access-key # pragma: allowlist secret languages: [generic] severity: ERROR message: | @@ -361,7 +361,7 @@ rules: category: "security" note: "Not necessarily dangerous, but aggregated roles can accumulate unexpected permissions if selectors are too broad" - - id: k8s-rbac-secrets-cluster-access + - id: k8s-rbac-secrets-cluster-access # pragma: allowlist secret languages: [yaml] severity: WARNING message: | @@ -498,7 +498,7 @@ rules: cwe: "CWE-653" category: "security" - - id: k8s-secret-in-configmap + - id: k8s-secret-in-configmap # pragma: allowlist secret languages: [yaml] severity: ERROR message: | @@ -541,7 +541,7 @@ rules: cwe: "CWE-522" category: "security" - - id: yaml-hardcoded-secret + - id: yaml-hardcoded-secret # pragma: allowlist secret languages: [yaml] severity: WARNING message: | @@ -560,7 +560,7 @@ rules: languages: [yaml] severity: WARNING message: | - Pod explicitly enables automountServiceAccountToken (CWE-200). + Workload explicitly enables automountServiceAccountToken (CWE-200). When enabled, the ServiceAccount token is mounted into the pod at /var/run/secrets/kubernetes.io/serviceaccount/token. If the pod is @@ -568,12 +568,54 @@ rules: Remediation: Set automountServiceAccountToken: false if the pod doesn't need Kubernetes API access (most application pods don't). - patterns: - - pattern: | - automountServiceAccountToken: true - - pattern-inside: | - kind: Pod - ... + pattern-either: + # Match Pod directly + - patterns: + - pattern: | + automountServiceAccountToken: true + - pattern-inside: | + kind: Pod + ... + # Match Deployment, StatefulSet, DaemonSet, ReplicaSet pod template + - patterns: + - pattern: | + automountServiceAccountToken: true + - pattern-inside: | + kind: $KIND + ... + spec: + ... + template: + ... + - metavariable-regex: + metavariable: $KIND + regex: (Deployment|StatefulSet|DaemonSet|ReplicaSet) + # Match Job pod template + - patterns: + - pattern: | + automountServiceAccountToken: true + - pattern-inside: | + kind: Job + ... + spec: + ... + template: + ... + # Match CronJob pod template (nested under jobTemplate) + - patterns: + - pattern: | + automountServiceAccountToken: true + - pattern-inside: | + kind: CronJob + ... + spec: + ... + jobTemplate: + ... + spec: + ... + template: + ... metadata: cwe: "CWE-200" category: "security" @@ -582,7 +624,7 @@ rules: languages: [yaml] severity: WARNING message: | - Pod uses default ServiceAccount (CWE-250). + Workload uses default ServiceAccount (CWE-250). The default ServiceAccount may have more permissions than needed. Each workload should use a dedicated ServiceAccount with minimal RBAC @@ -594,12 +636,14 @@ rules: metadata: name: my-app-sa pattern-either: + # Match Pod directly — explicit default - pattern: | kind: Pod ... spec: ... serviceAccountName: default + # Match Pod directly — no SA specified - patterns: - pattern: | kind: Pod @@ -608,6 +652,90 @@ rules: ... - pattern-not: | serviceAccountName: $SA + # Match controllers — explicit default + - patterns: + - pattern: | + spec: + ... + template: + ... + spec: + ... + serviceAccountName: default + - pattern-inside: | + kind: $KIND + ... + - metavariable-regex: + metavariable: $KIND + regex: (Deployment|StatefulSet|DaemonSet|ReplicaSet|Job) + # Match controllers — no SA specified (implicit default) + - patterns: + - pattern: | + spec: + ... + template: + ... + spec: + ... + - pattern-not: | + spec: + ... + template: + ... + spec: + ... + serviceAccountName: $SA + - pattern-inside: | + kind: $KIND + ... + - metavariable-regex: + metavariable: $KIND + regex: (Deployment|StatefulSet|DaemonSet|ReplicaSet|Job) + # Match CronJob — explicit default + - patterns: + - pattern: | + spec: + ... + jobTemplate: + ... + spec: + ... + template: + ... + spec: + ... + serviceAccountName: default + - pattern-inside: | + kind: CronJob + ... + # Match CronJob — no SA specified (implicit default) + - patterns: + - pattern: | + spec: + ... + jobTemplate: + ... + spec: + ... + template: + ... + spec: + ... + - pattern-not: | + spec: + ... + jobTemplate: + ... + spec: + ... + template: + ... + spec: + ... + serviceAccountName: $SA + - pattern-inside: | + kind: CronJob + ... metadata: cwe: "CWE-250" category: "security" @@ -616,10 +744,10 @@ rules: # SECTION 4: GITHUB ACTIONS SECURITY — Workflow files # ========================================================================== - - id: github-actions-hardcoded-secret + - id: github-actions-hardcoded-secret # pragma: allowlist secret languages: [yaml] severity: ERROR - message: | + message: | # pragma: allowlist secret Hardcoded secret in GitHub Actions workflow. Security Risk: Secrets in workflows are visible in git history and to all collaborators. @@ -645,6 +773,10 @@ rules: patterns: - pattern-not: ${{ secrets.$SECRET }} - pattern-not: ${{ env.$ENV }} + paths: + include: + - "**/.github/workflows/*.yml" + - "**/.github/workflows/*.yaml" metadata: cwe: "CWE-798" category: "security" @@ -678,11 +810,11 @@ rules: env: TITLE: ${{ github.event.pull_request.title }} patterns: - - pattern-regex: 'run:.*\$\{\{\s*github\.(head_ref|event\.(issue|pull_request|discussion|review|review_comment|comment)\.(title|body|head\.ref|head\.label)|event\.head_commit\.message|event\.commits\[\d+\]\.message)\s*\}\}' + - pattern-regex: 'run:\s*(?:[|>][-+]?\n(?:[ \t]+[^\n]*\n)*|[^\n]*)\$\{\{\s*github\.(head_ref|event\.(issue|pull_request|discussion|review|review_comment|comment)\.(title|body|head\.ref|head\.label)|event\.head_commit\.message|event\.commits\[\d+\]\.message)\s*\}\}' paths: include: - - ".github/workflows/*.yml" - - ".github/workflows/*.yaml" + - "**/.github/workflows/*.yml" + - "**/.github/workflows/*.yaml" metadata: cwe: "CWE-78" owasp: "A03:2021 - Injection" @@ -716,12 +848,11 @@ rules: - If checkout is needed, use merge commit: refs/pull/${{ github.event.number }}/merge - Add persist-credentials: false to limit token scope patterns: - - pattern-regex: 'pull_request_target' - - pattern-regex: 'ref:\s*\$\{\{.*pull_request\.head\.(sha|ref)\s*\}\}' + - pattern-regex: 'pull_request_target[\s\S]*?uses:\s*actions/checkout@[^\n]*\n(\s+[\w-]+:.*\n)*\s+ref:\s*\$\{\{[^\}]*pull_request\.head\.(sha|ref)\s*\}\}' paths: include: - - ".github/workflows/*.yml" - - ".github/workflows/*.yaml" + - "**/.github/workflows/*.yml" + - "**/.github/workflows/*.yaml" metadata: cwe: "CWE-829" category: "security" @@ -920,13 +1051,23 @@ rules: patterns: - pattern-either: - pattern: | - const $VAR = "password" - - pattern: | - const $VAR = "secret" + $VAR := $VALUE - pattern: | - const $VAR = "token" + const $VAR = $VALUE - pattern: | - var $VAR = "Bearer ..." + var $VAR = $VALUE + - metavariable-regex: + metavariable: $VAR + regex: (?i)(password|passwd|secret|token|api[_-]?key|private[_-]?key|credentials?) + - metavariable-regex: + metavariable: $VALUE + regex: '"[^"]{8,}"' + - pattern-not: | + $VAR := os.Getenv("...") + - pattern-not: | + var $VAR = os.Getenv("...") + - pattern-not: | + $VAR, $_ := os.LookupEnv("...") metadata: cwe: "CWE-798" owasp: "A07:2021" @@ -1673,7 +1814,7 @@ rules: metadata: category: "security" - - id: dockerfile-secret-in-env + - id: dockerfile-secret-in-env # pragma: allowlist secret languages: [dockerfile] severity: ERROR message: | @@ -1726,7 +1867,7 @@ rules: Remediation: Always quote variables in file operations: rm "$FILE" # correct rm $FILE # dangerous - pattern-regex: '(rm|cp|mv|eval|chmod|chown|kill|pkill)\s+[^|;]*(?