java-dependency-analyzer 1.0.0__tar.gz → 1.0.1__tar.gz

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 (32) hide show
  1. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/PKG-INFO +25 -2
  2. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/README.md +24 -1
  3. java_dependency_analyzer-1.0.1/java_dependency_analyzer/__init__.py +18 -0
  4. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/cache/vulnerability_cache.py +6 -2
  5. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/parsers/base.py +4 -4
  6. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/parsers/gradle_dep_tree_parser.py +1 -3
  7. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/parsers/gradle_parser.py +14 -4
  8. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/parsers/maven_dep_tree_parser.py +1 -3
  9. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/parsers/maven_parser.py +13 -4
  10. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/resolvers/transitive.py +14 -9
  11. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/scanners/base.py +1 -3
  12. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/scanners/ghsa_scanner.py +2 -1
  13. java_dependency_analyzer-1.0.1/logging.ini +28 -0
  14. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/pyproject.toml +2 -1
  15. java_dependency_analyzer-1.0.0/java_dependency_analyzer/__init__.py +0 -11
  16. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/cache/__init__.py +0 -0
  17. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/cache/db.py +0 -0
  18. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/cli.py +0 -0
  19. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/models/__init__.py +0 -0
  20. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/models/dependency.py +0 -0
  21. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/models/report.py +0 -0
  22. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/parsers/__init__.py +0 -0
  23. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/reporters/__init__.py +0 -0
  24. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/reporters/base.py +0 -0
  25. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/reporters/html_reporter.py +0 -0
  26. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/reporters/json_reporter.py +0 -0
  27. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/reporters/templates/report.html +0 -0
  28. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/resolvers/__init__.py +0 -0
  29. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/scanners/__init__.py +0 -0
  30. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/scanners/osv_scanner.py +0 -0
  31. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/util/__init__.py +0 -0
  32. {java_dependency_analyzer-1.0.0 → java_dependency_analyzer-1.0.1}/java_dependency_analyzer/util/logger.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: java-dependency-analyzer
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: Java Dependency Analyzer is a tool that inspects dependencies.
5
5
  Author: Ron Webb
6
6
  Author-email: ron@ronella.xyz
@@ -15,7 +15,7 @@ Requires-Dist: lxml (>=6.0.2,<7.0.0)
15
15
  Requires-Dist: python-dotenv (>=1.2.2,<2.0.0)
16
16
  Description-Content-Type: text/markdown
17
17
 
18
- # Java Dependency Analyzer 1.0.0
18
+ # Java Dependency Analyzer 1.0.1
19
19
 
20
20
  > A Python CLI tool that inspects Java dependency hierarchies in Maven and Gradle projects and reports known vulnerabilities.
21
21
 
@@ -185,6 +185,29 @@ poetry run black java_dependency_analyzer
185
185
  poetry run pylint java_dependency_analyzer
186
186
  ```
187
187
 
188
+ ## Publishing to PyPI
189
+
190
+ ### Prerequisites
191
+
192
+ - A [PyPI](https://pypi.org/) account with an API token.
193
+
194
+ ### Configure the token
195
+
196
+ ```bash
197
+ poetry config pypi-token.pypi <your-token>
198
+ ```
199
+
200
+ ### Build and publish
201
+
202
+ ```bash
203
+ poetry publish --build
204
+ ```
205
+
206
+ This builds the source distribution and wheel, then uploads them to PyPI in one step.
207
+
208
+ > **Note:** PyPI releases are immutable. Once a version is published, it cannot be overwritten.
209
+ > To fix a mistake, yank the release via the PyPI web UI and publish a new version.
210
+
188
211
  ## [Changelog](CHANGELOG.md)
189
212
 
190
213
  ## Author
@@ -1,4 +1,4 @@
1
- # Java Dependency Analyzer 1.0.0
1
+ # Java Dependency Analyzer 1.0.1
2
2
 
3
3
  > A Python CLI tool that inspects Java dependency hierarchies in Maven and Gradle projects and reports known vulnerabilities.
4
4
 
@@ -168,6 +168,29 @@ poetry run black java_dependency_analyzer
168
168
  poetry run pylint java_dependency_analyzer
169
169
  ```
170
170
 
171
+ ## Publishing to PyPI
172
+
173
+ ### Prerequisites
174
+
175
+ - A [PyPI](https://pypi.org/) account with an API token.
176
+
177
+ ### Configure the token
178
+
179
+ ```bash
180
+ poetry config pypi-token.pypi <your-token>
181
+ ```
182
+
183
+ ### Build and publish
184
+
185
+ ```bash
186
+ poetry publish --build
187
+ ```
188
+
189
+ This builds the source distribution and wheel, then uploads them to PyPI in one step.
190
+
191
+ > **Note:** PyPI releases are immutable. Once a version is published, it cannot be overwritten.
192
+ > To fix a mistake, yank the release via the PyPI web UI and publish a new version.
193
+
171
194
  ## [Changelog](CHANGELOG.md)
172
195
 
173
196
  ## Author
@@ -0,0 +1,18 @@
1
+ """
2
+ java_dependency_analyzer package.
3
+
4
+ Java Dependency Analyzer is a tool that inspects dependencies.
5
+
6
+ :author: Ron Webb
7
+ :since: 1.0.0
8
+ """
9
+
10
+ from importlib.metadata import version, PackageNotFoundError
11
+
12
+ __author__ = "Ron Webb"
13
+ __since__ = "1.0.0"
14
+
15
+ try:
16
+ __version__ = version("java-dependency-analyzer")
17
+ except PackageNotFoundError:
18
+ __version__ = "unknown"
@@ -81,14 +81,18 @@ class VulnerabilityCache:
81
81
  :author: Ron Webb
82
82
  :since: 1.0.0
83
83
  """
84
- cursor = self._conn.execute(_SELECT_SQL, (source, group_id, artifact_id, version))
84
+ cursor = self._conn.execute(
85
+ _SELECT_SQL, (source, group_id, artifact_id, version)
86
+ )
85
87
  row = cursor.fetchone()
86
88
  if row is None:
87
89
  return None
88
90
  payload, cached_at = row
89
91
  if self._is_expired(cached_at):
90
92
  with self._conn:
91
- self._conn.execute(_DELETE_SQL, (source, group_id, artifact_id, version))
93
+ self._conn.execute(
94
+ _DELETE_SQL, (source, group_id, artifact_id, version)
95
+ )
92
96
  _logger.debug(
93
97
  "Cache entry expired and deleted for %s/%s:%s@%s",
94
98
  source,
@@ -21,7 +21,9 @@ __since__ = "1.0.0"
21
21
  _logger = setup_logger(__name__)
22
22
 
23
23
  # Runtime scopes that contribute to the executable classpath
24
- RUNTIME_SCOPES = frozenset({"compile", "runtime", "implementation", "api", "runtimeOnly"})
24
+ RUNTIME_SCOPES = frozenset(
25
+ {"compile", "runtime", "implementation", "api", "runtimeOnly"}
26
+ )
25
27
 
26
28
 
27
29
  def attach_node(
@@ -138,9 +140,7 @@ class DepTreeParser(DependencyParser):
138
140
  return build_tree_from_lines(lines, self._line_to_entry)
139
141
 
140
142
  @abstractmethod
141
- def _line_to_entry(
142
- self, line: str
143
- ) -> tuple[int, bool, Dependency] | None:
143
+ def _line_to_entry(self, line: str) -> tuple[int, bool, Dependency] | None:
144
144
  """
145
145
  Convert a single dependency-tree text line into a
146
146
  ``(depth, is_leaf, dep)`` tuple, or return *None* to skip the line.
@@ -56,9 +56,7 @@ class GradleDepTreeParser(DepTreeParser):
56
56
  # Private helpers
57
57
  # ------------------------------------------------------------------
58
58
 
59
- def _line_to_entry(
60
- self, line: str
61
- ) -> tuple[int, bool, Dependency] | None:
59
+ def _line_to_entry(self, line: str) -> tuple[int, bool, Dependency] | None:
62
60
  """
63
61
  Convert a single Gradle dep-tree line to a ``(depth, is_leaf, dep)``
64
62
  entry, or return *None* to skip the line.
@@ -139,12 +139,13 @@ class GradleParser(DependencyParser):
139
139
  :author: Ron Webb
140
140
  :since: 1.0.0
141
141
  """
142
+
142
143
  def _replacer(match: re.Match) -> str:
143
144
  return props.get(match.group(1), match.group(0))
144
145
 
145
146
  return re.sub(r"\$\{(\w+)\}", _replacer, content)
146
147
 
147
- def _strip_comments(self, content: str, is_kotlin_dsl: bool) -> str: # pylint: disable=unused-argument
148
+ def _strip_comments(self, content: str, _is_kotlin_dsl: bool) -> str:
148
149
  """
149
150
  Remove single-line (//) and block (/* */) comments from the file content.
150
151
 
@@ -165,7 +166,12 @@ class GradleParser(DependencyParser):
165
166
  :since: 1.0.0
166
167
  """
167
168
  deps: list[Dependency] = []
168
- for pattern in (_GROOVY_SHORTHAND, _KOTLIN_SHORTHAND, _GROOVY_BLOCK, _KOTLIN_BLOCK):
169
+ for pattern in (
170
+ _GROOVY_SHORTHAND,
171
+ _KOTLIN_SHORTHAND,
172
+ _GROOVY_BLOCK,
173
+ _KOTLIN_BLOCK,
174
+ ):
169
175
  for match in pattern.finditer(content):
170
176
  dep = self._match_to_dependency(match)
171
177
  if dep is not None:
@@ -194,10 +200,14 @@ class GradleParser(DependencyParser):
194
200
 
195
201
  # Skip variable references that couldn't be resolved
196
202
  if "$" in version:
197
- _logger.debug("Skipping %s:%s — unresolved version: %s", group, artifact, version)
203
+ _logger.debug(
204
+ "Skipping %s:%s — unresolved version: %s", group, artifact, version
205
+ )
198
206
  return None
199
207
 
200
- _logger.debug("Found dependency: %s:%s:%s (scope=%s)", group, artifact, version, scope)
208
+ _logger.debug(
209
+ "Found dependency: %s:%s:%s (scope=%s)", group, artifact, version, scope
210
+ )
201
211
  return Dependency(
202
212
  group_id=group,
203
213
  artifact_id=artifact,
@@ -52,9 +52,7 @@ class MavenDepTreeParser(DepTreeParser):
52
52
  # Private helpers
53
53
  # ------------------------------------------------------------------
54
54
 
55
- def _line_to_entry(
56
- self, line: str
57
- ) -> tuple[int, bool, Dependency] | None:
55
+ def _line_to_entry(self, line: str) -> tuple[int, bool, Dependency] | None:
58
56
  """
59
57
  Convert a single Maven dep-tree line to a ``(depth, is_leaf, dep)``
60
58
  entry, or return *None* to skip the line.
@@ -43,7 +43,9 @@ class MavenParser(DependencyParser):
43
43
  """
44
44
  _logger.info("Parsing Maven POM: %s", file_path)
45
45
  try:
46
- tree = etree.parse(file_path) # nosec B320 # pylint: disable=c-extension-no-member
46
+ tree = etree.parse(
47
+ file_path
48
+ ) # nosec B320 # pylint: disable=c-extension-no-member
47
49
  except etree.XMLSyntaxError as exc: # pylint: disable=c-extension-no-member
48
50
  _logger.error("Failed to parse POM XML: %s", exc)
49
51
  return []
@@ -88,7 +90,9 @@ class MavenParser(DependencyParser):
88
90
  props_el = root.find(self._tag("properties", namespace))
89
91
  if props_el is not None:
90
92
  for child in props_el:
91
- local = etree.QName(child.tag).localname # pylint: disable=c-extension-no-member
93
+ local = etree.QName(
94
+ child.tag
95
+ ).localname # pylint: disable=c-extension-no-member
92
96
  if child.text:
93
97
  props[local] = child.text.strip()
94
98
 
@@ -153,9 +157,12 @@ class MavenParser(DependencyParser):
153
157
  :author: Ron Webb
154
158
  :since: 1.0.0
155
159
  """
160
+
156
161
  def text(tag: str) -> str:
157
162
  child_el = dep_el.find(self._tag(tag, namespace))
158
- raw = child_el.text.strip() if child_el is not None and child_el.text else ""
163
+ raw = (
164
+ child_el.text.strip() if child_el is not None and child_el.text else ""
165
+ )
159
166
  return self._resolve_value(raw, properties)
160
167
 
161
168
  group_id = text("groupId")
@@ -167,7 +174,9 @@ class MavenParser(DependencyParser):
167
174
  return None
168
175
 
169
176
  if scope not in RUNTIME_SCOPES:
170
- _logger.debug("Skipping dependency %s:%s (scope=%s)", group_id, artifact_id, scope)
177
+ _logger.debug(
178
+ "Skipping dependency %s:%s (scope=%s)", group_id, artifact_id, scope
179
+ )
171
180
  return None
172
181
 
173
182
  if not version:
@@ -71,7 +71,9 @@ class TransitiveResolver:
71
71
  _visited = set()
72
72
 
73
73
  if depth >= _MAX_DEPTH:
74
- _logger.debug("Max depth %d reached at %s", _MAX_DEPTH, dependency.coordinates)
74
+ _logger.debug(
75
+ "Max depth %d reached at %s", _MAX_DEPTH, dependency.coordinates
76
+ )
75
77
  return dependency
76
78
 
77
79
  key = dependency.coordinates
@@ -132,8 +134,7 @@ class TransitiveResolver:
132
134
  :since: 1.0.0
133
135
  """
134
136
  return (
135
- f"{_MAVEN_CENTRAL}/{dep.maven_path}"
136
- f"/{dep.artifact_id}-{dep.version}.pom"
137
+ f"{_MAVEN_CENTRAL}/{dep.maven_path}" f"/{dep.artifact_id}-{dep.version}.pom"
137
138
  )
138
139
 
139
140
  def _fetch_pom(self, url: str) -> bytes | None:
@@ -162,7 +163,9 @@ class TransitiveResolver:
162
163
  :since: 1.0.0
163
164
  """
164
165
  try:
165
- root = etree.fromstring(pom_content) # nosec B320 # pylint: disable=c-extension-no-member
166
+ root = etree.fromstring(
167
+ pom_content
168
+ ) # nosec B320 # pylint: disable=c-extension-no-member
166
169
  except etree.XMLSyntaxError as exc: # pylint: disable=c-extension-no-member
167
170
  _logger.warning("Could not parse POM for %s: %s", parent.coordinates, exc)
168
171
  return []
@@ -171,7 +174,9 @@ class TransitiveResolver:
171
174
  ns_prefix = "m:" if root.tag.startswith("{") else ""
172
175
 
173
176
  def find(node: etree._Element, tag: str) -> etree._Element | None:
174
- return node.find(f"{ns_prefix}{tag}", ns_map) if ns_prefix else node.find(tag)
177
+ return (
178
+ node.find(f"{ns_prefix}{tag}", ns_map) if ns_prefix else node.find(tag)
179
+ )
175
180
 
176
181
  def text(node: etree._Element, tag: str) -> str:
177
182
  child = find(node, tag)
@@ -213,15 +218,15 @@ class TransitiveResolver:
213
218
  """
214
219
  props: dict[str, str] = {}
215
220
  props_el = (
216
- root.find("m:properties", ns_map)
217
- if ns_prefix
218
- else root.find("properties")
221
+ root.find("m:properties", ns_map) if ns_prefix else root.find("properties")
219
222
  )
220
223
  if props_el is not None:
221
224
  for child in props_el:
222
225
  if not isinstance(child.tag, str): # skip comments and PIs
223
226
  continue
224
- local = etree.QName(child.tag).localname # pylint: disable=c-extension-no-member
227
+ local = etree.QName(
228
+ child.tag
229
+ ).localname # pylint: disable=c-extension-no-member
225
230
  if child.text:
226
231
  props[local] = child.text.strip()
227
232
  return props
@@ -83,9 +83,7 @@ class VulnerabilityScanner(ABC):
83
83
  payload,
84
84
  )
85
85
 
86
- def _apply_cache_source(
87
- self, data: str, source: str
88
- ) -> list[Vulnerability]:
86
+ def _apply_cache_source(self, data: str, source: str) -> list[Vulnerability]:
89
87
  """
90
88
  Deserialise *data* (a cached JSON string), parse into ``Vulnerability``
91
89
  objects, and mark each with ``source = "<source>-cache"``.
@@ -97,7 +97,8 @@ class GhsaScanner(VulnerabilityScanner):
97
97
  response = self._client.get(_GHSA_API_URL, params=params)
98
98
  if response.status_code == 429:
99
99
  _logger.warning(
100
- "GitHub Advisory API rate limit exceeded for %s", dependency.coordinates
100
+ "GitHub Advisory API rate limit exceeded for %s",
101
+ dependency.coordinates,
101
102
  )
102
103
  return []
103
104
  response.raise_for_status()
@@ -0,0 +1,28 @@
1
+ [loggers]
2
+ keys=root
3
+
4
+ [handlers]
5
+ keys=consoleHandler,fileHandler
6
+
7
+ [formatters]
8
+ keys=logFormatter,consoleFormatter
9
+
10
+ [logger_root]
11
+ level=INFO
12
+ handlers=consoleHandler,fileHandler
13
+
14
+ [handler_consoleHandler]
15
+ class=StreamHandler
16
+ formatter=consoleFormatter
17
+ args=(sys.stderr,)
18
+
19
+ [handler_fileHandler]
20
+ class=FileHandler
21
+ formatter=logFormatter
22
+ args=('java_dependency_analyzer.log', 'a')
23
+
24
+ [formatter_logFormatter]
25
+ format=%(asctime)s [%(levelname)s] %(name)s - %(message)s
26
+
27
+ [formatter_consoleFormatter]
28
+ format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "java-dependency-analyzer"
3
- version = "1.0.0"
3
+ version = "1.0.1"
4
4
  description = "Java Dependency Analyzer is a tool that inspects dependencies."
5
5
  authors = [
6
6
  {name = "Ron Webb",email = "ron@ronella.xyz"}
@@ -22,6 +22,7 @@ jda = "java_dependency_analyzer.cli:main"
22
22
 
23
23
  [tool.poetry]
24
24
  packages = [{include = "java_dependency_analyzer"}]
25
+ include = ["logging.ini"]
25
26
 
26
27
  [build-system]
27
28
  requires = ["poetry-core>=2.0.0,<3.0.0"]
@@ -1,11 +0,0 @@
1
- """
2
- java_dependency_analyzer package.
3
-
4
- Java Dependency Analyzer is a tool that inspects dependencies.
5
-
6
- :author: Ron Webb
7
- :since: 1.0.0
8
- """
9
-
10
- __author__ = "Ron Webb"
11
- __since__ = "1.0.0"