qgis-plugin-analyzer 1.4.0__tar.gz → 1.6.0__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 (70) hide show
  1. {qgis_plugin_analyzer-1.4.0/src/qgis_plugin_analyzer.egg-info → qgis_plugin_analyzer-1.6.0}/PKG-INFO +32 -10
  2. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/README.md +31 -9
  3. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/pyproject.toml +7 -2
  4. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/__init__.py +2 -1
  5. qgis_plugin_analyzer-1.6.0/src/analyzer/cli/__init__.py +14 -0
  6. qgis_plugin_analyzer-1.6.0/src/analyzer/cli/app.py +147 -0
  7. qgis_plugin_analyzer-1.6.0/src/analyzer/cli/base.py +93 -0
  8. qgis_plugin_analyzer-1.6.0/src/analyzer/cli/commands/__init__.py +19 -0
  9. qgis_plugin_analyzer-1.6.0/src/analyzer/cli/commands/analyze.py +47 -0
  10. qgis_plugin_analyzer-1.6.0/src/analyzer/cli/commands/fix.py +58 -0
  11. qgis_plugin_analyzer-1.6.0/src/analyzer/cli/commands/init.py +41 -0
  12. qgis_plugin_analyzer-1.6.0/src/analyzer/cli/commands/list_rules.py +41 -0
  13. qgis_plugin_analyzer-1.6.0/src/analyzer/cli/commands/security.py +46 -0
  14. qgis_plugin_analyzer-1.6.0/src/analyzer/cli/commands/summary.py +52 -0
  15. qgis_plugin_analyzer-1.6.0/src/analyzer/cli/commands/version.py +41 -0
  16. qgis_plugin_analyzer-1.6.0/src/analyzer/cli.py +34 -0
  17. qgis_plugin_analyzer-1.6.0/src/analyzer/commands.py +163 -0
  18. qgis_plugin_analyzer-1.6.0/src/analyzer/engine.py +832 -0
  19. qgis_plugin_analyzer-1.6.0/src/analyzer/fixer.py +390 -0
  20. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/reporters/markdown_reporter.py +88 -14
  21. qgis_plugin_analyzer-1.6.0/src/analyzer/reporters/summary_reporter.py +399 -0
  22. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/rules/qgis_rules.py +3 -1
  23. qgis_plugin_analyzer-1.6.0/src/analyzer/scanner.py +302 -0
  24. qgis_plugin_analyzer-1.6.0/src/analyzer/secrets.py +84 -0
  25. qgis_plugin_analyzer-1.6.0/src/analyzer/security_checker.py +85 -0
  26. qgis_plugin_analyzer-1.6.0/src/analyzer/security_rules.py +127 -0
  27. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/transformers.py +29 -8
  28. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/utils/__init__.py +2 -0
  29. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/utils/path_utils.py +53 -1
  30. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/validators.py +90 -55
  31. qgis_plugin_analyzer-1.6.0/src/analyzer/visitors/__init__.py +19 -0
  32. qgis_plugin_analyzer-1.6.0/src/analyzer/visitors/base.py +75 -0
  33. qgis_plugin_analyzer-1.6.0/src/analyzer/visitors/composite_visitor.py +73 -0
  34. qgis_plugin_analyzer-1.6.0/src/analyzer/visitors/imports_visitor.py +85 -0
  35. qgis_plugin_analyzer-1.6.0/src/analyzer/visitors/metrics_visitor.py +158 -0
  36. qgis_plugin_analyzer-1.6.0/src/analyzer/visitors/security_visitor.py +52 -0
  37. qgis_plugin_analyzer-1.6.0/src/analyzer/visitors/standards_visitor.py +284 -0
  38. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0/src/qgis_plugin_analyzer.egg-info}/PKG-INFO +32 -10
  39. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/qgis_plugin_analyzer.egg-info/SOURCES.txt +22 -0
  40. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/tests/test_scanner.py +1 -0
  41. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/tests/test_validators.py +41 -0
  42. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/tests/test_vulnerability.py +1 -0
  43. qgis_plugin_analyzer-1.4.0/src/analyzer/cli.py +0 -311
  44. qgis_plugin_analyzer-1.4.0/src/analyzer/engine.py +0 -586
  45. qgis_plugin_analyzer-1.4.0/src/analyzer/fixer.py +0 -314
  46. qgis_plugin_analyzer-1.4.0/src/analyzer/reporters/summary_reporter.py +0 -222
  47. qgis_plugin_analyzer-1.4.0/src/analyzer/scanner.py +0 -794
  48. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/LICENSE +0 -0
  49. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/setup.cfg +0 -0
  50. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/__init__.py +0 -0
  51. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/models/__init__.py +0 -0
  52. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/models/analysis_models.py +0 -0
  53. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/reporters/__init__.py +0 -0
  54. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/reporters/html_reporter.py +0 -0
  55. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/rules/__init__.py +0 -0
  56. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/rules/modernization_rules.py +0 -0
  57. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/semantic.py +0 -0
  58. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/utils/ast_utils.py +0 -0
  59. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/utils/config_utils.py +0 -0
  60. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/utils/logging_utils.py +0 -0
  61. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/analyzer/utils/performance_utils.py +0 -0
  62. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/qgis_plugin_analyzer.egg-info/dependency_links.txt +0 -0
  63. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/qgis_plugin_analyzer.egg-info/entry_points.txt +0 -0
  64. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/src/qgis_plugin_analyzer.egg-info/top_level.txt +0 -0
  65. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/tests/test_analyzer.py +0 -0
  66. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/tests/test_fixer.py +0 -0
  67. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/tests/test_high_complexity.py +0 -0
  68. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/tests/test_safety.py +0 -0
  69. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/tests/test_security.py +0 -0
  70. {qgis_plugin_analyzer-1.4.0 → qgis_plugin_analyzer-1.6.0}/tests/test_semantic.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qgis-plugin-analyzer
3
- Version: 1.4.0
3
+ Version: 1.6.0
4
4
  Summary: A professional static analysis tool for QGIS (PyQGIS) plugins
5
5
  Author-email: geociencio <juanbernales@gmail.com>
6
6
  License: GPL-3.0-or-later
@@ -34,13 +34,16 @@ Dynamic: license-file
34
34
  ![License](https://img.shields.io/badge/License-GPLv3-blue.svg)
35
35
  ![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)
36
36
  ![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg?logo=git)
37
- ![Quality Score](https://img.shields.io/badge/Module%20Stability-55.6%2F100-yellow)
38
- ![Maintainability](https://img.shields.io/badge/Maintainability-100.0%2F100-brightgreen)
37
+ ![Quality Score](https://img.shields.io/badge/Module%20Stability-92.3%2F100-brightgreen)
38
+ ![Maintainability](https://img.shields.io/badge/Maintainability-84.1%2F100-green)
39
+ ![Security Score](https://img.shields.io/badge/Security--Bandit-98.7%2F100-brightgreen)
39
40
 
40
41
  The **QGIS Plugin Analyzer** is a static analysis tool designed specifically for QGIS (PyQGIS) plugin developers. Its goal is to elevate plugin quality by ensuring they follow community best practices and are optimized for AI-assisted development.
41
42
 
42
43
  ## ✨ Main Features
43
44
 
45
+ - **Security Core (Bandit-inspired)**: Professional vulnerability scanning detecting `eval`, `exec`, shell injections, and SQL injection risks.
46
+ - **Deep Entropy Secret Scanner**: Detects hardcoded API keys, passwords, and sensitive tokens using regex and information entropy.
44
47
  - **High-Performance Engine**: Parallel analysis powered by `ProcessPoolExecutor` for ultra-fast execution on multi-core systems.
45
48
  - **Project Auto-Detection**: Intelligently distinguishes between official QGIS Plugins and Generic Python Projects, tailoring validation logic accordingly.
46
49
  - **Advanced Ignore Engine**: Robust `.analyzerignore` support with non-anchored patterns and smart default excludes (`.venv`, `build`, etc.).
@@ -59,21 +62,21 @@ The **QGIS Plugin Analyzer** is a static analysis tool designed specifically for
59
62
 
60
63
  | Feature | **QGIS Plugin Analyzer** | flake8-qgis | Ruff (Standard) | Official Repo Bot |
61
64
  | :--- | :---: | :---: | :---: | :---: |
65
+ | **Run Locally / Offline**| ✅ (Your Machine) | ✅ | ✅ | ❌ (Upload Only) |
62
66
  | **Static Linting** | ✅ (Ruff + Custom) | ✅ (flake8) | ✅ (General) | ✅ (Limited) |
63
67
  | **QGIS-Specific Rules**| ✅ (Precise AST) | ✅ (Regex/AST) | ❌ | ✅ |
64
68
  | **Interactive Auto-Fix**| ✅ | ❌ | ❌ | ❌ |
65
69
  | **Semantic Analysis** | ✅ | ❌ | ❌ | ❌ |
66
- | **Compliance Checks** | ✅ | ❌ | ❌ | ✅ |
67
- | **i18n / API Audit** | ✅ | ❌ | ❌ | ✅ |
68
- | **Architecture Audit** | ✅ (UI/Core) | ❌ | ❌ | ❌ |
70
+ | **Security Audit** | ✅ (Bandit-style) | ❌ | ❌ | ✅ (Server-side) |
71
+ | **Secret Scanning** | ✅ (Entropy) | ❌ | ❌ | ✅ (Server-side) |
69
72
  | **HTML/MD Reports** | ✅ | ❌ | ❌ | ❌ |
70
73
  | **AI Context Gen** | ✅ (Project Brain) | ❌ | ❌ | ❌ |
71
74
 
72
75
  ### Key Differentiators
73
76
 
74
- 1. **High-Performance Hybrid Engine**: Combines multi-core AST processing with deep understanding of cross-file relationships and Qt-specific patterns.
75
- 2. **Safety-First Auto-Fixing**: AST-based transformations with Git status verification and interactive diff previews.
76
- 3. **Repository Compliance**: Local pre-checks to ensure your plugin passes the Official QGIS Repository policies.
77
+ 1. **Shift Left (Run Locally)**: The biggest advantage is being able to run the **same high-standard checks** as the Official Repository *before* you upload your plugin. No more "reject-fix-upload" loops.
78
+ 2. **High-Performance Hybrid Engine**: Combines multi-core AST processing with deep understanding of cross-file relationships and Qt-specific patterns.
79
+ 3. **Safety-First Auto-Fixing**: AST-based transformations with Git status verification and interactive diff previews.
77
80
  4. **Zero Runtime Stack**: Minimal footprint, ultra-fast execution, and easy CI integration.
78
81
  5. **AI-Centric Design**: Built to help developers and AI agents understand complex QGIS plugins instantly.
79
82
 
@@ -129,7 +132,7 @@ You can run `qgis-plugin-analyzer` automatically before every commit to ensure q
129
132
 
130
133
  ```yaml
131
134
  - repo: https://github.com/geociencio/qgis-plugin-analyzer
132
- rev: v1.4.0 # Use the latest tag
135
+ rev: main # Use 'main' for latest features or a specific tag like v1.5.0
133
136
  hooks:
134
137
  - id: qgis-plugin-analyzer
135
138
  ```
@@ -188,6 +191,7 @@ Audits an existing QGIS plugin repository.
188
191
  | :--- | :--- | :--- |
189
192
  | `project_path` | **(Required)** Path to the plugin directory to analyze. | N/A |
190
193
  | `-o`, `--output` | Directory where HTML/Markdown reports will be saved. | `./analysis_results` |
194
+ | `-r`, `--report` | Explicitly generate detailed HTML/Markdown reports. | `False` |
191
195
  | `-p`, `--profile`| Configuration profile from `pyproject.toml` (`default`, `release`). | `default` |
192
196
 
193
197
  ### `qgis-analyzer fix`
@@ -210,6 +214,18 @@ Shows a professional, color-coded summary of findings directly in your terminal.
210
214
  | `-b`, `--by` | Granularity of the summary: `total`, `modules`, `functions`, `classes`. | `total` |
211
215
  | `-i`, `--input` | Path to the `project_context.json` file to summarize. | `analysis_results/project_context.json` |
212
216
 
217
+ ### `qgis-analyzer security`
218
+ Performs a focused security scan on a file or directory.
219
+
220
+ | Argument | Description | Default |
221
+ | :--- | :--- | :--- |
222
+ | `path` | **(Required)** Path to the file or directory to scan. | N/A |
223
+ | `--deep` | Run more intensive (but slower) security checks. | `False` |
224
+ | `-p`, `--profile`| Configuration profile. | `default` |
225
+
226
+ ### `qgis-analyzer version`
227
+ Shows the current version of the analyzer.
228
+
213
229
  **Example:**
214
230
  ```bash
215
231
  # Executive summary
@@ -245,6 +261,7 @@ The development of this analyzer is based on official QGIS community guidelines,
245
261
  - **[QGIS Plugin Repository Requirements](https://plugins.qgis.org/publish/)**: Mandatory criteria for plugin approval in the official repository.
246
262
  - **[QGIS Coding Standards](https://docs.qgis.org/latest/en/docs/developer_guide/codingstandards.html)**: Core style and organization guidelines for the QGIS project.
247
263
  - **[QGIS HIG (Human Interface Guidelines)](https://docs.qgis.org/latest/en/docs/developer_guide/hig.html)**: Standards for consistent and accessible user interface design.
264
+ - **[QGIS Security Scanning Documentation](https://plugins.qgis.org/docs/security-scanning)**: Official guide on automated security analysis (Bandit, detect-secrets) for plugins.
248
265
 
249
266
  ### Industry & Community Standards
250
267
  - **[flake8-qgis Rules](https://github.com/qgis/flake8-qgis)**: Community-driven linting rules for PyQGIS (QGS101-106).
@@ -254,6 +271,11 @@ The development of this analyzer is based on official QGIS community guidelines,
254
271
  - **[Conventional Commits](https://www.conventionalcommits.org/)**: Standard for clear, machine-readable commit history.
255
272
  - **[Keep a Changelog](https://keepachangelog.com/)**: Best practices for maintainable version history.
256
273
 
274
+ ### Security Standards
275
+ - **[Bandit (PyCQA)](https://bandit.readthedocs.io/)**: The security rules implemented (B1xx - B6xx) are directly derived from the Bandit project's rule set for identifying common security issues in Python code.
276
+ - **[CWE (Common Weakness Enumeration)](https://cwe.mitre.org/)**: Security findings are mapped to standard CWE IDs (e.g., CWE-78 Command Injection, CWE-89 SQL Injection) for industry-standard classification.
277
+ - **[OWASP Top 10](https://owasp.org/www-project-top-ten/)**: The "Hardcoded Secret" and "Injection" checks align with critical OWASP vulnerabilities.
278
+
257
279
  ### Internal Resources
258
280
  - **[Detailed Rules Catalog](RULES.md)**: Full documentation of all audit rules implemented in this analyzer.
259
281
  - **[Standardized Scoring Metrics](docs/SCORING_STANDARDS.md)**: Mathematical logic and thresholds for project evaluation.
@@ -6,13 +6,16 @@
6
6
  ![License](https://img.shields.io/badge/License-GPLv3-blue.svg)
7
7
  ![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)
8
8
  ![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg?logo=git)
9
- ![Quality Score](https://img.shields.io/badge/Module%20Stability-55.6%2F100-yellow)
10
- ![Maintainability](https://img.shields.io/badge/Maintainability-100.0%2F100-brightgreen)
9
+ ![Quality Score](https://img.shields.io/badge/Module%20Stability-92.3%2F100-brightgreen)
10
+ ![Maintainability](https://img.shields.io/badge/Maintainability-84.1%2F100-green)
11
+ ![Security Score](https://img.shields.io/badge/Security--Bandit-98.7%2F100-brightgreen)
11
12
 
12
13
  The **QGIS Plugin Analyzer** is a static analysis tool designed specifically for QGIS (PyQGIS) plugin developers. Its goal is to elevate plugin quality by ensuring they follow community best practices and are optimized for AI-assisted development.
13
14
 
14
15
  ## ✨ Main Features
15
16
 
17
+ - **Security Core (Bandit-inspired)**: Professional vulnerability scanning detecting `eval`, `exec`, shell injections, and SQL injection risks.
18
+ - **Deep Entropy Secret Scanner**: Detects hardcoded API keys, passwords, and sensitive tokens using regex and information entropy.
16
19
  - **High-Performance Engine**: Parallel analysis powered by `ProcessPoolExecutor` for ultra-fast execution on multi-core systems.
17
20
  - **Project Auto-Detection**: Intelligently distinguishes between official QGIS Plugins and Generic Python Projects, tailoring validation logic accordingly.
18
21
  - **Advanced Ignore Engine**: Robust `.analyzerignore` support with non-anchored patterns and smart default excludes (`.venv`, `build`, etc.).
@@ -31,21 +34,21 @@ The **QGIS Plugin Analyzer** is a static analysis tool designed specifically for
31
34
 
32
35
  | Feature | **QGIS Plugin Analyzer** | flake8-qgis | Ruff (Standard) | Official Repo Bot |
33
36
  | :--- | :---: | :---: | :---: | :---: |
37
+ | **Run Locally / Offline**| ✅ (Your Machine) | ✅ | ✅ | ❌ (Upload Only) |
34
38
  | **Static Linting** | ✅ (Ruff + Custom) | ✅ (flake8) | ✅ (General) | ✅ (Limited) |
35
39
  | **QGIS-Specific Rules**| ✅ (Precise AST) | ✅ (Regex/AST) | ❌ | ✅ |
36
40
  | **Interactive Auto-Fix**| ✅ | ❌ | ❌ | ❌ |
37
41
  | **Semantic Analysis** | ✅ | ❌ | ❌ | ❌ |
38
- | **Compliance Checks** | ✅ | ❌ | ❌ | ✅ |
39
- | **i18n / API Audit** | ✅ | ❌ | ❌ | ✅ |
40
- | **Architecture Audit** | ✅ (UI/Core) | ❌ | ❌ | ❌ |
42
+ | **Security Audit** | ✅ (Bandit-style) | ❌ | ❌ | ✅ (Server-side) |
43
+ | **Secret Scanning** | ✅ (Entropy) | ❌ | ❌ | ✅ (Server-side) |
41
44
  | **HTML/MD Reports** | ✅ | ❌ | ❌ | ❌ |
42
45
  | **AI Context Gen** | ✅ (Project Brain) | ❌ | ❌ | ❌ |
43
46
 
44
47
  ### Key Differentiators
45
48
 
46
- 1. **High-Performance Hybrid Engine**: Combines multi-core AST processing with deep understanding of cross-file relationships and Qt-specific patterns.
47
- 2. **Safety-First Auto-Fixing**: AST-based transformations with Git status verification and interactive diff previews.
48
- 3. **Repository Compliance**: Local pre-checks to ensure your plugin passes the Official QGIS Repository policies.
49
+ 1. **Shift Left (Run Locally)**: The biggest advantage is being able to run the **same high-standard checks** as the Official Repository *before* you upload your plugin. No more "reject-fix-upload" loops.
50
+ 2. **High-Performance Hybrid Engine**: Combines multi-core AST processing with deep understanding of cross-file relationships and Qt-specific patterns.
51
+ 3. **Safety-First Auto-Fixing**: AST-based transformations with Git status verification and interactive diff previews.
49
52
  4. **Zero Runtime Stack**: Minimal footprint, ultra-fast execution, and easy CI integration.
50
53
  5. **AI-Centric Design**: Built to help developers and AI agents understand complex QGIS plugins instantly.
51
54
 
@@ -101,7 +104,7 @@ You can run `qgis-plugin-analyzer` automatically before every commit to ensure q
101
104
 
102
105
  ```yaml
103
106
  - repo: https://github.com/geociencio/qgis-plugin-analyzer
104
- rev: v1.4.0 # Use the latest tag
107
+ rev: main # Use 'main' for latest features or a specific tag like v1.5.0
105
108
  hooks:
106
109
  - id: qgis-plugin-analyzer
107
110
  ```
@@ -160,6 +163,7 @@ Audits an existing QGIS plugin repository.
160
163
  | :--- | :--- | :--- |
161
164
  | `project_path` | **(Required)** Path to the plugin directory to analyze. | N/A |
162
165
  | `-o`, `--output` | Directory where HTML/Markdown reports will be saved. | `./analysis_results` |
166
+ | `-r`, `--report` | Explicitly generate detailed HTML/Markdown reports. | `False` |
163
167
  | `-p`, `--profile`| Configuration profile from `pyproject.toml` (`default`, `release`). | `default` |
164
168
 
165
169
  ### `qgis-analyzer fix`
@@ -182,6 +186,18 @@ Shows a professional, color-coded summary of findings directly in your terminal.
182
186
  | `-b`, `--by` | Granularity of the summary: `total`, `modules`, `functions`, `classes`. | `total` |
183
187
  | `-i`, `--input` | Path to the `project_context.json` file to summarize. | `analysis_results/project_context.json` |
184
188
 
189
+ ### `qgis-analyzer security`
190
+ Performs a focused security scan on a file or directory.
191
+
192
+ | Argument | Description | Default |
193
+ | :--- | :--- | :--- |
194
+ | `path` | **(Required)** Path to the file or directory to scan. | N/A |
195
+ | `--deep` | Run more intensive (but slower) security checks. | `False` |
196
+ | `-p`, `--profile`| Configuration profile. | `default` |
197
+
198
+ ### `qgis-analyzer version`
199
+ Shows the current version of the analyzer.
200
+
185
201
  **Example:**
186
202
  ```bash
187
203
  # Executive summary
@@ -217,6 +233,7 @@ The development of this analyzer is based on official QGIS community guidelines,
217
233
  - **[QGIS Plugin Repository Requirements](https://plugins.qgis.org/publish/)**: Mandatory criteria for plugin approval in the official repository.
218
234
  - **[QGIS Coding Standards](https://docs.qgis.org/latest/en/docs/developer_guide/codingstandards.html)**: Core style and organization guidelines for the QGIS project.
219
235
  - **[QGIS HIG (Human Interface Guidelines)](https://docs.qgis.org/latest/en/docs/developer_guide/hig.html)**: Standards for consistent and accessible user interface design.
236
+ - **[QGIS Security Scanning Documentation](https://plugins.qgis.org/docs/security-scanning)**: Official guide on automated security analysis (Bandit, detect-secrets) for plugins.
220
237
 
221
238
  ### Industry & Community Standards
222
239
  - **[flake8-qgis Rules](https://github.com/qgis/flake8-qgis)**: Community-driven linting rules for PyQGIS (QGS101-106).
@@ -226,6 +243,11 @@ The development of this analyzer is based on official QGIS community guidelines,
226
243
  - **[Conventional Commits](https://www.conventionalcommits.org/)**: Standard for clear, machine-readable commit history.
227
244
  - **[Keep a Changelog](https://keepachangelog.com/)**: Best practices for maintainable version history.
228
245
 
246
+ ### Security Standards
247
+ - **[Bandit (PyCQA)](https://bandit.readthedocs.io/)**: The security rules implemented (B1xx - B6xx) are directly derived from the Bandit project's rule set for identifying common security issues in Python code.
248
+ - **[CWE (Common Weakness Enumeration)](https://cwe.mitre.org/)**: Security findings are mapped to standard CWE IDs (e.g., CWE-78 Command Injection, CWE-89 SQL Injection) for industry-standard classification.
249
+ - **[OWASP Top 10](https://owasp.org/www-project-top-ten/)**: The "Hardcoded Secret" and "Injection" checks align with critical OWASP vulnerabilities.
250
+
229
251
  ### Internal Resources
230
252
  - **[Detailed Rules Catalog](RULES.md)**: Full documentation of all audit rules implemented in this analyzer.
231
253
  - **[Standardized Scoring Metrics](docs/SCORING_STANDARDS.md)**: Mathematical logic and thresholds for project evaluation.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "qgis-plugin-analyzer"
3
- version = "1.4.0"
3
+ version = "1.6.0"
4
4
  description = "A professional static analysis tool for QGIS (PyQGIS) plugins"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.8"
@@ -43,6 +43,10 @@ dev = [
43
43
  "ruff>=0.9.0",
44
44
  "mypy>=1.0.0",
45
45
  "qgis-stubs>=0.1",
46
+ "pytest>=8.3.5",
47
+ "PyYAML>=6.0",
48
+ "types-pyyaml>=6.0.12.20241230",
49
+ "build>=1.2.2.post1",
46
50
  ]
47
51
 
48
52
  [tool.mypy]
@@ -56,7 +60,7 @@ disallow_untyped_defs = false # Gradual adoption
56
60
  [tool.ruff]
57
61
  line-length = 100
58
62
  target-version = "py38"
59
- exclude = ["src/analyzer/templates"]
63
+ exclude = ["src/analyzer/templates", ".ai-context"]
60
64
 
61
65
  [tool.ruff.lint]
62
66
  select = ["E", "F", "W", "I", "N", "UP", "B"]
@@ -64,6 +68,7 @@ ignore = ["E701", "E501"] # Permite if cond: statement y líneas largas
64
68
 
65
69
  [tool.ruff.lint.per-file-ignores]
66
70
  "src/analyzer/reporters.py" = ["F401", "F403", "F405"] # Dominate usa mucho import *
71
+ "src/analyzer/visitors/*.py" = ["N802"] # visit_* methods are part of ast.NodeVisitor API
67
72
 
68
73
  [tool.qgis-analyzer.profiles.default]
69
74
  strict = false
@@ -16,4 +16,5 @@
16
16
  # * the Free Software Foundation; either version 2 of the License, or *
17
17
  # * (at your option) any later version. *
18
18
  # * *
19
- # ***************************************************************************/
19
+ #
20
+ __version__ = "1.5.0"
@@ -0,0 +1,14 @@
1
+ """CLI package for QGIS Plugin Analyzer."""
2
+
3
+ import sys
4
+
5
+ from .app import CLIApp
6
+
7
+
8
+ def main() -> None:
9
+ """Main entry point for the QGIS Plugin Analyzer CLI."""
10
+ app = CLIApp()
11
+ sys.exit(app.run())
12
+
13
+
14
+ __all__ = ["CLIApp", "main"]
@@ -0,0 +1,147 @@
1
+ """CLI Application orchestrator."""
2
+
3
+ import argparse
4
+ import pathlib
5
+ import sys
6
+ from typing import Dict, List, Optional
7
+
8
+ from ..utils import logger, setup_logger
9
+ from .base import BaseCommand
10
+ from .commands import (
11
+ AnalyzeCommand,
12
+ FixCommand,
13
+ InitCommand,
14
+ ListRulesCommand,
15
+ SecurityCommand,
16
+ SummaryCommand,
17
+ VersionCommand,
18
+ )
19
+
20
+
21
+ class CLIApp:
22
+ """Main CLI application orchestrator.
23
+
24
+ Manages command registration, argument parsing, and execution.
25
+ """
26
+
27
+ def __init__(self):
28
+ """Initialize the CLI application with all available commands."""
29
+ self.commands: Dict[str, BaseCommand] = self._discover_commands()
30
+
31
+ def _discover_commands(self) -> Dict[str, BaseCommand]:
32
+ """Auto-discover and instantiate all command classes.
33
+
34
+ Returns:
35
+ Dictionary mapping command names to command instances.
36
+ """
37
+ command_classes: List[type[BaseCommand]] = [
38
+ AnalyzeCommand,
39
+ SecurityCommand,
40
+ FixCommand,
41
+ ListRulesCommand,
42
+ InitCommand,
43
+ SummaryCommand,
44
+ VersionCommand,
45
+ ]
46
+ return {cmd().name: cmd() for cmd in command_classes}
47
+
48
+ def _build_parser(self) -> argparse.ArgumentParser:
49
+ """Build the argument parser with all commands.
50
+
51
+ Returns:
52
+ Configured ArgumentParser instance.
53
+ """
54
+ from .. import __version__
55
+
56
+ parser = argparse.ArgumentParser(
57
+ description="QGIS Plugin Analyzer - A guardian for your PyQGIS code"
58
+ )
59
+ parser.add_argument("-v", "--version", action="version", version=f"%(prog)s {__version__}")
60
+ subparsers = parser.add_subparsers(dest="command", help="Command to execute")
61
+
62
+ # Register all commands
63
+ for cmd in self.commands.values():
64
+ cmd_parser = subparsers.add_parser(cmd.name, help=cmd.help)
65
+ cmd.configure_parser(cmd_parser)
66
+
67
+ return parser
68
+
69
+ def _parse_args(
70
+ self, parser: argparse.ArgumentParser, argv: Optional[List[str]] = None
71
+ ) -> argparse.Namespace:
72
+ """Parse command-line arguments with legacy support.
73
+
74
+ Args:
75
+ parser: The argument parser.
76
+ argv: Optional argument list (defaults to sys.argv).
77
+
78
+ Returns:
79
+ Parsed arguments namespace.
80
+ """
81
+ if argv is None:
82
+ argv = sys.argv[1:]
83
+
84
+ # Legacy support: default to 'analyze' if first arg is a path
85
+ if argv and argv[0] not in self.commands and not argv[0].startswith("-"):
86
+ argv.insert(0, "analyze")
87
+
88
+ return parser.parse_args(argv)
89
+
90
+ def _setup_logging(self, args: argparse.Namespace) -> None:
91
+ """Setup logging based on command arguments.
92
+
93
+ Args:
94
+ args: Parsed command-line arguments.
95
+ """
96
+ output_dir = pathlib.Path(getattr(args, "output", "./analysis_results")).resolve()
97
+ output_dir.mkdir(parents=True, exist_ok=True)
98
+ setup_logger(output_dir)
99
+
100
+ def _execute_command(self, args: argparse.Namespace) -> int:
101
+ """Execute the selected command.
102
+
103
+ Args:
104
+ args: Parsed command-line arguments.
105
+
106
+ Returns:
107
+ Exit code from command execution.
108
+ """
109
+ if not args.command or args.command not in self.commands:
110
+ return 1
111
+
112
+ command = self.commands[args.command]
113
+ return command.execute(args)
114
+
115
+ def run(self, argv: Optional[List[str]] = None) -> int:
116
+ """Run the CLI application.
117
+
118
+ Args:
119
+ argv: Optional argument list (defaults to sys.argv).
120
+
121
+ Returns:
122
+ Exit code (0 for success, non-zero for failure).
123
+ """
124
+ parser = self._build_parser()
125
+
126
+ try:
127
+ args = self._parse_args(parser, argv)
128
+
129
+ if not args.command:
130
+ parser.print_help()
131
+ return 0
132
+
133
+ self._setup_logging(args)
134
+ return self._execute_command(args)
135
+
136
+ except KeyboardInterrupt:
137
+ logger.info("\n⏹️ Analysis interrupted.")
138
+ return 1
139
+ except FileNotFoundError as e:
140
+ logger.error(f"Error: File not found: {e}")
141
+ return 1
142
+ except ValueError as e:
143
+ logger.error(f"Error: {e}")
144
+ return 1
145
+ except Exception as e:
146
+ logger.critical(f"Critical Error: {e}", exc_info=True)
147
+ return 1
@@ -0,0 +1,93 @@
1
+ """Base command class for CLI commands."""
2
+
3
+ import argparse
4
+ import pathlib
5
+ from abc import ABC, abstractmethod
6
+ from typing import Optional
7
+
8
+
9
+ class BaseCommand(ABC):
10
+ """Abstract base class for CLI commands.
11
+
12
+ Each command encapsulates its own argument configuration and execution logic.
13
+ """
14
+
15
+ @property
16
+ @abstractmethod
17
+ def name(self) -> str:
18
+ """Command name as it appears in the CLI.
19
+
20
+ Returns:
21
+ The command name string.
22
+ """
23
+
24
+ @property
25
+ @abstractmethod
26
+ def help(self) -> str:
27
+ """Help text for the command.
28
+
29
+ Returns:
30
+ A brief description of what the command does.
31
+ """
32
+
33
+ @abstractmethod
34
+ def configure_parser(self, parser: argparse.ArgumentParser) -> None:
35
+ """Configure command-specific arguments.
36
+
37
+ Args:
38
+ parser: The argument parser for this command.
39
+ """
40
+
41
+ @abstractmethod
42
+ def execute(self, args: argparse.Namespace) -> int:
43
+ """Execute the command.
44
+
45
+ Args:
46
+ args: Parsed command-line arguments.
47
+
48
+ Returns:
49
+ Exit code (0 for success, non-zero for failure).
50
+ """
51
+
52
+ def add_common_args(
53
+ self,
54
+ parser: argparse.ArgumentParser,
55
+ include_output: bool = True,
56
+ include_profile: bool = True,
57
+ ) -> None:
58
+ """Add common arguments shared across multiple commands.
59
+
60
+ Args:
61
+ parser: The argument parser to add arguments to.
62
+ include_output: Whether to include the --output argument.
63
+ include_profile: Whether to include the --profile argument.
64
+ """
65
+ if include_output:
66
+ parser.add_argument(
67
+ "-o",
68
+ "--output",
69
+ help="Output directory for reports",
70
+ default="./analysis_results",
71
+ )
72
+ if include_profile:
73
+ parser.add_argument(
74
+ "-p",
75
+ "--profile",
76
+ help="Configuration profile from pyproject.toml",
77
+ default="default",
78
+ )
79
+
80
+ def setup_output_dir(self, args: argparse.Namespace) -> Optional[pathlib.Path]:
81
+ """Setup and return the output directory if present in args.
82
+
83
+ Args:
84
+ args: Parsed command-line arguments.
85
+
86
+ Returns:
87
+ The resolved output directory path, or None if not applicable.
88
+ """
89
+ if hasattr(args, "output"):
90
+ output_dir = pathlib.Path(args.output).resolve()
91
+ output_dir.mkdir(parents=True, exist_ok=True)
92
+ return output_dir
93
+ return None
@@ -0,0 +1,19 @@
1
+ """CLI commands package."""
2
+
3
+ from .analyze import AnalyzeCommand
4
+ from .fix import FixCommand
5
+ from .init import InitCommand
6
+ from .list_rules import ListRulesCommand
7
+ from .security import SecurityCommand
8
+ from .summary import SummaryCommand
9
+ from .version import VersionCommand
10
+
11
+ __all__ = [
12
+ "AnalyzeCommand",
13
+ "SecurityCommand",
14
+ "FixCommand",
15
+ "ListRulesCommand",
16
+ "InitCommand",
17
+ "SummaryCommand",
18
+ "VersionCommand",
19
+ ]
@@ -0,0 +1,47 @@
1
+ """Analyze command implementation."""
2
+
3
+ import argparse
4
+
5
+ from ...commands import handle_analyze
6
+ from ..base import BaseCommand
7
+
8
+
9
+ class AnalyzeCommand(BaseCommand):
10
+ """Command to analyze an existing QGIS plugin."""
11
+
12
+ @property
13
+ def name(self) -> str:
14
+ """Command name."""
15
+ return "analyze"
16
+
17
+ @property
18
+ def help(self) -> str:
19
+ """Command help text."""
20
+ return "Analyze an existing QGIS plugin"
21
+
22
+ def configure_parser(self, parser: argparse.ArgumentParser) -> None:
23
+ """Configure analyze command arguments.
24
+
25
+ Args:
26
+ parser: The argument parser for this command.
27
+ """
28
+ parser.add_argument("project_path", help="Path to the QGIS project to analyze")
29
+ self.add_common_args(parser)
30
+ parser.add_argument(
31
+ "-r",
32
+ "--report",
33
+ action="store_true",
34
+ help="Generate detailed HTML/Markdown reports",
35
+ )
36
+
37
+ def execute(self, args: argparse.Namespace) -> int:
38
+ """Execute the analyze command.
39
+
40
+ Args:
41
+ args: Parsed command-line arguments.
42
+
43
+ Returns:
44
+ Exit code (0 for success).
45
+ """
46
+ handle_analyze(args)
47
+ return 0
@@ -0,0 +1,58 @@
1
+ """Fix command implementation."""
2
+
3
+ import argparse
4
+
5
+ from ...commands import handle_fix
6
+ from ..base import BaseCommand
7
+
8
+
9
+ class FixCommand(BaseCommand):
10
+ """Command to auto-fix common QGIS plugin issues."""
11
+
12
+ @property
13
+ def name(self) -> str:
14
+ """Command name."""
15
+ return "fix"
16
+
17
+ @property
18
+ def help(self) -> str:
19
+ """Command help text."""
20
+ return "Auto-fix common QGIS plugin issues"
21
+
22
+ def configure_parser(self, parser: argparse.ArgumentParser) -> None:
23
+ """Configure fix command arguments.
24
+
25
+ Args:
26
+ parser: The argument parser for this command.
27
+ """
28
+ parser.add_argument("path", type=str, help="Path to the QGIS plugin directory")
29
+ parser.add_argument(
30
+ "--dry-run",
31
+ action="store_true",
32
+ default=True,
33
+ help="Show proposed changes without applying (default: True)",
34
+ )
35
+ parser.add_argument("--apply", action="store_true", help="Apply fixes (disables dry-run)")
36
+ parser.add_argument(
37
+ "--auto-approve",
38
+ action="store_true",
39
+ help="Apply all fixes without confirmation",
40
+ )
41
+ self.add_common_args(parser, include_output=False)
42
+ parser.add_argument(
43
+ "--rules",
44
+ type=str,
45
+ help="Comma-separated list of rule IDs to fix",
46
+ )
47
+
48
+ def execute(self, args: argparse.Namespace) -> int:
49
+ """Execute the fix command.
50
+
51
+ Args:
52
+ args: Parsed command-line arguments.
53
+
54
+ Returns:
55
+ Exit code (0 for success).
56
+ """
57
+ handle_fix(args)
58
+ return 0