guarddog 2.5.0__py3-none-any.whl → 2.7.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.
Files changed (71) hide show
  1. guarddog/analyzer/analyzer.py +58 -20
  2. guarddog/analyzer/metadata/__init__.py +2 -0
  3. guarddog/analyzer/metadata/bundled_binary.py +6 -6
  4. guarddog/analyzer/metadata/deceptive_author.py +3 -1
  5. guarddog/analyzer/metadata/detector.py +7 -2
  6. guarddog/analyzer/metadata/empty_information.py +8 -3
  7. guarddog/analyzer/metadata/go/typosquatting.py +4 -3
  8. guarddog/analyzer/metadata/npm/bundled_binary.py +7 -2
  9. guarddog/analyzer/metadata/npm/deceptive_author.py +1 -1
  10. guarddog/analyzer/metadata/npm/direct_url_dependency.py +2 -1
  11. guarddog/analyzer/metadata/npm/empty_information.py +10 -7
  12. guarddog/analyzer/metadata/npm/potentially_compromised_email_domain.py +4 -3
  13. guarddog/analyzer/metadata/npm/release_zero.py +13 -5
  14. guarddog/analyzer/metadata/npm/typosquatting.py +1 -1
  15. guarddog/analyzer/metadata/npm/unclaimed_maintainer_email_domain.py +3 -2
  16. guarddog/analyzer/metadata/npm/utils.py +4 -5
  17. guarddog/analyzer/metadata/potentially_compromised_email_domain.py +8 -4
  18. guarddog/analyzer/metadata/pypi/__init__.py +12 -6
  19. guarddog/analyzer/metadata/pypi/bundled_binary.py +7 -2
  20. guarddog/analyzer/metadata/pypi/deceptive_author.py +1 -1
  21. guarddog/analyzer/metadata/pypi/empty_information.py +16 -5
  22. guarddog/analyzer/metadata/pypi/potentially_compromised_email_domain.py +4 -3
  23. guarddog/analyzer/metadata/pypi/release_zero.py +16 -6
  24. guarddog/analyzer/metadata/pypi/repository_integrity_mismatch.py +53 -27
  25. guarddog/analyzer/metadata/pypi/single_python_file.py +9 -4
  26. guarddog/analyzer/metadata/pypi/typosquatting.py +21 -8
  27. guarddog/analyzer/metadata/pypi/unclaimed_maintainer_email_domain.py +6 -2
  28. guarddog/analyzer/metadata/pypi/utils.py +1 -4
  29. guarddog/analyzer/metadata/release_zero.py +1 -1
  30. guarddog/analyzer/metadata/repository_integrity_mismatch.py +10 -3
  31. guarddog/analyzer/metadata/resources/top_pypi_packages.json +43984 -15984
  32. guarddog/analyzer/metadata/typosquatting.py +12 -8
  33. guarddog/analyzer/metadata/unclaimed_maintainer_email_domain.py +7 -2
  34. guarddog/analyzer/sourcecode/__init__.py +34 -7
  35. guarddog/analyzer/sourcecode/api-obfuscation.yml +42 -0
  36. guarddog/analyzer/sourcecode/code-execution.yml +1 -0
  37. guarddog/analyzer/sourcecode/dll-hijacking.yml +5 -0
  38. guarddog/analyzer/sourcecode/go-exec-base64.yml +40 -0
  39. guarddog/analyzer/sourcecode/go-exec-download.yml +85 -0
  40. guarddog/analyzer/sourcecode/go-exfiltrate-sensitive-data.yml +85 -0
  41. guarddog/analyzer/sourcecode/npm-obfuscation.yml +2 -1
  42. guarddog/analyzer/sourcecode/shady-links.yml +2 -0
  43. guarddog/analyzer/sourcecode/suspicious_passwd_access_linux.yar +12 -0
  44. guarddog/analyzer/sourcecode/unicode.yml +75 -0
  45. guarddog/cli.py +33 -107
  46. guarddog/ecosystems.py +3 -0
  47. guarddog/reporters/__init__.py +28 -0
  48. guarddog/reporters/human_readable.py +138 -0
  49. guarddog/reporters/json.py +28 -0
  50. guarddog/reporters/reporter_factory.py +50 -0
  51. guarddog/reporters/sarif.py +179 -173
  52. guarddog/scanners/__init__.py +5 -0
  53. guarddog/scanners/extension_scanner.py +152 -0
  54. guarddog/scanners/github_action_project_scanner.py +47 -8
  55. guarddog/scanners/github_action_scanner.py +6 -2
  56. guarddog/scanners/go_project_scanner.py +42 -5
  57. guarddog/scanners/npm_package_scanner.py +12 -4
  58. guarddog/scanners/npm_project_scanner.py +54 -10
  59. guarddog/scanners/pypi_package_scanner.py +9 -3
  60. guarddog/scanners/pypi_project_scanner.py +67 -29
  61. guarddog/scanners/scanner.py +247 -164
  62. guarddog/utils/archives.py +2 -1
  63. guarddog/utils/package_info.py +3 -1
  64. {guarddog-2.5.0.dist-info → guarddog-2.7.0.dist-info}/METADATA +11 -10
  65. guarddog-2.7.0.dist-info/RECORD +100 -0
  66. {guarddog-2.5.0.dist-info → guarddog-2.7.0.dist-info}/WHEEL +1 -1
  67. guarddog-2.5.0.dist-info/RECORD +0 -90
  68. {guarddog-2.5.0.dist-info → guarddog-2.7.0.dist-info}/entry_points.txt +0 -0
  69. {guarddog-2.5.0.dist-info → guarddog-2.7.0.dist-info/licenses}/LICENSE +0 -0
  70. {guarddog-2.5.0.dist-info → guarddog-2.7.0.dist-info/licenses}/LICENSE-3rdparty.csv +0 -0
  71. {guarddog-2.5.0.dist-info → guarddog-2.7.0.dist-info/licenses}/NOTICE +0 -0
@@ -2,11 +2,12 @@ import concurrent.futures
2
2
  import json
3
3
  import logging
4
4
  import os
5
- import sys
6
5
  import tempfile
7
6
  import typing
8
7
  from abc import abstractmethod
9
8
  from concurrent.futures import ThreadPoolExecutor
9
+ from dataclasses import dataclass
10
+ from typing import List, Optional, Set, Tuple
10
11
 
11
12
  import requests
12
13
 
@@ -21,183 +22,66 @@ def noop(arg: typing.Any) -> None:
21
22
  pass
22
23
 
23
24
 
24
- class ProjectScanner:
25
- def __init__(self, package_scanner):
26
- super().__init__()
27
- self.package_scanner = package_scanner
28
-
29
- def _authenticate_by_access_token(self) -> tuple[str, str]:
30
- """
31
- Gives GitHub authentication through access token
32
-
33
- Returns:
34
- tuple[str, str]: username, personal access token
35
- """
36
-
37
- user = os.getenv("GIT_USERNAME")
38
- personal_access_token = os.getenv("GH_TOKEN")
39
- if not user or not personal_access_token:
40
- log.error(
41
- """WARNING: Please set GIT_USERNAME (Github handle) and GH_TOKEN
42
- (generate a personal access token in Github settings > developer)
43
- as environment variables before proceeding."""
44
- )
45
- exit(1)
46
- return (user, personal_access_token)
47
-
48
- def scan_requirements(
49
- self,
50
- requirements: str,
51
- rules=None,
52
- callback: typing.Callable[[dict], None] = noop,
53
- ) -> dict:
54
- """
55
- Reads the requirements.txt file and scans each possible
56
- dependency and version
57
-
58
- Args:
59
- requirements (str): contents of requirements.txt file
60
- rules: list of rules to apply
61
- callback: callback to call for each result
62
-
63
- Returns:
64
- dict: mapping of dependencies to scan results
65
-
66
- ex.
67
- {
68
- ....
69
- <dependency-name>: {
70
- issues: ...,
71
- results: {
72
- ...
73
- }
74
- },
75
- ...
76
- }
77
- """
78
-
79
- def scan_single_dependency(dependency, version):
80
- log.debug(f"Scanning {dependency} version {version}")
81
- result = self.package_scanner.scan_remote(dependency, version, rules)
82
- return {"dependency": dependency, "version": version, "result": result}
83
-
84
- dependencies = self.parse_requirements(requirements)
85
- num_workers = PARALLELISM
86
-
87
- log.info(
88
- f"Scanning using at most {num_workers} parallel worker threads\n"
89
- )
90
- with ThreadPoolExecutor(max_workers=num_workers) as pool:
91
- try:
92
- futures: typing.List[concurrent.futures.Future] = []
93
- for dependency, versions in dependencies.items():
94
- assert versions is None or len(versions) > 0
95
- if versions is None:
96
- # this will cause scan_remote to use the latest version
97
- futures.append(
98
- pool.submit(scan_single_dependency, dependency, None)
99
- )
100
- else:
101
- futures.extend(
102
- map(
103
- lambda version: pool.submit(
104
- scan_single_dependency, dependency, version
105
- ),
106
- versions,
107
- )
108
- )
109
-
110
- results = []
111
- for future in concurrent.futures.as_completed(futures):
112
- result = future.result()
113
- if callback is not None:
114
- callback(result)
115
- results.append(result)
116
- except KeyboardInterrupt:
117
- log.warning("Received keyboard interrupt, cancelling scan\n")
118
- pool.shutdown(wait=False, cancel_futures=True)
25
+ @dataclass
26
+ class DependencyVersion:
27
+ """
28
+ This class represents the identified dependency versions in a project,
29
+ usually defined in a specification file (requirements.txt, package.json, etc.)
119
30
 
120
- return results # type: ignore
31
+ Attributes:
32
+ version (str): The version of the dependency. e.g., "1.0.0"
33
+ location (int): This indicates the line number in the specification file where the dependency is defined.
34
+ """
121
35
 
122
- def scan_remote(self, url: str, branch: str, requirements_name: str) -> dict:
123
- """
124
- Scans remote requirements.txt file
36
+ version: str # the version number of the dependency
37
+ location: int
125
38
 
126
- Args:
127
- url (str): url of the GitHub repo
128
- branch (str): branch containing requirements.txt
129
- requirements_name (str, optional): name of requirements file.
130
- Defaults to "requirements.txt".
39
+ def __eq__(self, other):
40
+ if isinstance(other, str):
41
+ return self.version == other
42
+ if isinstance(other, DependencyVersion):
43
+ return self.version == other.version
44
+ return NotImplemented
131
45
 
132
- Returns:
133
- dict: mapping of dependencies to scan results
46
+ def __hash__(self):
47
+ return hash(self.version)
134
48
 
135
- ex.
136
- {
137
- ....
138
- <dependency-name>: {
139
- issues: ...,
140
- results: {
141
- ...
142
- }
143
- },
144
- ...
145
- }
146
- """
49
+ def __repr__(self):
50
+ return f"DependencyVersion({self.version!r})"
147
51
 
148
- token = self._authenticate_by_access_token()
149
- githubusercontent_url = url.replace("github", "raw.githubusercontent")
150
52
 
151
- req_url = f"{githubusercontent_url}/{branch}/{requirements_name}"
152
- resp = requests.get(url=req_url, auth=token)
53
+ @dataclass
54
+ class Dependency:
55
+ """
56
+ This class represents a dependency in a project, usually defined in a specification file
153
57
 
154
- if resp.status_code == 200:
155
- return self.scan_requirements(resp.content.decode())
156
- else:
157
- log.error(
158
- f"{req_url} does not exist. Check your link or branch name."
159
- )
160
- sys.exit(255)
58
+ Attributes:
59
+ name (str): The name of the dependency. e.g., "requests"
60
+ versions (Set[DependencyVersion]): A set of identified versions of the dependency.
61
+ """
161
62
 
162
- def scan_local(
163
- self, path, rules=None, callback: typing.Callable[[dict], None] = noop
164
- ):
165
- """
166
- Scans a local requirements.txt file
63
+ name: str
64
+ versions: Set[DependencyVersion]
167
65
 
168
- Args:
169
- path (str): path to requirements.txt file
170
- rules: list of rules to apply
171
- callback: callback to call for each result
66
+ def __eq__(self, other):
67
+ if isinstance(other, str):
68
+ return self.name == other
69
+ if isinstance(other, Dependency):
70
+ return self.name == other.name
71
+ return NotImplemented
172
72
 
173
- Returns:
174
- dict: mapping of dependencies to scan results
73
+ def __repr__(self):
74
+ return f"Dependency({self.name!r})"
175
75
 
176
- ex.
177
- {
178
- ....
179
- <dependency-name>: {
180
- issues: ...,
181
- results: {
182
- ...
183
- }
184
- },
185
- ...
186
- }
187
- """
188
76
 
189
- try:
190
- with open(path, "r") as f:
191
- return self.scan_requirements(f.read(), rules, callback)
192
- except Exception as e:
193
- log.error(f"Received {e}")
194
- sys.exit(255)
77
+ @dataclass
78
+ class DependencyFile:
79
+ """
80
+ This class represents a specification file for a project (requirements.txt, package.json, etc.)
81
+ """
195
82
 
196
- @abstractmethod
197
- def parse_requirements(
198
- self, raw_requirements: str
199
- ) -> dict[str, set[str]]: # returns { package: version }
200
- pass
83
+ file_path: str
84
+ dependencies: List[Dependency]
201
85
 
202
86
 
203
87
  class PackageScanner:
@@ -324,3 +208,202 @@ class PackageScanner:
324
208
  finally:
325
209
  log.debug(f"Removing temporary archive file {archive_path}")
326
210
  os.remove(archive_path)
211
+
212
+
213
+ class ProjectScanner:
214
+ def __init__(self, package_scanner: PackageScanner):
215
+ super().__init__()
216
+ self.package_scanner = package_scanner
217
+
218
+ def _authenticate_by_access_token(self) -> tuple[str, str]:
219
+ """
220
+ Gives GitHub authentication through access token
221
+
222
+ Returns:
223
+ tuple[str, str]: username, personal access token
224
+ """
225
+
226
+ user = os.getenv("GIT_USERNAME")
227
+ personal_access_token = os.getenv("GH_TOKEN")
228
+ if not user or not personal_access_token:
229
+ log.error(
230
+ """WARNING: Please set GIT_USERNAME (Github handle) and GH_TOKEN
231
+ (generate a personal access token in Github settings > developer)
232
+ as environment variables before proceeding."""
233
+ )
234
+ exit(1)
235
+ return (user, personal_access_token)
236
+
237
+ def scan_dependencies(
238
+ self,
239
+ dependencies: List[Dependency],
240
+ rules=None,
241
+ callback: typing.Callable[[dict], None] = noop,
242
+ ) -> list[dict]:
243
+ """
244
+ scans each possible dependency and version supplied
245
+
246
+ Args:
247
+ dependencies a list of dependencies to scan
248
+ rules: list of rules to apply
249
+ callback: callback to call for each result
250
+
251
+ Returns:
252
+ dict: mapping of dependencies to scan results
253
+
254
+ ex.
255
+ {
256
+ ....
257
+ <dependency-name>: {
258
+ issues: ...,
259
+ results: {
260
+ ...
261
+ }
262
+ },
263
+ ...
264
+ }
265
+ """
266
+
267
+ def scan_single_dependency(dependency: str, version: Optional[str]) -> dict:
268
+ log.debug(f"Scanning {dependency} version {version}")
269
+ result = self.package_scanner.scan_remote(dependency, version, rules)
270
+ return {"dependency": dependency, "version": version, "result": result}
271
+
272
+ num_workers = PARALLELISM
273
+
274
+ log.info(f"Scanning using at most {num_workers} parallel worker threads\n")
275
+ with ThreadPoolExecutor(max_workers=num_workers) as pool:
276
+ try:
277
+ futures: typing.List[concurrent.futures.Future] = []
278
+ for dependency in dependencies:
279
+ versions = dependency.versions
280
+ if not versions:
281
+ # this will cause scan_remote to use the latest version
282
+ futures.append(
283
+ pool.submit(scan_single_dependency, dependency.name, None)
284
+ )
285
+ else:
286
+ futures.extend(
287
+ map(
288
+ lambda version: pool.submit(
289
+ scan_single_dependency,
290
+ dependency.name,
291
+ version.version,
292
+ ),
293
+ versions,
294
+ )
295
+ )
296
+
297
+ results = []
298
+ for future in concurrent.futures.as_completed(futures):
299
+ result = future.result()
300
+ if callback is not None:
301
+ callback(result)
302
+ results.append(result)
303
+ except KeyboardInterrupt:
304
+ log.warning("Received keyboard interrupt, cancelling scan\n")
305
+ pool.shutdown(wait=False, cancel_futures=True)
306
+
307
+ return results
308
+
309
+ def scan_remote(
310
+ self, url: str, branch: str, requirements_name: str
311
+ ) -> tuple[List[Dependency], list[dict]]:
312
+ """
313
+ Scans remote requirements.txt file
314
+
315
+ Args:
316
+ url (str): url of the GitHub repo
317
+ branch (str): branch containing requirements.txt
318
+ requirements_name (str, optional): name of requirements file.
319
+ Defaults to "requirements.txt".
320
+
321
+ Returns:
322
+ deps: list of dependencies to scan
323
+ results: mapping of dependencies to scan results
324
+ ex.
325
+ {
326
+ ....
327
+ <dependency-name>: {
328
+ issues: ...,
329
+ results: {
330
+ ...
331
+ }
332
+ },
333
+ ...
334
+ }
335
+ """
336
+
337
+ token = self._authenticate_by_access_token()
338
+ githubusercontent_url = url.replace("github", "raw.githubusercontent")
339
+ req_url = f"{githubusercontent_url}/{branch}/{requirements_name}"
340
+ resp = requests.get(url=req_url, auth=token)
341
+ resp.raise_for_status()
342
+ dependencies = self.parse_requirements(resp.content.decode())
343
+ return dependencies, self.scan_dependencies(dependencies)
344
+
345
+ def scan_local(
346
+ self, path, rules=None, callback: typing.Callable[[dict], None] = noop
347
+ ) -> Tuple[List[DependencyFile], list[dict]]:
348
+ """
349
+ Scans a local requirements files (requirements.txt, package.json, etc.)
350
+
351
+ Args:
352
+ path (str): path to requirements file or directory to search
353
+ rules: list of rules to apply
354
+ callback: callback to call for each result
355
+
356
+ Returns:
357
+ deps: list of dependencies to scan
358
+ results: mapping of dependencies to scan results
359
+ ex.
360
+ {
361
+ ....
362
+ <dependency-name>: {
363
+ issues: ...,
364
+ results: {
365
+ ...
366
+ }
367
+ },
368
+ ...
369
+ }
370
+
371
+ """
372
+
373
+ requirement_paths = []
374
+
375
+ try:
376
+ if os.path.isfile(path):
377
+ requirement_paths.append(path)
378
+ elif os.path.isdir(path):
379
+ requirement_paths.extend(self.find_requirements(path))
380
+ else:
381
+ raise ValueError(f"unable to find file or directory {path}")
382
+
383
+ dep_files: List[DependencyFile] = []
384
+
385
+ for req in requirement_paths:
386
+ with open(req, "r") as f:
387
+ dep_files.append(
388
+ DependencyFile(
389
+ file_path=req,
390
+ dependencies=self.parse_requirements(f.read()),
391
+ )
392
+ )
393
+ deps_to_scan = [d for d_file in dep_files for d in d_file.dependencies]
394
+ results = self.scan_dependencies(deps_to_scan, rules, callback)
395
+ return dep_files, results
396
+ except Exception as e:
397
+ log.error(f"Error while scanning. Received {e}")
398
+ raise e
399
+
400
+ @abstractmethod
401
+ def parse_requirements(self, raw_requirements: str) -> List[Dependency]:
402
+ pass
403
+
404
+ @abstractmethod
405
+ def find_requirements(
406
+ self,
407
+ directory: str,
408
+ ) -> list[str]: # returns paths of files
409
+ pass
@@ -20,6 +20,7 @@ def is_supported_archive(path: str) -> bool:
20
20
  bool: Represents the decision reached for the file
21
21
 
22
22
  """
23
+
23
24
  def is_tar_archive(path: str) -> bool:
24
25
  tar_exts = [".bz2", ".bzip2", ".gz", ".gzip", ".tgz", ".xz"]
25
26
 
@@ -68,7 +69,7 @@ def safe_extract(source_archive: str, target_directory: str) -> None:
68
69
  recurse_add_perms(target_directory)
69
70
 
70
71
  elif zipfile.is_zipfile(source_archive):
71
- with zipfile.ZipFile(source_archive, 'r') as zip:
72
+ with zipfile.ZipFile(source_archive, "r") as zip:
72
73
  for file in zip.namelist():
73
74
  # Note: zip.extract cleans up any malicious file name
74
75
  # such as directory traversal attempts This is not the
@@ -25,7 +25,9 @@ def get_package_info(name: str) -> dict:
25
25
 
26
26
  # Check if package file exists
27
27
  if response.status_code != 200:
28
- raise Exception("Received status code: " + str(response.status_code) + " from PyPI")
28
+ raise Exception(
29
+ "Received status code: " + str(response.status_code) + " from PyPI"
30
+ )
29
31
 
30
32
  data = response.json()
31
33
 
@@ -1,8 +1,11 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: guarddog
3
- Version: 2.5.0
4
- Summary: GuardDog is a CLI tool to Identify malicious PyPI packages
3
+ Version: 2.7.0
4
+ Summary: GuardDog is a CLI tool for identifying malicious open source packages
5
5
  License: Apache-2.0
6
+ License-File: LICENSE
7
+ License-File: LICENSE-3rdparty.csv
8
+ License-File: NOTICE
6
9
  Author: Ellen Wang
7
10
  Requires-Python: >=3.10,<4
8
11
  Classifier: License :: OSI Approved :: Apache Software License
@@ -11,23 +14,21 @@ Classifier: Programming Language :: Python :: 3.10
11
14
  Classifier: Programming Language :: Python :: 3.11
12
15
  Classifier: Programming Language :: Python :: 3.12
13
16
  Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
14
18
  Requires-Dist: click (>=8.1.3,<9.0.0)
15
- Requires-Dist: click-option-group (>=0.5.5,<0.6.0)
16
- Requires-Dist: colorama (>=0.4.6,<0.5.0)
17
19
  Requires-Dist: configparser (>=5.3,<8.0)
18
- Requires-Dist: disposable-email-domains (>=0.0.103,<0.0.119)
20
+ Requires-Dist: disposable-email-domains (>=0.0.103,<0.0.121)
19
21
  Requires-Dist: prettytable (>=3.6.0,<4.0.0)
20
- Requires-Dist: pygit2 (>=1.11,<1.18)
22
+ Requires-Dist: pygit2 (>=1.11,<1.19)
21
23
  Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
22
24
  Requires-Dist: python-whois (>=0.8,<0.10)
23
25
  Requires-Dist: pyyaml (>=6.0,<7.0)
24
26
  Requires-Dist: requests (>=2.29.0,<3.0.0)
25
27
  Requires-Dist: semantic-version (>=2.10.0,<3.0.0)
26
- Requires-Dist: semgrep (>=1.102.0,<2.0.0)
27
- Requires-Dist: setuptools (>=70.3,<76.0)
28
+ Requires-Dist: semgrep (==1.121.0)
28
29
  Requires-Dist: tarsafe (>=0.0.5,<0.0.6)
29
30
  Requires-Dist: termcolor (>=2.1.0,<3.0.0)
30
- Requires-Dist: urllib3 (==2.3.0)
31
+ Requires-Dist: urllib3 (>=2.5.0,<3.0.0)
31
32
  Requires-Dist: yara-python (>=4.5.1,<5.0.0)
32
33
  Project-URL: Repository, https://github.com/DataDog/guarddog
33
34
  Description-Content-Type: text/x-rst
@@ -0,0 +1,100 @@
1
+ guarddog/__init__.py,sha256=reb53KZG9b1nFmsDxj2fropaOceOCyM9bVMUdmZ2wS8,227
2
+ guarddog/__main__.py,sha256=GEdfW6I6g2c3H7bS0G43E4C-g7kXGUswzDCPFSwPgHY,246
3
+ guarddog/analyzer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ guarddog/analyzer/analyzer.py,sha256=eeWbazqGWFJBeT_OHHmEndH3hG_6PMxUajof6T9jutM,15413
5
+ guarddog/analyzer/metadata/__init__.py,sha256=tQTwWanifLsxfCdXIytPCO3chEIiTZ583uqKiQXQOog,855
6
+ guarddog/analyzer/metadata/bundled_binary.py,sha256=Tfgbc-exhbfvYjpgFH_Aa5KtT4ugJhrTV9SavZw1pHs,2594
7
+ guarddog/analyzer/metadata/deceptive_author.py,sha256=CLwntpSNBO4Bji3IEw2lctvNCMLiOxz0pG7KSEzIynM,2811
8
+ guarddog/analyzer/metadata/detector.py,sha256=dFhmoPVtxoed-Lz3Bd8GXZhorOVajvo5sIl9Iw8oCOM,641
9
+ guarddog/analyzer/metadata/empty_information.py,sha256=qswYDOrL3MxJ4VYq893Eiev5y56fgXrk5-CTKVXbkeA,1199
10
+ guarddog/analyzer/metadata/github_action/__init__.py,sha256=hOtiXKW-v5slzYW2M3k35M_YFfuLm8CNv5MwNSdFYMM,311
11
+ guarddog/analyzer/metadata/go/__init__.py,sha256=apwPnP9D4WEqgtR4RY0YIuFN7oNJXxJE_vYlp0ffRvQ,391
12
+ guarddog/analyzer/metadata/go/typosquatting.py,sha256=gU7VK5agC9T4xrWzliMHbcUOf2opw2MtLdMEf8n1_vI,4019
13
+ guarddog/analyzer/metadata/npm/__init__.py,sha256=j1Ng74bb1yD9XHFoYmJPzWL7vYMmLt6c2Lbc8lCqnUI,1326
14
+ guarddog/analyzer/metadata/npm/bundled_binary.py,sha256=vxJLhaTS7wymbktvmfJsF3whz3DWisjdD4wqHlNXvhg,392
15
+ guarddog/analyzer/metadata/npm/deceptive_author.py,sha256=RIBCWK3NjZiTf7tiz2V0ECy2Zr6Uwb69RQwIcWku380,366
16
+ guarddog/analyzer/metadata/npm/direct_url_dependency.py,sha256=hjjTLIT0UVudSf9A9Hory2OAqUkdxoXfKGXDgMtnNso,2449
17
+ guarddog/analyzer/metadata/npm/empty_information.py,sha256=Vpjr5Xe8JB4RIPzM0-BNmiqYiuY42LUbml7Yjig7Rcs,791
18
+ guarddog/analyzer/metadata/npm/npm_metadata_mismatch.py,sha256=Fj9MT7XlO2iXis4Da-_0CmM0weQiv8bVzKUoSm8ntYU,4428
19
+ guarddog/analyzer/metadata/npm/potentially_compromised_email_domain.py,sha256=Sm7fBfzayrbYXOpU5XzeCGKNfcX40hMOCSjKjhKwz-g,1719
20
+ guarddog/analyzer/metadata/npm/release_zero.py,sha256=FNHYfxl52i0V3HydccspcfF82T5L9d3ZyE-_J-UVoS0,633
21
+ guarddog/analyzer/metadata/npm/typosquatting.py,sha256=Roq7KeXO5P7DPtV9WivMhIgEXpBwoANr-nZo7VeGmCc,3146
22
+ guarddog/analyzer/metadata/npm/unclaimed_maintainer_email_domain.py,sha256=B8olfxXiaSM8c47dyftIgxmMVuwg4s7dMuVxrMS0GNE,953
23
+ guarddog/analyzer/metadata/npm/utils.py,sha256=QirjkoXhDcrGfoteh-V717TGq1xpXmvuC9dEp_5bt2s,454
24
+ guarddog/analyzer/metadata/potentially_compromised_email_domain.py,sha256=p2KCIByv4dBNM8h_1xPJgAOL197LLDvSLalZQpsvadg,2960
25
+ guarddog/analyzer/metadata/pypi/__init__.py,sha256=ef2tKnVzJPVy2eLgvBDII77t-zKOCPBOC6dmTCl_XBc,1381
26
+ guarddog/analyzer/metadata/pypi/bundled_binary.py,sha256=J5FqMYPTYnmb2MX9BFextVM5P0IzR522fhWV8TuTYMg,393
27
+ guarddog/analyzer/metadata/pypi/deceptive_author.py,sha256=KRbi7xfGYnEiq0p5HFazjV00a-3tIGe2ogXRvPhz9tI,367
28
+ guarddog/analyzer/metadata/pypi/empty_information.py,sha256=Ppaa_aEIlFJ5VpRtcm2ozlBoRHrTs6CWpPyh82sTM7g,814
29
+ guarddog/analyzer/metadata/pypi/potentially_compromised_email_domain.py,sha256=8zncGNsyfhWe04HRDF54rfP8y--_6l9ze-F-gH97pWo,1774
30
+ guarddog/analyzer/metadata/pypi/release_zero.py,sha256=UNUAFeB34B88N0LRe-tnmoZ3Y4x4AVZbe7m0i9Q3W7U,789
31
+ guarddog/analyzer/metadata/pypi/repository_integrity_mismatch.py,sha256=RB-Wf5Cbuh8KswkcCCHk7BVWvmOJccdHyb4mBjxiLhk,11903
32
+ guarddog/analyzer/metadata/pypi/single_python_file.py,sha256=L-YlmlP1TYA9XBeTHBPfECWgVIPTenUWRRnqwCMyh-o,1402
33
+ guarddog/analyzer/metadata/pypi/typosquatting.py,sha256=hqCBmmKL3_hp36iqsLYYYpqEQZYPQdFcAEpGn5b1F8w,4858
34
+ guarddog/analyzer/metadata/pypi/unclaimed_maintainer_email_domain.py,sha256=zfT0qTyN83O-7Yevc6tYIjmCAgh8Y_dhE6oZlKdNmm0,421
35
+ guarddog/analyzer/metadata/pypi/utils.py,sha256=UtG2JVep8bSOMz5LkrhXqS5Oy7Na19nHVct4IQMsQik,177
36
+ guarddog/analyzer/metadata/release_zero.py,sha256=F0I8coYivya7zns0XI1xNY4LLtTDQXoBUmze1wjwW4g,439
37
+ guarddog/analyzer/metadata/repository_integrity_mismatch.py,sha256=WGvck9JV2iCB8xaOc6X5cEGiu59juSWFfwbf6uEah3E,792
38
+ guarddog/analyzer/metadata/resources/placeholder_email_domains.txt,sha256=o3mm9u6vuaVeN4wRgDTidR5oL6ufLTCrE9ISVYbOGUc,11
39
+ guarddog/analyzer/metadata/resources/top_go_packages.json,sha256=HHOTcuWTGqlpXDOUgF7ejgmr8sGF_T5l7NQYdXmHcKQ,104044
40
+ guarddog/analyzer/metadata/resources/top_npm_packages.json,sha256=eeqVkFNW8ltYcGbjAJBzZrdxBEKezxa6AVVYoEpFazs,192960
41
+ guarddog/analyzer/metadata/resources/top_pypi_packages.json,sha256=7tN8yUTqbpq3HvNePK9IKrTIEeYblTMHXhUzyOdVN-w,1479906
42
+ guarddog/analyzer/metadata/typosquatting.py,sha256=6xmqsB0oKwrsXfTJ9DE1z8-nUW8ukgk1ZSS32iJmapk,4587
43
+ guarddog/analyzer/metadata/unclaimed_maintainer_email_domain.py,sha256=e-K9mSdph3y33fP_W7LNOlC7AnUVk28a1VfQJtG9vxo,2375
44
+ guarddog/analyzer/metadata/utils.py,sha256=bOrkELPza4ScUx1DfQxlqU-9DQeA5weISF42c0QCtls,1768
45
+ guarddog/analyzer/sourcecode/__init__.py,sha256=031VVi-DqTHxeYiI2mf2AuI76GaNUUyra_t0pRVqo5k,4631
46
+ guarddog/analyzer/sourcecode/api-obfuscation.yml,sha256=y_m6PSh8CfF6nxMulj2Yx4XYxS1T5OO0Eos9WJiDR74,2137
47
+ guarddog/analyzer/sourcecode/clipboard-access.yml,sha256=B36E7xKtAVgwZ29UWtvZa1AJcyfrhvehbLo6tlJqffk,524
48
+ guarddog/analyzer/sourcecode/cmd-overwrite.yml,sha256=l-tE3_G-LqCuCZnHab6v0PpCdMpoHPutBYcijeMZEA0,682
49
+ guarddog/analyzer/sourcecode/code-execution.yml,sha256=YyWBMhcXGzrM6JbXzRTBBFCs7bnoNGPFJJ_FIV2OYtc,5028
50
+ guarddog/analyzer/sourcecode/dll-hijacking.yml,sha256=_lkYp0P4545aiGazC5lRBeCcMHhGWzaK-95zu5MfRLY,3721
51
+ guarddog/analyzer/sourcecode/download-executable.yml,sha256=VuSNkpVh3DxHG7wfep3eAErGsOY9EL_268sNULYbfW4,3361
52
+ guarddog/analyzer/sourcecode/exec-base64.yml,sha256=Wg1jI_ff9I58Xq8gt8wXOQMrwHcPnzkAPyAURxnKHgw,2371
53
+ guarddog/analyzer/sourcecode/exfiltrate-sensitive-data.yml,sha256=hUxQEsJ4qF_25oMF8pdzAFOzq59m6k28WKz280uyaMg,2264
54
+ guarddog/analyzer/sourcecode/go-exec-base64.yml,sha256=Y5TUfLrmU1e5FTYW2zRKwn8yluBARHSXPr6Mr5vMVOY,1554
55
+ guarddog/analyzer/sourcecode/go-exec-download.yml,sha256=ZaZOvn3Xojsd2m8MQGLW1H7p28bPdpEbmDd37q2ZiX4,2931
56
+ guarddog/analyzer/sourcecode/go-exfiltrate-sensitive-data.yml,sha256=sb5GI-523zgE1nxNCrnRVjBSeOp7IfPy7qTQPBJMkco,3697
57
+ guarddog/analyzer/sourcecode/npm-dll-hijacking.yml,sha256=1TvI6UtCGCOMy4Ii-kM_oICYbMRGeOYdgXrG7-zmJ_Y,3460
58
+ guarddog/analyzer/sourcecode/npm-exec-base64.yml,sha256=zc5w2FTlHoZ7ot1flzlmYBkQu1I8eG1E63S5Aki7Goc,814
59
+ guarddog/analyzer/sourcecode/npm-exfiltrate-sensitive-data.yml,sha256=UYWXdkAab-dg_6UwVjiauHmy-9nlKiF86qcyxAwUoXg,3488
60
+ guarddog/analyzer/sourcecode/npm-install-script.yml,sha256=6BLe_V0SGEi1C79Y-FEIcMYHl4vLOOz8bLPrCU5jre8,1329
61
+ guarddog/analyzer/sourcecode/npm-obfuscation.yml,sha256=UxR5ezKr9sFcXEh2JKa20IYqq25J0JDfje82O3jUYMg,2174
62
+ guarddog/analyzer/sourcecode/npm-serialize-environment.yml,sha256=gFpr58INp44ZwxYZlIHyzpOgbVMDLv1ZRPTGAczX5dw,835
63
+ guarddog/analyzer/sourcecode/npm-silent-process-execution.yml,sha256=qnJHGesNPNpxGa8n2kQMpttLGck-6vZjI_SsweDyk7M,3513
64
+ guarddog/analyzer/sourcecode/npm-steganography.yml,sha256=XH0udcriAQq_6WOHAG4TpIedw8GgKyWx9gsG_Q_Fki8,915
65
+ guarddog/analyzer/sourcecode/obfuscation.yml,sha256=dp0BeCYShcTS8QiijSa9U53r6jkCjrFBW5jjNVoXdUU,1224
66
+ guarddog/analyzer/sourcecode/shady-links.yml,sha256=uDYVWDh0u20oy2zbXTJns64lvrQzLi95CLWgnftvX6Y,3222
67
+ guarddog/analyzer/sourcecode/silent-process-execution.yml,sha256=b6RjenMv7si7lXGak3uMmD7PMtQRuKPeJFggPW6UDNI,418
68
+ guarddog/analyzer/sourcecode/steganography.yml,sha256=3ceO6SJhu4XpZEjfwelLdOxeZ4Ho1OgUjbcacwtOhR0,606
69
+ guarddog/analyzer/sourcecode/suspicious_passwd_access_linux.yar,sha256=kplidsJ-ctg6W58VlYtLq10saZbcD1pm5_Xh4sqmHwk,422
70
+ guarddog/analyzer/sourcecode/unicode.yml,sha256=7fAygEtYwJ1iNKsyCjmLAEu15CLMWApfWXx_t_W3sOA,5596
71
+ guarddog/cli.py,sha256=Pk4WUD5a_TlPRpq2G4v_6FDGWu8IriXQPQ_ft8RXm5o,10692
72
+ guarddog/ecosystems.py,sha256=I1XPAhPuv7OnfZT3z0xcgEecUY1tFJdrklV07sMYffg,582
73
+ guarddog/reporters/__init__.py,sha256=lHNa5ZDsaIpjzS7SmheD5_GGAimGitXU-DNk-Wn97bI,749
74
+ guarddog/reporters/human_readable.py,sha256=WEyjOPdBE8adxC-tdFgwxcyDijsppLk4gIiZOUO69O0,4548
75
+ guarddog/reporters/json.py,sha256=gpbucxGoXBA6s7fNRzhQwZ4P6gWyz7BowsmQrnm4x6U,802
76
+ guarddog/reporters/reporter_factory.py,sha256=JUagC2UFkN2TZGpZIkI1MwMHEbwT9Ja1goQP95-k9SM,1465
77
+ guarddog/reporters/sarif.py,sha256=diOHJcN3CkSBxBDDg6l9DiZ3ebtUNCw0Rwd7QxCpM9k,7691
78
+ guarddog/scanners/__init__.py,sha256=dyBzyKANxTQvyd-oTjgm43gPwRMqB80eOzZ6UFNOuO8,2157
79
+ guarddog/scanners/extension_scanner.py,sha256=YdZ7Ai4U-MC83RJcnoo63m_aylAf3VnylwgvhGK3ktU,4915
80
+ guarddog/scanners/github_action_project_scanner.py,sha256=ISoBqUurwN0lMBtXwcNoalo3ghlbOJkZs9vSNZOT0kk,4216
81
+ guarddog/scanners/github_action_scanner.py,sha256=6lriTel3U7vNmCWBf0SWti9sLCv88RPlP8SVoAgpKJs,1781
82
+ guarddog/scanners/go_package_scanner.py,sha256=OdCbwtjJow9AxEv34z7WBfgTamqKj5DxJh7dly_1NuY,2926
83
+ guarddog/scanners/go_project_scanner.py,sha256=2suZJWvYBhiiBMIQXs38SR04E_Ast50jO44X27gEG10,3349
84
+ guarddog/scanners/npm_package_scanner.py,sha256=ciOvpRViMIQvNFupe5-hdXv65QLU5ObmacRkR2pgp18,2056
85
+ guarddog/scanners/npm_project_scanner.py,sha256=liz5Fyscab53IiSPg0T21Z0vT5eotcHPc_W5Xam4A88,4957
86
+ guarddog/scanners/pypi_package_scanner.py,sha256=ZkuRRbNejnpfFpIHJJ42GH34khiG8CUKWEPvVh_M_uk,2449
87
+ guarddog/scanners/pypi_project_scanner.py,sha256=O91c1UP2iZju84_N7cSE7pWGrY6rKapeUqXEVyKld3A,6435
88
+ guarddog/scanners/scanner.py,sha256=F7FhN-BQWtcTvh_gdhvj-rXYLMslzeTNxPbJsw1he2s,13695
89
+ guarddog/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
+ guarddog/utils/archives.py,sha256=OJBmRI3Gnr01_dUddI3MuTjXXlEyIjj3aLwffxBfMEc,2594
91
+ guarddog/utils/config.py,sha256=Msz7altsmNKry0vBPtL2BJ_VdBXsBFZX5ksLvXc2ix4,1403
92
+ guarddog/utils/exceptions.py,sha256=23Kzl3exqYK6X-bcGUeb8wPmSglWNX3GIDPkJ6lQzo4,54
93
+ guarddog/utils/package_info.py,sha256=6fHJPeLn6-tHhKHw0Soedfv2ruPd8zhW2kbhlc3Aem0,975
94
+ guarddog-2.7.0.dist-info/METADATA,sha256=GivoRFtvZAPGs4kqFkSePkEvpLgm87j4rqkA7DDz96I,1439
95
+ guarddog-2.7.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
96
+ guarddog-2.7.0.dist-info/entry_points.txt,sha256=vX2fvhnNdkbEL4pDzrH2NqjWVxeOaEYi0sJYmNgS2-s,45
97
+ guarddog-2.7.0.dist-info/licenses/LICENSE,sha256=w1aNZxHyoyOPJ4fSdiyrr06tCJZbTjCsH9K1uqeDVyU,11377
98
+ guarddog-2.7.0.dist-info/licenses/LICENSE-3rdparty.csv,sha256=cS61ONZL_xlXaTMvQXyBEi3J3es-40Gg6G-6idoa5Qk,314
99
+ guarddog-2.7.0.dist-info/licenses/NOTICE,sha256=nlyNt2IjG8IBoQkb7n6jszwAvmREpKAx0POzFO1s2JM,140
100
+ guarddog-2.7.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.1
2
+ Generator: poetry-core 2.2.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any