bitwarden_workflow_linter 0.5.6__py3-none-any.whl → 0.5.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
1
  """Metadata for Workflow Linter."""
2
2
 
3
- __version__ = "0.5.6"
3
+ __version__ = "0.5.7"
@@ -7,5 +7,6 @@ enabled_rules:
7
7
  - bitwarden_workflow_linter.rules.step_pinned.RuleStepUsesPinned
8
8
  - bitwarden_workflow_linter.rules.underscore_outputs.RuleUnderscoreOutputs
9
9
  - bitwarden_workflow_linter.rules.run_actionlint.RunActionlint
10
+ - bitwarden_workflow_linter.rules.check_pr_target.RuleCheckPrTarget
10
11
 
11
12
  approved_actions_path: default_actions.json
@@ -23,6 +23,7 @@ class Job:
23
23
  key: Optional[str] = None
24
24
  name: Optional[str] = None
25
25
  env: Optional[CommentedMap] = None
26
+ needs: Optional[List[str]] = None
26
27
  steps: Optional[List[Step]] = None
27
28
  uses: Optional[str] = None
28
29
  uses_path: Optional[str] = None
@@ -32,6 +33,13 @@ class Job:
32
33
  )
33
34
  outputs: Optional[CommentedMap] = None
34
35
 
36
+ @classmethod
37
+ def parse_needs(cls: Self, value):
38
+ """Parser to make all needs values lists that can be searched by linter."""
39
+ if isinstance(value, str):
40
+ return [value]
41
+ return value
42
+
35
43
  @classmethod
36
44
  def init(cls: Self, key: str, data: CommentedMap) -> Self:
37
45
  """Custom dataclass constructor to map job data to a Job."""
@@ -40,6 +48,7 @@ class Job:
40
48
  "name": data["name"] if "name" in data else None,
41
49
  "runs-on": data["runs-on"] if "runs-on" in data else None,
42
50
  "env": data["env"] if "env" in data else None,
51
+ "needs": Job.parse_needs(data["needs"]) if "needs" in data else None,
43
52
  "outputs": data["outputs"] if "outputs" in data else None,
44
53
  }
45
54
 
@@ -0,0 +1,81 @@
1
+ """A Rule to enforce check-run is run when workflow uses pull_request_target."""
2
+
3
+ from typing import Optional, Tuple
4
+
5
+ from ..models.workflow import Workflow
6
+ from ..rule import Rule
7
+ from ..utils import LintLevels, Settings
8
+
9
+
10
+ class RuleCheckPrTarget(Rule):
11
+ def __init__(self, settings: Optional[Settings] = None) -> None:
12
+ """
13
+ To ensure pull_request_target is safe to use, the check-run step is added
14
+ to all jobs as a dependency.
15
+
16
+ Once a branch is pushed to Github, it already opens up a vulnerability
17
+ even if the check-run scan fails to detect this.
18
+
19
+ In order to prevent a vulnerable branch from being used for an attack
20
+ prior to being caught through vetting, all pull_request_target workflows
21
+ should only be run by users with appropriate permissions.
22
+
23
+ This greatly reduces the risk as community contributors can't use a fork to run a compromised workflow that uses pull_request_target.
24
+ """
25
+ self.message = "A check-run job must be included as a direct job dependency when pull_request_target is used and the workflow may only apply to runs on the main branch"
26
+ self.on_fail = LintLevels.WARNING
27
+ self.compatibility = [Workflow]
28
+ self.settings = settings
29
+
30
+ def targets_main_branch(self, obj:Workflow) -> bool:
31
+ if obj.on["pull_request_target"].get("branches"):
32
+ branches_list = obj.on["pull_request_target"].get("branches")
33
+ if isinstance(branches_list, str):
34
+ branches_list = [branches_list]
35
+ if any(branch != 'main' for branch in branches_list):
36
+ return False
37
+ else:
38
+ return False
39
+ return True
40
+
41
+ def has_check_run(self, obj: Workflow) -> Tuple[bool, str]:
42
+ for name, job in obj.jobs.items():
43
+ if job.uses == "bitwarden/gh-actions/.github/workflows/check-run.yml@main":
44
+ return True, name
45
+ return False, ""
46
+
47
+ def check_run_required(self, obj:Workflow, check_job:str) -> list:
48
+ missing_jobs = []
49
+ for job in list(obj.jobs.keys()):
50
+ if job is not check_job:
51
+ job_list = obj.jobs[job].needs
52
+ if job_list:
53
+ if check_job not in job_list:
54
+ missing_jobs.append(job)
55
+ else:
56
+ missing_jobs.append(job)
57
+ return missing_jobs
58
+
59
+ def fn(self, obj: Workflow) -> Tuple[bool, str]:
60
+ Errors = []
61
+ if obj.on.get("pull_request_target"):
62
+ result, check_job = self.has_check_run(obj)
63
+ main_branch_only = self.targets_main_branch(obj)
64
+ if not main_branch_only:
65
+ Errors.append("Workflows using pull_request_target can only target the main branch")
66
+ if result:
67
+ missing_jobs = self.check_run_required(obj, check_job)
68
+ if missing_jobs:
69
+ job_list = ', '.join(missing_jobs)
70
+ message = f"Check-run is missing from the following jobs in the workflow: {job_list}"
71
+ Errors.append(message)
72
+ else:
73
+ message = f"A check-run job must be included as a direct job dependency when pull_request_target is used"
74
+ Errors.append(message)
75
+ if Errors:
76
+ self.message = "\n".join(Errors)
77
+ return False, f"{self.message}"
78
+ else:
79
+ return True, ""
80
+ else:
81
+ return True, ""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bitwarden_workflow_linter
3
- Version: 0.5.6
3
+ Version: 0.5.7
4
4
  Summary: Custom GitHub Action Workflow Linter
5
5
  Project-URL: Homepage, https://github.com/bitwarden/workflow-linter
6
6
  Project-URL: Issues, https://github.com/bitwarden/workflow-linter/issues
@@ -1,18 +1,19 @@
1
- bitwarden_workflow_linter/__about__.py,sha256=yMKKZhZd98JL3DnAj0EuLwP2lI9B4ay-CHK8LiRA-_k,59
1
+ bitwarden_workflow_linter/__about__.py,sha256=UUJxPIlJEXBTr87K95rAO3yy6PUDPLgqBoI5F9FVF6s,59
2
2
  bitwarden_workflow_linter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  bitwarden_workflow_linter/actions.py,sha256=LAn3yQeMMmCOvJWeTn3dE1U2nyEJqIBMwESq3TtY9hE,9069
4
4
  bitwarden_workflow_linter/cli.py,sha256=wgkK1MlVbo6Zx3f2CZZ_tkSWq_hdsGciHJA1knX6Yuw,1699
5
5
  bitwarden_workflow_linter/default_actions.json,sha256=6-h_0i7sNJPQMC5AzFtQxrlCJZcXECSBlBAKPrGTJfs,12770
6
- bitwarden_workflow_linter/default_settings.yaml,sha256=0sD7oU3UkTU7_63NqNCSIjQppt9kQXOQNsA11fkl8Xs,643
6
+ bitwarden_workflow_linter/default_settings.yaml,sha256=-kMS3HpE6-h7IIx7izq1I4LA6D73L-MMA3ozgGDpsjQ,713
7
7
  bitwarden_workflow_linter/lint.py,sha256=RDHv5jGeGCf5XIHE8jyqQET3-cFykl7223SQVS4Q3pg,5525
8
8
  bitwarden_workflow_linter/load.py,sha256=XXbja-sVUtXaJjqF9sTP2xzFto3CXcCuEBNFp3Yo4VM,5078
9
9
  bitwarden_workflow_linter/rule.py,sha256=Qb60JiUDAWN3ayrMGoSbbDCSFmw-ql8djzAkxISaob4,3250
10
10
  bitwarden_workflow_linter/utils.py,sha256=9WO3T9w9vKAow_xR6JDz2MvdMXKExV18AzucF0vh67s,4695
11
11
  bitwarden_workflow_linter/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- bitwarden_workflow_linter/models/job.py,sha256=nBK7_VYu6RRST7WLtdLsoRErl5j4Er8W9hCw9XlSECk,2043
12
+ bitwarden_workflow_linter/models/job.py,sha256=d4F0QG35DqaqgfbPa2YDRRxOZZakFDktIOsuUa-BbC8,2387
13
13
  bitwarden_workflow_linter/models/step.py,sha256=j81iWYWcNI9x55n1MOR0N6ogKaQ_4-CKu9LnI_fwEOE,1814
14
14
  bitwarden_workflow_linter/models/workflow.py,sha256=Jr1CJSCpII9Z4RT6Sh1wjzZU6ov3fZt45BmTrfLNLLE,1479
15
15
  bitwarden_workflow_linter/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ bitwarden_workflow_linter/rules/check_pr_target.py,sha256=BPpd30ZQHQEqcvYY63zjd-v2rNngEMIqWghPWhU7WoE,3526
16
17
  bitwarden_workflow_linter/rules/job_environment_prefix.py,sha256=sY1cBU5AeBHWSyun7gwnoS0ycRyBMjjVo_2lvanBj7U,2612
17
18
  bitwarden_workflow_linter/rules/name_capitalized.py,sha256=quuqXM_qg93UE8mQo1YQp8cQ_Fx6c2u03_19s_c0ntw,1981
18
19
  bitwarden_workflow_linter/rules/name_exists.py,sha256=MxcaNQz64JXeHRPiOip9BxJNgPdpKQa7Z51mDoNw2hU,1681
@@ -21,8 +22,8 @@ bitwarden_workflow_linter/rules/run_actionlint.py,sha256=qzfrBUcvNMkeBKGMKkIyqRa
21
22
  bitwarden_workflow_linter/rules/step_approved.py,sha256=6XuYoasw2ME8vQu5G0ZygUSi7X5amLLWeXH81cqvKv8,3159
22
23
  bitwarden_workflow_linter/rules/step_pinned.py,sha256=fyqBjarR0UNQ6tU_ja0ZOi2afP942BMqOz5nU_yKzmw,3413
23
24
  bitwarden_workflow_linter/rules/underscore_outputs.py,sha256=w8pP1dTJEC9I2X5fQIAHDAEiaNP1xMhb4kPiF-dn8U0,4131
24
- bitwarden_workflow_linter-0.5.6.dist-info/METADATA,sha256=01WP8Mce9hy1t9uuoG6RpOTgi0RJtFsxTEPxCMN9Sug,6164
25
- bitwarden_workflow_linter-0.5.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
- bitwarden_workflow_linter-0.5.6.dist-info/entry_points.txt,sha256=SA_yF9CwL4VMUvdcmCd7k9rjsQNzfeOUBuDnMnaO8QQ,60
27
- bitwarden_workflow_linter-0.5.6.dist-info/licenses/LICENSE.txt,sha256=uY-7N9tbI7xc_c0WeTIGpacSCnsB91N05eCIg3bkaRw,35140
28
- bitwarden_workflow_linter-0.5.6.dist-info/RECORD,,
25
+ bitwarden_workflow_linter-0.5.7.dist-info/METADATA,sha256=j9lNs6EV6TE8oHpf6tNzoeOfwnBkEdSBkiTkFdOs_78,6164
26
+ bitwarden_workflow_linter-0.5.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
27
+ bitwarden_workflow_linter-0.5.7.dist-info/entry_points.txt,sha256=SA_yF9CwL4VMUvdcmCd7k9rjsQNzfeOUBuDnMnaO8QQ,60
28
+ bitwarden_workflow_linter-0.5.7.dist-info/licenses/LICENSE.txt,sha256=uY-7N9tbI7xc_c0WeTIGpacSCnsB91N05eCIg3bkaRw,35140
29
+ bitwarden_workflow_linter-0.5.7.dist-info/RECORD,,