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.
- guarddog/analyzer/metadata/pypi/typosquatting.py +1 -1
- guarddog/analyzer/sourcecode/shady-links.yml +1 -1
- guarddog/scanners/__init__.py +3 -0
- guarddog/scanners/github_action_project_scanner.py +101 -0
- {guarddog-2.4.0.dist-info → guarddog-2.5.0.dist-info}/METADATA +2 -2
- {guarddog-2.4.0.dist-info → guarddog-2.5.0.dist-info}/RECORD +11 -10
- {guarddog-2.4.0.dist-info → guarddog-2.5.0.dist-info}/WHEEL +1 -1
- {guarddog-2.4.0.dist-info → guarddog-2.5.0.dist-info}/LICENSE +0 -0
- {guarddog-2.4.0.dist-info → guarddog-2.5.0.dist-info}/LICENSE-3rdparty.csv +0 -0
- {guarddog-2.4.0.dist-info → guarddog-2.5.0.dist-info}/NOTICE +0 -0
- {guarddog-2.4.0.dist-info → guarddog-2.5.0.dist-info}/entry_points.txt +0 -0
|
@@ -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
|
|
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
|
guarddog/scanners/__init__.py
CHANGED
|
@@ -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.
|
|
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.
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
84
|
-
guarddog-2.
|
|
85
|
-
guarddog-2.
|
|
86
|
-
guarddog-2.
|
|
87
|
-
guarddog-2.
|
|
88
|
-
guarddog-2.
|
|
89
|
-
guarddog-2.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|