qgis-plugin-analyzer 1.5.0__py3-none-any.whl → 1.6.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.
- analyzer/cli/__init__.py +14 -0
- analyzer/cli/app.py +147 -0
- analyzer/cli/base.py +93 -0
- analyzer/cli/commands/__init__.py +19 -0
- analyzer/cli/commands/analyze.py +47 -0
- analyzer/cli/commands/fix.py +58 -0
- analyzer/cli/commands/init.py +41 -0
- analyzer/cli/commands/list_rules.py +41 -0
- analyzer/cli/commands/security.py +46 -0
- analyzer/cli/commands/summary.py +52 -0
- analyzer/cli/commands/version.py +41 -0
- analyzer/cli.py +4 -184
- analyzer/commands.py +7 -7
- analyzer/engine.py +421 -238
- analyzer/fixer.py +206 -130
- analyzer/reporters/markdown_reporter.py +48 -15
- analyzer/reporters/summary_reporter.py +193 -80
- analyzer/scanner.py +218 -138
- analyzer/transformers.py +29 -8
- analyzer/utils/__init__.py +2 -0
- analyzer/utils/path_utils.py +53 -1
- analyzer/validators.py +90 -55
- analyzer/visitors/__init__.py +19 -0
- analyzer/visitors/base.py +75 -0
- analyzer/visitors/composite_visitor.py +73 -0
- analyzer/visitors/imports_visitor.py +85 -0
- analyzer/visitors/metrics_visitor.py +158 -0
- analyzer/visitors/security_visitor.py +52 -0
- analyzer/visitors/standards_visitor.py +284 -0
- {qgis_plugin_analyzer-1.5.0.dist-info → qgis_plugin_analyzer-1.6.0.dist-info}/METADATA +16 -7
- qgis_plugin_analyzer-1.6.0.dist-info/RECORD +52 -0
- analyzer/visitors.py +0 -455
- qgis_plugin_analyzer-1.5.0.dist-info/RECORD +0 -35
- {qgis_plugin_analyzer-1.5.0.dist-info → qgis_plugin_analyzer-1.6.0.dist-info}/WHEEL +0 -0
- {qgis_plugin_analyzer-1.5.0.dist-info → qgis_plugin_analyzer-1.6.0.dist-info}/entry_points.txt +0 -0
- {qgis_plugin_analyzer-1.5.0.dist-info → qgis_plugin_analyzer-1.6.0.dist-info}/licenses/LICENSE +0 -0
- {qgis_plugin_analyzer-1.5.0.dist-info → qgis_plugin_analyzer-1.6.0.dist-info}/top_level.txt +0 -0
analyzer/cli.py
CHANGED
|
@@ -19,195 +19,15 @@
|
|
|
19
19
|
# ***************************************************************************/
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
import argparse
|
|
23
|
-
import pathlib
|
|
24
22
|
import sys
|
|
25
23
|
|
|
26
|
-
from . import
|
|
27
|
-
from .commands import (
|
|
28
|
-
handle_analyze,
|
|
29
|
-
handle_fix,
|
|
30
|
-
handle_init,
|
|
31
|
-
handle_list_rules,
|
|
32
|
-
handle_security,
|
|
33
|
-
handle_summary,
|
|
34
|
-
)
|
|
35
|
-
from .utils import logger, setup_logger
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def _setup_argument_parser() -> argparse.ArgumentParser:
|
|
39
|
-
"""Sets up and returns the argument parser with all subcommands.
|
|
40
|
-
|
|
41
|
-
Returns:
|
|
42
|
-
A configured ArgumentParser instance.
|
|
43
|
-
"""
|
|
44
|
-
parser = argparse.ArgumentParser(
|
|
45
|
-
description="QGIS Plugin Analyzer - A guardian for your PyQGIS code"
|
|
46
|
-
)
|
|
47
|
-
parser.add_argument("-v", "--version", action="version", version=f"%(prog)s {__version__}")
|
|
48
|
-
subparsers = parser.add_subparsers(dest="command", help="Command to execute")
|
|
49
|
-
|
|
50
|
-
# Analyze Command
|
|
51
|
-
analyze_parser = subparsers.add_parser("analyze", help="Analyze an existing QGIS plugin")
|
|
52
|
-
analyze_parser.add_argument("project_path", help="Path to the QGIS project to analyze")
|
|
53
|
-
analyze_parser.add_argument(
|
|
54
|
-
"-o",
|
|
55
|
-
"--output",
|
|
56
|
-
help="Output directory for reports",
|
|
57
|
-
default="./analysis_results",
|
|
58
|
-
)
|
|
59
|
-
analyze_parser.add_argument(
|
|
60
|
-
"-r",
|
|
61
|
-
"--report",
|
|
62
|
-
action="store_true",
|
|
63
|
-
help="Generate detailed HTML/Markdown reports",
|
|
64
|
-
)
|
|
65
|
-
analyze_parser.add_argument(
|
|
66
|
-
"-p",
|
|
67
|
-
"--profile",
|
|
68
|
-
help="Configuration profile from pyproject.toml",
|
|
69
|
-
default="default",
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
# Security Command
|
|
73
|
-
security_parser = subparsers.add_parser("security", help="Run a focused security scan")
|
|
74
|
-
security_parser.add_argument("project_path", help="Path to the QGIS project to scan")
|
|
75
|
-
security_parser.add_argument(
|
|
76
|
-
"-o",
|
|
77
|
-
"--output",
|
|
78
|
-
help="Output directory for reports",
|
|
79
|
-
default="./analysis_results",
|
|
80
|
-
)
|
|
81
|
-
security_parser.add_argument(
|
|
82
|
-
"-p",
|
|
83
|
-
"--profile",
|
|
84
|
-
help="Configuration profile from pyproject.toml",
|
|
85
|
-
default="default",
|
|
86
|
-
)
|
|
87
|
-
security_parser.add_argument(
|
|
88
|
-
"--deep",
|
|
89
|
-
action="store_true",
|
|
90
|
-
help="Run more intensive (but slower) security checks",
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
# Fix Command
|
|
94
|
-
fix_parser = subparsers.add_parser("fix", help="Auto-fix common QGIS plugin issues")
|
|
95
|
-
fix_parser.add_argument("path", type=str, help="Path to the QGIS plugin directory")
|
|
96
|
-
fix_parser.add_argument(
|
|
97
|
-
"--dry-run",
|
|
98
|
-
action="store_true",
|
|
99
|
-
default=True,
|
|
100
|
-
help="Show proposed changes without applying (default: True)",
|
|
101
|
-
)
|
|
102
|
-
fix_parser.add_argument("--apply", action="store_true", help="Apply fixes (disables dry-run)")
|
|
103
|
-
fix_parser.add_argument(
|
|
104
|
-
"--auto-approve",
|
|
105
|
-
action="store_true",
|
|
106
|
-
help="Apply all fixes without confirmation",
|
|
107
|
-
)
|
|
108
|
-
fix_parser.add_argument(
|
|
109
|
-
"-p",
|
|
110
|
-
"--profile",
|
|
111
|
-
help="Configuration profile from pyproject.toml",
|
|
112
|
-
default="default",
|
|
113
|
-
)
|
|
114
|
-
fix_parser.add_argument(
|
|
115
|
-
"--rules",
|
|
116
|
-
type=str,
|
|
117
|
-
help="Comma-separated list of rule IDs to fix",
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
# List Rules Command
|
|
121
|
-
subparsers.add_parser("list-rules", help="List all available QGIS audit rules")
|
|
122
|
-
|
|
123
|
-
# Version Command
|
|
124
|
-
subparsers.add_parser("version", help="Show the current version of the analyzer")
|
|
125
|
-
|
|
126
|
-
# Init Command
|
|
127
|
-
subparsers.add_parser("init", help="Initialize a new .analyzerignore with defaults")
|
|
128
|
-
|
|
129
|
-
# Summary Command
|
|
130
|
-
summary_parser = subparsers.add_parser(
|
|
131
|
-
"summary", help="Show a quick terminal summary of analysis results"
|
|
132
|
-
)
|
|
133
|
-
summary_parser.add_argument(
|
|
134
|
-
"-i",
|
|
135
|
-
"--input",
|
|
136
|
-
help="Path to the research JSON file",
|
|
137
|
-
default="analysis_results/project_context.json",
|
|
138
|
-
)
|
|
139
|
-
summary_parser.add_argument(
|
|
140
|
-
"-b",
|
|
141
|
-
"--by",
|
|
142
|
-
choices=["total", "modules", "functions", "classes"],
|
|
143
|
-
default="total",
|
|
144
|
-
help="Granularity of the summary (default: total)",
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
return parser
|
|
24
|
+
from .cli import CLIApp
|
|
148
25
|
|
|
149
26
|
|
|
150
27
|
def main() -> None:
|
|
151
|
-
"""Main entry point for the QGIS Plugin Analyzer CLI.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
sets up the global logging environment.
|
|
155
|
-
"""
|
|
156
|
-
parser = _setup_argument_parser()
|
|
157
|
-
|
|
158
|
-
# Legacy support / default to analyze if no command provided
|
|
159
|
-
if len(sys.argv) > 1 and sys.argv[1] not in [
|
|
160
|
-
"analyze",
|
|
161
|
-
"security",
|
|
162
|
-
"version",
|
|
163
|
-
"fix",
|
|
164
|
-
"list-rules",
|
|
165
|
-
"init",
|
|
166
|
-
"summary",
|
|
167
|
-
"-h",
|
|
168
|
-
"--help",
|
|
169
|
-
]:
|
|
170
|
-
# If the first argument is a path (doesn't start with -), assume 'analyze'
|
|
171
|
-
if not sys.argv[1].startswith("-"):
|
|
172
|
-
sys.argv.insert(1, "analyze")
|
|
173
|
-
|
|
174
|
-
args = parser.parse_args()
|
|
175
|
-
|
|
176
|
-
# Initialize logger (default to analysis_results if not specified)
|
|
177
|
-
output_dir = pathlib.Path(getattr(args, "output", "./analysis_results")).resolve()
|
|
178
|
-
output_dir.mkdir(parents=True, exist_ok=True)
|
|
179
|
-
setup_logger(output_dir)
|
|
180
|
-
|
|
181
|
-
# Command Dispatcher
|
|
182
|
-
dispatch = {
|
|
183
|
-
"fix": lambda: handle_fix(args),
|
|
184
|
-
"analyze": lambda: handle_analyze(args),
|
|
185
|
-
"list-rules": lambda: handle_list_rules(),
|
|
186
|
-
"init": lambda: handle_init(),
|
|
187
|
-
"summary": lambda: handle_summary(args),
|
|
188
|
-
"security": lambda: handle_security(args),
|
|
189
|
-
"version": lambda: print(f"qgis-analyzer {__version__}"),
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
try:
|
|
193
|
-
if args.command in dispatch:
|
|
194
|
-
dispatch[args.command]()
|
|
195
|
-
else:
|
|
196
|
-
parser.print_help()
|
|
197
|
-
|
|
198
|
-
except KeyboardInterrupt:
|
|
199
|
-
logger.info("\n⏹️ Analysis interrupted.")
|
|
200
|
-
sys.exit(1)
|
|
201
|
-
except FileNotFoundError as e:
|
|
202
|
-
logger.error(f"Error: File not found: {e}")
|
|
203
|
-
sys.exit(1)
|
|
204
|
-
except ValueError as e:
|
|
205
|
-
# This handles path traversal or other validation errors
|
|
206
|
-
logger.error(f"Error: {e}")
|
|
207
|
-
sys.exit(1)
|
|
208
|
-
except Exception as e:
|
|
209
|
-
logger.critical(f"Critical Error: {e}", exc_info=True)
|
|
210
|
-
sys.exit(1)
|
|
28
|
+
"""Main entry point for the QGIS Plugin Analyzer CLI."""
|
|
29
|
+
app = CLIApp()
|
|
30
|
+
sys.exit(app.run())
|
|
211
31
|
|
|
212
32
|
|
|
213
33
|
if __name__ == "__main__":
|
analyzer/commands.py
CHANGED
|
@@ -5,6 +5,7 @@ interface definition (cli.py) from execution logic.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import argparse
|
|
8
|
+
import dataclasses
|
|
8
9
|
import json
|
|
9
10
|
import pathlib
|
|
10
11
|
import sys
|
|
@@ -82,11 +83,9 @@ def handle_analyze(args: argparse.Namespace) -> None:
|
|
|
82
83
|
"""
|
|
83
84
|
analyzer = ProjectAnalyzer(args.project_path, args.output, args.profile)
|
|
84
85
|
|
|
85
|
-
# Override config based on CLI flag
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
else:
|
|
89
|
-
analyzer.config["generate_html"] = False
|
|
86
|
+
# Override config based on CLI flag using dataclasses.replace since it's frozen
|
|
87
|
+
report_enabled = bool(hasattr(args, "report") and args.report)
|
|
88
|
+
analyzer.config = dataclasses.replace(analyzer.config, generate_html=report_enabled)
|
|
90
89
|
|
|
91
90
|
success = analyzer.run()
|
|
92
91
|
|
|
@@ -147,9 +146,10 @@ def handle_security(args: argparse.Namespace) -> None:
|
|
|
147
146
|
# Run analyzer with current profile
|
|
148
147
|
analyzer = ProjectAnalyzer(str(project_path), args.output, args.profile)
|
|
149
148
|
|
|
150
|
-
# We could potentially add
|
|
149
|
+
# We could potentially add high-level flags here
|
|
151
150
|
if args.deep:
|
|
152
|
-
|
|
151
|
+
# Note: ProjectConfig currently doesn't have security_deep_scan,
|
|
152
|
+
# but if it did, we would use dataclasses.replace here too.
|
|
153
153
|
print("🔍 Deep scan enabled (Entropy analysis and full secret detection)")
|
|
154
154
|
|
|
155
155
|
success = analyzer.run()
|