lintro 0.13.1__py3-none-any.whl → 0.13.3__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.
Potentially problematic release.
This version of lintro might be problematic. Click here for more details.
- lintro/__init__.py +1 -1
- lintro/parsers/__init__.py +60 -9
- lintro/parsers/bandit/__init__.py +6 -0
- lintro/parsers/bandit/bandit_issue.py +49 -0
- lintro/parsers/bandit/bandit_parser.py +99 -0
- {lintro-0.13.1.dist-info → lintro-0.13.3.dist-info}/METADATA +5 -3
- {lintro-0.13.1.dist-info → lintro-0.13.3.dist-info}/RECORD +11 -8
- {lintro-0.13.1.dist-info → lintro-0.13.3.dist-info}/WHEEL +0 -0
- {lintro-0.13.1.dist-info → lintro-0.13.3.dist-info}/entry_points.txt +0 -0
- {lintro-0.13.1.dist-info → lintro-0.13.3.dist-info}/licenses/LICENSE +0 -0
- {lintro-0.13.1.dist-info → lintro-0.13.3.dist-info}/top_level.txt +0 -0
lintro/__init__.py
CHANGED
lintro/parsers/__init__.py
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
"""Parser modules for Lintro tools."""
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from importlib import import_module
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
# Type checking imports
|
|
10
|
+
from lintro.parsers import (
|
|
11
|
+
actionlint,
|
|
12
|
+
bandit,
|
|
13
|
+
darglint,
|
|
14
|
+
hadolint,
|
|
15
|
+
prettier,
|
|
16
|
+
ruff,
|
|
17
|
+
yamllint,
|
|
18
|
+
)
|
|
12
19
|
|
|
13
20
|
__all__ = [
|
|
14
21
|
"actionlint",
|
|
@@ -19,3 +26,47 @@ __all__ = [
|
|
|
19
26
|
"ruff",
|
|
20
27
|
"yamllint",
|
|
21
28
|
]
|
|
29
|
+
|
|
30
|
+
# Lazy-load parser submodules to avoid circular imports
|
|
31
|
+
_SUBMODULES = {
|
|
32
|
+
"actionlint",
|
|
33
|
+
"bandit",
|
|
34
|
+
"darglint",
|
|
35
|
+
"hadolint",
|
|
36
|
+
"prettier",
|
|
37
|
+
"ruff",
|
|
38
|
+
"yamllint",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def __getattr__(name: str) -> object:
|
|
43
|
+
"""Lazy-load parser submodules to avoid circular import issues.
|
|
44
|
+
|
|
45
|
+
This function is called when an attribute is accessed that doesn't exist
|
|
46
|
+
in the module. It allows accessing parser submodules without eagerly
|
|
47
|
+
importing them all at package initialization time.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
name: The name of the attribute being accessed.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
The imported submodule.
|
|
54
|
+
|
|
55
|
+
Raises:
|
|
56
|
+
AttributeError: If the requested name is not a known submodule.
|
|
57
|
+
"""
|
|
58
|
+
if name in _SUBMODULES:
|
|
59
|
+
module = import_module(f".{name}", __package__)
|
|
60
|
+
# Cache the module in this module's namespace for future access
|
|
61
|
+
globals()[name] = module
|
|
62
|
+
return module
|
|
63
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def __dir__() -> list[str]:
|
|
67
|
+
"""Return list of available attributes for this module.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
List of submodule names and other module attributes.
|
|
71
|
+
"""
|
|
72
|
+
return list(__all__)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Bandit issue model for security vulnerabilities."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class BanditIssue:
|
|
9
|
+
"""Represents a security issue found by Bandit.
|
|
10
|
+
|
|
11
|
+
Attributes:
|
|
12
|
+
file: str: Path to the file containing the issue.
|
|
13
|
+
line: int: Line number where the issue was found.
|
|
14
|
+
col_offset: int: Column offset of the issue.
|
|
15
|
+
issue_severity: str: Severity level (LOW, MEDIUM, HIGH).
|
|
16
|
+
issue_confidence: str: Confidence level (LOW, MEDIUM, HIGH).
|
|
17
|
+
test_id: str: Bandit test ID (e.g., B602, B301).
|
|
18
|
+
test_name: str: Name of the test that found the issue.
|
|
19
|
+
issue_text: str: Description of the security issue.
|
|
20
|
+
more_info: str: URL with more information about the issue.
|
|
21
|
+
cwe: dict[str, Any] | None: CWE (Common Weakness Enumeration) information.
|
|
22
|
+
code: str: Code snippet containing the issue.
|
|
23
|
+
line_range: list[int]: Range of lines containing the issue.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
file: str
|
|
27
|
+
line: int
|
|
28
|
+
col_offset: int
|
|
29
|
+
issue_severity: str
|
|
30
|
+
issue_confidence: str
|
|
31
|
+
test_id: str
|
|
32
|
+
test_name: str
|
|
33
|
+
issue_text: str
|
|
34
|
+
more_info: str
|
|
35
|
+
cwe: dict[str, Any] | None = None
|
|
36
|
+
code: str | None = None
|
|
37
|
+
line_range: list[int] | None = None
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def message(self) -> str:
|
|
41
|
+
"""Get a human-readable message for the issue.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
str: Formatted issue message.
|
|
45
|
+
"""
|
|
46
|
+
return (
|
|
47
|
+
f"[{self.test_id}:{self.test_name}] {self.issue_severity} severity, "
|
|
48
|
+
f"{self.issue_confidence} confidence: {self.issue_text}"
|
|
49
|
+
)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""Bandit output parser for security issues."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from loguru import logger
|
|
6
|
+
|
|
7
|
+
from lintro.parsers.bandit.bandit_issue import BanditIssue
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def parse_bandit_output(bandit_data: dict[str, Any]) -> list[BanditIssue]:
|
|
11
|
+
"""Parse Bandit JSON output into BanditIssue objects.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
bandit_data: dict[str, Any]: JSON data from Bandit output.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
list[BanditIssue]: List of parsed security issues.
|
|
18
|
+
|
|
19
|
+
Raises:
|
|
20
|
+
ValueError: If the bandit data structure is invalid.
|
|
21
|
+
"""
|
|
22
|
+
if not isinstance(bandit_data, dict):
|
|
23
|
+
raise ValueError("Bandit data must be a dictionary")
|
|
24
|
+
|
|
25
|
+
results = bandit_data.get("results", [])
|
|
26
|
+
if not isinstance(results, list):
|
|
27
|
+
raise ValueError("Bandit results must be a list")
|
|
28
|
+
|
|
29
|
+
issues: list[BanditIssue] = []
|
|
30
|
+
|
|
31
|
+
for result in results:
|
|
32
|
+
if not isinstance(result, dict):
|
|
33
|
+
continue
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
filename = result.get("filename", "")
|
|
37
|
+
line_number = result.get("line_number", 0)
|
|
38
|
+
col_offset = result.get("col_offset", 0)
|
|
39
|
+
issue_severity = result.get("issue_severity", "UNKNOWN")
|
|
40
|
+
issue_confidence = result.get("issue_confidence", "UNKNOWN")
|
|
41
|
+
test_id = result.get("test_id", "")
|
|
42
|
+
test_name = result.get("test_name", "")
|
|
43
|
+
issue_text = result.get("issue_text", "")
|
|
44
|
+
more_info = result.get("more_info", "")
|
|
45
|
+
cwe = result.get("issue_cwe")
|
|
46
|
+
code = result.get("code")
|
|
47
|
+
line_range = result.get("line_range")
|
|
48
|
+
|
|
49
|
+
# Validate critical fields; skip malformed entries
|
|
50
|
+
if not isinstance(filename, str):
|
|
51
|
+
logger.warning("Skipping issue with non-string filename")
|
|
52
|
+
continue
|
|
53
|
+
if not isinstance(line_number, int):
|
|
54
|
+
logger.warning("Skipping issue with non-integer line_number")
|
|
55
|
+
continue
|
|
56
|
+
if not isinstance(col_offset, int):
|
|
57
|
+
col_offset = 0
|
|
58
|
+
|
|
59
|
+
sev = (
|
|
60
|
+
str(issue_severity).upper() if issue_severity is not None else "UNKNOWN"
|
|
61
|
+
)
|
|
62
|
+
conf = (
|
|
63
|
+
str(issue_confidence).upper()
|
|
64
|
+
if issue_confidence is not None
|
|
65
|
+
else "UNKNOWN"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
test_id = test_id if isinstance(test_id, str) else ""
|
|
69
|
+
test_name = test_name if isinstance(test_name, str) else ""
|
|
70
|
+
issue_text = issue_text if isinstance(issue_text, str) else ""
|
|
71
|
+
more_info = more_info if isinstance(more_info, str) else ""
|
|
72
|
+
|
|
73
|
+
# Normalize line_range to list[int] when provided
|
|
74
|
+
if isinstance(line_range, list):
|
|
75
|
+
line_range = [x for x in line_range if isinstance(x, int)] or None
|
|
76
|
+
else:
|
|
77
|
+
line_range = None
|
|
78
|
+
|
|
79
|
+
issue = BanditIssue(
|
|
80
|
+
file=filename,
|
|
81
|
+
line=line_number,
|
|
82
|
+
col_offset=col_offset,
|
|
83
|
+
issue_severity=sev,
|
|
84
|
+
issue_confidence=conf,
|
|
85
|
+
test_id=test_id,
|
|
86
|
+
test_name=test_name,
|
|
87
|
+
issue_text=issue_text,
|
|
88
|
+
more_info=more_info,
|
|
89
|
+
cwe=cwe if isinstance(cwe, dict) else None,
|
|
90
|
+
code=code if isinstance(code, str) else None,
|
|
91
|
+
line_range=line_range,
|
|
92
|
+
)
|
|
93
|
+
issues.append(issue)
|
|
94
|
+
except (KeyError, TypeError, ValueError) as e:
|
|
95
|
+
# Log warning but continue processing other issues
|
|
96
|
+
logger.warning(f"Failed to parse bandit issue: {e}")
|
|
97
|
+
continue
|
|
98
|
+
|
|
99
|
+
return issues
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lintro
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.3
|
|
4
4
|
Summary: A unified CLI tool for code formatting, linting, and quality assurance
|
|
5
5
|
Author-email: TurboCoder13 <turbocoder13@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -36,7 +36,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
36
36
|
Classifier: Topic :: Software Development :: Quality Assurance
|
|
37
37
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
38
38
|
Classifier: Topic :: Utilities
|
|
39
|
-
Requires-Python:
|
|
39
|
+
Requires-Python: >=3.13
|
|
40
40
|
Description-Content-Type: text/markdown
|
|
41
41
|
License-File: LICENSE
|
|
42
42
|
Requires-Dist: click==8.1.8
|
|
@@ -169,8 +169,10 @@ grype sbom:main-*-py-lintro-sbom.spdx-2.3.json
|
|
|
169
169
|
|
|
170
170
|
#### From PyPI (Recommended)
|
|
171
171
|
|
|
172
|
+
⚠️ **Important**: Versions prior to 0.13.2 contain a circular import bug that prevents installation as a dependency. Please use version 0.13.2 or later.
|
|
173
|
+
|
|
172
174
|
```bash
|
|
173
|
-
pip install lintro
|
|
175
|
+
pip install lintro>=0.13.2
|
|
174
176
|
```
|
|
175
177
|
|
|
176
178
|
#### Development Installation
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
lintro/__init__.py,sha256=
|
|
1
|
+
lintro/__init__.py,sha256=ZVCneiMLQUPnjOrRwodLP-_g_80H9vl0J2ZVOe26gyU,111
|
|
2
2
|
lintro/__main__.py,sha256=McxM6wEcEeCLBHZs0F8xzT1PxVqJYkjtaPq1_-Nug-0,106
|
|
3
3
|
lintro/cli.py,sha256=0apxBcqVxBZdCeXGMyaSdJYSntMIiCFwPwan1jUmPzM,2520
|
|
4
4
|
lintro/ascii-art/fail.txt,sha256=gshKngSrz5FvJdP6g7lPygpLsLsZZh4etBU_wR7PXOw,56396
|
|
@@ -44,10 +44,13 @@ lintro/models/core/__init__.py,sha256=mevoEG547VTotUsgyocEB72OknnGhExNWcfel7ZPLn
|
|
|
44
44
|
lintro/models/core/tool.py,sha256=TWkVV_KOYziqCdq_f3Goft3PKWKByitsQ2VFiX6MVHM,2594
|
|
45
45
|
lintro/models/core/tool_config.py,sha256=FCfKQ_bz2yBg_FJ5xEud7TdF0PZ3fPp1Yjty02rbHR8,1004
|
|
46
46
|
lintro/models/core/tool_result.py,sha256=z5fexuCBt1ir0hvzojxVX-Abt6MNIT7_JvH5oyW_FlI,1319
|
|
47
|
-
lintro/parsers/__init__.py,sha256=
|
|
47
|
+
lintro/parsers/__init__.py,sha256=Xb2NegNLAm10mlY8BJ6lOIAtVtoCmQfY5uA6mljEs-M,1663
|
|
48
48
|
lintro/parsers/actionlint/__init__.py,sha256=N953d0kbJEOZE9cG4eq9BjiSErdk10GMrngV_tKjLvo,33
|
|
49
49
|
lintro/parsers/actionlint/actionlint_issue.py,sha256=H_BQr04KAGHpVp1qKqETcWLi3i-u6atl7ccDae-jqX8,636
|
|
50
50
|
lintro/parsers/actionlint/actionlint_parser.py,sha256=tvpPOi1dQrnpf7V2-3LMANH_UfDdecI4_AwDQpZOyU0,1898
|
|
51
|
+
lintro/parsers/bandit/__init__.py,sha256=LMXIVOoW1wnS0mXbiVY-htdkUvb1clRSfSn7X3L2SSw,206
|
|
52
|
+
lintro/parsers/bandit/bandit_issue.py,sha256=zdC6dLFmks_w9JjDWQ00BPSpCUUkilElby-Ut0PVLtE,1607
|
|
53
|
+
lintro/parsers/bandit/bandit_parser.py,sha256=d4KRH_U_4Exv-Fxq63yyx7F484yorKr2p8caDPCyKHs,3591
|
|
51
54
|
lintro/parsers/black/black_issue.py,sha256=RwrT7n8aw4Nybakv83eXoeUxZlDtHwicWKNfrHYIYOg,507
|
|
52
55
|
lintro/parsers/black/black_parser.py,sha256=mCbEy4aTRtxC_SKKPNK6hThGEu8C0m9EnliFW__Sa3Y,2958
|
|
53
56
|
lintro/parsers/darglint/__init__.py,sha256=r2ilRjf8WdHOvXIGsg4SmtYkRUR76DowWLA_n9sIOV0,55
|
|
@@ -88,9 +91,9 @@ lintro/utils/output_manager.py,sha256=oIE0g8LNVQcWSQOb1MbDVqGYPrOjBGuUZ4R80M1Rev
|
|
|
88
91
|
lintro/utils/path_utils.py,sha256=NJP3vjVDcRBgUHtYNrpL0tRa0Sc3oQhGX3_2WWzdZE4,1395
|
|
89
92
|
lintro/utils/tool_executor.py,sha256=PNUuWkQj4TWI9UHVvZUpWoDkU00MAd1o5_VhG-iviCo,26124
|
|
90
93
|
lintro/utils/tool_utils.py,sha256=T3A5xf4zokUMPVzWzEn3f0M14EcDxoeRo-jf5yXEoyI,15613
|
|
91
|
-
lintro-0.13.
|
|
92
|
-
lintro-0.13.
|
|
93
|
-
lintro-0.13.
|
|
94
|
-
lintro-0.13.
|
|
95
|
-
lintro-0.13.
|
|
96
|
-
lintro-0.13.
|
|
94
|
+
lintro-0.13.3.dist-info/licenses/LICENSE,sha256=CwaAnyD2psonDBBJjbqFUz00W8nQw-FGDlEGZReUV6A,1069
|
|
95
|
+
lintro-0.13.3.dist-info/METADATA,sha256=I4ZziqInQAn3pXWgGXNa4gShtnJZjhy7edUDyiH5eSo,16194
|
|
96
|
+
lintro-0.13.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
97
|
+
lintro-0.13.3.dist-info/entry_points.txt,sha256=SYSk35jFyNLEHyrofSJsRv4qFN9NsT4VWSbvnTS9ov0,43
|
|
98
|
+
lintro-0.13.3.dist-info/top_level.txt,sha256=_D-7eyV6gNBOoIwHuf_h60wN_RWiw8GxB430Il9VKhU,7
|
|
99
|
+
lintro-0.13.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|