guarddog 2.4.0__py3-none-any.whl → 2.5.0__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.
@@ -39,7 +39,7 @@ class PypiTyposquatDetector(TyposquatDetector):
39
39
  }
40
40
  """
41
41
 
42
- popular_packages_url = "https://hugovk.github.io/top-pypi-packages/top-pypi-packages-30-days.min.json"
42
+ popular_packages_url = "https://hugovk.github.io/top-pypi-packages/top-pypi-packages.min.json"
43
43
 
44
44
  top_packages_filename = "top_pypi_packages.json"
45
45
  resources_dir = TOP_PACKAGES_CACHE_LOCATION
@@ -41,7 +41,7 @@ rules:
41
41
  - pattern-regex: ((?:https?:\/\/)?[^\n\[\/\?#"']*?(ipinfo\.io|checkip\.dyndns\.org|\bip\.me|jsonip\.com|ipify\.org|ifconfig\.me)\b)
42
42
 
43
43
  # top-level domains
44
- - pattern-regex: (https?:\/\/[^\n\[\/\?#"']*?\.(link|xyz|tk|ml|ga|cf|gq|pw|top|club|mw|bd|ke|am|sbs|date|quest|cd|bid|cd|ws|icu|cam|uno|email|stream)\/)
44
+ - pattern-regex: (https?:\/\/[^\n\[\/\?#"']*?\.(link|xyz|tk|ml|ga|cf|gq|pw|top|club|mw|bd|ke|am|sbs|date|quest|cd|bid|cd|ws|icu|cam|uno|email|stream|zip)\/)
45
45
  # IPv4
46
46
  - pattern-regex: (https?:\/\/[^\n\[\/\?#"']*?(?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))
47
47
  # IPv6
@@ -1,5 +1,6 @@
1
1
  from typing import Optional
2
2
 
3
+ from .github_action_project_scanner import GitHubActionDependencyScanner
3
4
  from .npm_package_scanner import NPMPackageScanner
4
5
  from .npm_project_scanner import NPMRequirementsScanner
5
6
  from .pypi_package_scanner import PypiPackageScanner
@@ -54,4 +55,6 @@ def get_project_scanner(ecosystem: ECOSYSTEM) -> Optional[ProjectScanner]:
54
55
  return NPMRequirementsScanner()
55
56
  case ECOSYSTEM.GO:
56
57
  return GoDependenciesScanner()
58
+ case ECOSYSTEM.GITHUB_ACTION:
59
+ return GitHubActionDependencyScanner()
57
60
  return None
@@ -0,0 +1,101 @@
1
+ import logging
2
+ from typing import List, Dict, TypedDict
3
+ from typing_extensions import NotRequired
4
+
5
+ import yaml
6
+ import re
7
+
8
+ from guarddog.scanners.github_action_scanner import GithubActionScanner
9
+ from guarddog.scanners.scanner import ProjectScanner
10
+
11
+ log = logging.getLogger("guarddog")
12
+
13
+
14
+ class GitHubWorkflowStep(TypedDict):
15
+ name: NotRequired[str]
16
+ uses: NotRequired[str]
17
+
18
+
19
+ class GitHubWorkflowJob(TypedDict):
20
+ name: str
21
+ uses: str
22
+ runs_on: str
23
+ steps: List[GitHubWorkflowStep]
24
+
25
+
26
+ class GitHubWorkflowFile(TypedDict):
27
+ name: str
28
+ jobs: Dict[str, GitHubWorkflowJob]
29
+
30
+
31
+ class GitHubAction(TypedDict):
32
+ name: str
33
+ ref: str
34
+
35
+
36
+ def parse_action_from_step(step: GitHubWorkflowStep) -> GitHubAction | None:
37
+ """
38
+ Parses a step in a GitHub workflow file and returns a GitHub action reference if it exists.
39
+
40
+ Args:
41
+ step (GitHubWorkflowStep): Step in a GitHub workflow file
42
+
43
+ Returns:
44
+ GitHubAction | None: GitHub action reference if it exists, None otherwise
45
+ """
46
+ if "uses" not in step:
47
+ return None
48
+
49
+ if step["uses"].startswith("/") or step["uses"].startswith("./"):
50
+ return None
51
+ parts = step["uses"].split("@", 1)
52
+ if len(parts) != 2:
53
+ log.debug(f"Invalid action reference: {step['uses']}")
54
+ return None
55
+
56
+ if re.search(r"^([\w-])+/([\w./-])+$", parts[0]):
57
+ return GitHubAction(name=parts[0], ref=parts[1])
58
+ return None
59
+
60
+
61
+ class GitHubActionDependencyScanner(ProjectScanner):
62
+ """
63
+ Scans all 3rd party actions in a GitHub workflow file.
64
+ """
65
+
66
+ def __init__(self) -> None:
67
+ super().__init__(GithubActionScanner())
68
+
69
+ def parse_requirements(self, raw_requirements: str) -> dict[str, set[str]]:
70
+ actions = self.parse_workflow_3rd_party_actions(raw_requirements)
71
+
72
+ requirements: dict[str, set[str]] = {}
73
+ for action in actions:
74
+ repo, version = action["name"], action["ref"]
75
+ if repo in requirements:
76
+ requirements[repo].add(version)
77
+ else:
78
+ requirements[repo] = {version}
79
+ return requirements
80
+
81
+ def parse_workflow_3rd_party_actions(
82
+ self, workflow_file: str
83
+ ) -> List[GitHubAction]:
84
+ """
85
+ Parses a GitHub workflow file and returns a list of 3rd party actions
86
+ used in the workflow.
87
+
88
+ Args:
89
+ workflow_file (str): Contents of the GitHub workflow file
90
+
91
+ Returns:
92
+ List[GitHubAction]: List of 3rd party actions used in the workflow
93
+ """
94
+ f: GitHubWorkflowFile = yaml.safe_load(workflow_file)
95
+ actions = []
96
+ for job in f.get("jobs", {}).values():
97
+ for step in job.get("steps", []):
98
+ action = parse_action_from_step(step)
99
+ if action:
100
+ actions.append(action)
101
+ return actions
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: guarddog
3
- Version: 2.4.0
3
+ Version: 2.5.0
4
4
  Summary: GuardDog is a CLI tool to Identify malicious PyPI packages
5
5
  License: Apache-2.0
6
6
  Author: Ellen Wang
@@ -15,7 +15,7 @@ Requires-Dist: click (>=8.1.3,<9.0.0)
15
15
  Requires-Dist: click-option-group (>=0.5.5,<0.6.0)
16
16
  Requires-Dist: colorama (>=0.4.6,<0.5.0)
17
17
  Requires-Dist: configparser (>=5.3,<8.0)
18
- Requires-Dist: disposable-email-domains (>=0.0.103,<0.0.118)
18
+ Requires-Dist: disposable-email-domains (>=0.0.103,<0.0.119)
19
19
  Requires-Dist: prettytable (>=3.6.0,<4.0.0)
20
20
  Requires-Dist: pygit2 (>=1.11,<1.18)
21
21
  Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
@@ -30,7 +30,7 @@ guarddog/analyzer/metadata/pypi/potentially_compromised_email_domain.py,sha256=3
30
30
  guarddog/analyzer/metadata/pypi/release_zero.py,sha256=GSHap4VSVF9_s3gD95kUD_5eZDHxrqQQoV5aFTgzrCw,716
31
31
  guarddog/analyzer/metadata/pypi/repository_integrity_mismatch.py,sha256=HNVEOj3cz43qHD47Chul05vpX_9uhfaPgjo6Q-fuCfA,11635
32
32
  guarddog/analyzer/metadata/pypi/single_python_file.py,sha256=CLAWaOJ_JNofGmtuCOT-37saryHeZzZAjfJQp31H6jU,1369
33
- guarddog/analyzer/metadata/pypi/typosquatting.py,sha256=Cn3r_2apz2rP2UGMggYNmtQlrDCq3ZDS_ix5TVj9FIk,4726
33
+ guarddog/analyzer/metadata/pypi/typosquatting.py,sha256=Lk0MoIfsMw557PrpUxLAynqNhWSjZDSVI7GJPdyvoG8,4718
34
34
  guarddog/analyzer/metadata/pypi/unclaimed_maintainer_email_domain.py,sha256=4u3s4Jq51arMznv-_0NwZst40x7jGtLJQIEd3Pp2U30,406
35
35
  guarddog/analyzer/metadata/pypi/utils.py,sha256=7ipsnFN1KHcFwU9u1GK8wqINKDCXJ1vQEpTwwZfJFp4,199
36
36
  guarddog/analyzer/metadata/release_zero.py,sha256=7cIdReF3TU3XJq6ALemK81tqaG3Cz1HNwwtH65Y9uPU,438
@@ -59,14 +59,15 @@ guarddog/analyzer/sourcecode/npm-serialize-environment.yml,sha256=gFpr58INp44Zwx
59
59
  guarddog/analyzer/sourcecode/npm-silent-process-execution.yml,sha256=qnJHGesNPNpxGa8n2kQMpttLGck-6vZjI_SsweDyk7M,3513
60
60
  guarddog/analyzer/sourcecode/npm-steganography.yml,sha256=XH0udcriAQq_6WOHAG4TpIedw8GgKyWx9gsG_Q_Fki8,915
61
61
  guarddog/analyzer/sourcecode/obfuscation.yml,sha256=dp0BeCYShcTS8QiijSa9U53r6jkCjrFBW5jjNVoXdUU,1224
62
- guarddog/analyzer/sourcecode/shady-links.yml,sha256=nbq1WLZGVGpriHcPcpBOo39vfYtFU3fU6w3glG2NeQk,3083
62
+ guarddog/analyzer/sourcecode/shady-links.yml,sha256=Jl2XO6O9vwyhfaj7K3u7-OtB4oQxKcdyb6-_qCrBdUo,3087
63
63
  guarddog/analyzer/sourcecode/silent-process-execution.yml,sha256=b6RjenMv7si7lXGak3uMmD7PMtQRuKPeJFggPW6UDNI,418
64
64
  guarddog/analyzer/sourcecode/steganography.yml,sha256=3ceO6SJhu4XpZEjfwelLdOxeZ4Ho1OgUjbcacwtOhR0,606
65
65
  guarddog/cli.py,sha256=TJT2yxoUokhoDm7P_FF-x0B9zbtxgDou5BFbu4vSWm4,13168
66
66
  guarddog/ecosystems.py,sha256=1-emct9cGLU3V0drEdNmGFEmxMEmJHEQOuyOiuuoCGA,489
67
67
  guarddog/reporters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
68
  guarddog/reporters/sarif.py,sha256=92HjvASZFyv5otB1qbsUqj6423tNgZbmSQS4qApffAw,5820
69
- guarddog/scanners/__init__.py,sha256=rWKVvLADz9G0nraNwiJIiSuLFb9p8u_V4qAl7HVt3jw,1762
69
+ guarddog/scanners/__init__.py,sha256=KNZcGjKNPOs60qpOE8Hr_HsiaRIpJLNzA8qbyvebRIk,1924
70
+ guarddog/scanners/github_action_project_scanner.py,sha256=FY5UEIGeQlmyyjh2Z1LDXsHnzfs2gCQ-wIurpbPvXN4,2847
70
71
  guarddog/scanners/github_action_scanner.py,sha256=GxhUSetLvT8YxKUIZue9MWOE_IVugM2MdiluOy4f068,1745
71
72
  guarddog/scanners/go_package_scanner.py,sha256=OdCbwtjJow9AxEv34z7WBfgTamqKj5DxJh7dly_1NuY,2926
72
73
  guarddog/scanners/go_project_scanner.py,sha256=3D5dYSA7FVqc7IIM7uAHlCJZalshP_WhagWmOcYirog,2123
@@ -80,10 +81,10 @@ guarddog/utils/archives.py,sha256=jOXAhxZx-mTtpDidGGKxQg052CvaQOAVklvOeUn9HTQ,25
80
81
  guarddog/utils/config.py,sha256=Msz7altsmNKry0vBPtL2BJ_VdBXsBFZX5ksLvXc2ix4,1403
81
82
  guarddog/utils/exceptions.py,sha256=23Kzl3exqYK6X-bcGUeb8wPmSglWNX3GIDPkJ6lQzo4,54
82
83
  guarddog/utils/package_info.py,sha256=TFjE1xsGNf60SuHlIeDV2pzMUbogl5TKJdSzswat6jI,953
83
- guarddog-2.4.0.dist-info/LICENSE,sha256=w1aNZxHyoyOPJ4fSdiyrr06tCJZbTjCsH9K1uqeDVyU,11377
84
- guarddog-2.4.0.dist-info/LICENSE-3rdparty.csv,sha256=cS61ONZL_xlXaTMvQXyBEi3J3es-40Gg6G-6idoa5Qk,314
85
- guarddog-2.4.0.dist-info/METADATA,sha256=WsXluo_bRC7j4N5BJMceuhQkvhdURMZMz7T3xY9e8fY,1432
86
- guarddog-2.4.0.dist-info/NOTICE,sha256=nlyNt2IjG8IBoQkb7n6jszwAvmREpKAx0POzFO1s2JM,140
87
- guarddog-2.4.0.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
88
- guarddog-2.4.0.dist-info/entry_points.txt,sha256=vX2fvhnNdkbEL4pDzrH2NqjWVxeOaEYi0sJYmNgS2-s,45
89
- guarddog-2.4.0.dist-info/RECORD,,
84
+ guarddog-2.5.0.dist-info/LICENSE,sha256=w1aNZxHyoyOPJ4fSdiyrr06tCJZbTjCsH9K1uqeDVyU,11377
85
+ guarddog-2.5.0.dist-info/LICENSE-3rdparty.csv,sha256=cS61ONZL_xlXaTMvQXyBEi3J3es-40Gg6G-6idoa5Qk,314
86
+ guarddog-2.5.0.dist-info/METADATA,sha256=ZqDqDVCnZh05Dgy9jpJ4SKqEX9ltSWJR15Qd5ATC9QI,1432
87
+ guarddog-2.5.0.dist-info/NOTICE,sha256=nlyNt2IjG8IBoQkb7n6jszwAvmREpKAx0POzFO1s2JM,140
88
+ guarddog-2.5.0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
89
+ guarddog-2.5.0.dist-info/entry_points.txt,sha256=vX2fvhnNdkbEL4pDzrH2NqjWVxeOaEYi0sJYmNgS2-s,45
90
+ guarddog-2.5.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.0.1
2
+ Generator: poetry-core 2.1.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any