QGIS Plugin Analyzer 🛡️
👉 View Full Rules Catalog (RULES.md)
Quality Metrics:
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.
✨ Main Features
Security & correctness
- Bandit-inspired Security Core: Detects
eval/exec(B307/B102), unsafepickle(B301),shell=True(B602), SQL injection (B608) and hardcoded secrets; findings carry CWE ids. - External security tools (optional):
security --tools bandit,detect-secrets,flake8runs the real portal tools, normalizes their findings (portal_severity,blocking) and emits--json; missing tools are reported astool_unavailablewith exit code2. - Deep Entropy Secret Scanner: Detect hardcoded API keys, passwords and tokens via regex + information entropy (
security --deep). - Extended Safety Audit: Signal leaks, missing slots and UI-blocking loops, with
QgsTasksuggestions. - Qt Resource Validation: Detects missing/broken resource paths (
:/plugins/...).
QGIS standards & modernization
- Precise AST rules: i18n (
self.tr()/QCoreApplication.translate()), obsolete API, protected members, mandatory cleanup,iface-as-argument, and more. - Qt6 Migration Rules: 11 AST-based
QT6_*rules (drop-in parity withflake8-qgisQGS4xx) detecting APIs removed in Qt6. - QGIS Metadata Rules: 5 catalog-backed
METADATA_*checks (repository, version, QGIS 4 readiness, deprecatedsupportsQt6, category) in themetadatascope and portal profile. - Official Repository Compliance: Validates binaries and metadata URLs before upload. The source-tree size is informational; the portal 25 MB limit applies to the built ZIP (qgis-manage's artifact layer).
Quality & maintainability
- Cyclomatic Complexity Gate:
--max-cc Nfails CI when a function exceeds the threshold (cc_gate/cc_violationsin--json). - Design Patterns & Anti-patterns: Detects
singleton/factory/observer/strategy/decorator, plusGOD_OBJECT,SPAGHETTI_CODE,MAGIC_NUMBER,DEAD_CODE. - Halstead Metrics & Optimizations: Per-module Halstead metrics and
module_too_large/complexity_refactoringsuggestions. - Transversal
# noqa: Suppress any rule inline with# noqa(whole line) or# noqa: RULE_ID(single rule). - Enhanced Configuration Profiles: Rule-level severity control (
error,warning,info,ignore) viapyproject.toml. - Integrated Ruff Analysis: Runs Ruff through the active interpreter, exposing
ruff_metadata.tool_unavailablewhen unavailable.
Architecture & performance
- High-Performance Engine:
ProcessPoolExecutorwith single-pass AST traversal, shared worker context, a configurable worker count (--workers) and file batching. - Deep Semantic Analysis: Cross-file dependency graph (Mermaid), circular-import detection and coupling metrics.
- Project Auto-Detection: Distinguishes official QGIS plugins from generic Python projects and tailors validation accordingly.
- Advanced Ignore Engine:
.analyzerignorewith non-anchored patterns and smart default excludes (.venv,build, …).
DX & integration
- Interactive Auto-Fix: AST-based fixes (GDAL imports, PyQt bridge, logging, i18n) with Git-status verification and diff preview.
- CI Output Contract:
--json,--include-content, and a versioned/schema'dproject_context.json(schema/schema_version,analyzer_version,ruff_metadata,patterns,optimizations). - Quality Gates in CI:
ruff+mypy+pytest(coverage floor) and the--max-ccself-analysis gate on Python 3.11–3.13. - Embedded Web Server: Built-in
servecommand to browse HTML reports. - Real-time Progress: CLI progress bar with ETA tracking.
- Integrated Lint Analysis: Runs Ruff and, optionally, Flake8 (
--flake8/--linters ruff,flake8) through the active interpreter, exposingruff_metadata/flake8_metadatawith explicittool_unavailableflags. - Zero Runtime Dependencies: Standard library only; Ruff is the only required external tool. Flake8, Bandit and detect-secrets are optional extras (
qgis-analyzer[flake8],[security],[portal]).
🆕 What's New in v1.17.0
Portal gate integration — the analyzer now covers the source-level checks used by the official-repository gate.
- 🐍 Flake8 + real security tools —
analyze --flake8/--lintersandsecurity --tools bandit,detect-secrets,flake8run the actual tools, normalize findings (portal_severity,blocking) and exposetool_unavailable. - 🧾
security --json— versioned report with findings, summary, tool status and exit codes0/1/2, ready for qgis-manage. - ⚙️ Portal profile —
[tool.qgis-analyzer.profiles.portal]wires linters, security tools, the security threshold, metadata rules and thefail_ongate. - 📋 Metadata rules — five
METADATA_*checks for repository, version, QGIS 4,supportsQt6and category. - 📦 25 MB source-tree warning — the portal limit now applies to the built ZIP
(qgis-manage), not to
docs//tests/.
📖 Full Release Notes | 🗺️ CLI Commands Roadmap
🚀 Installation and Usage
Installation with uv (Recommended):
If you have uv installed, you can install the analyzer quickly and in isolation:
1. As a global tool (isolated):
uv tool install git+https://github.com/geociencio/qgis-plugin-analyzer.git
2. Standard pip installation (Git):
pip install git+https://github.com/geociencio/qgis-plugin-analyzer.git
3. Local installation for development:
git clone https://github.com/geociencio/qgis-plugin-analyzer
cd qgis-plugin-analyzer
uv sync
Installation with pip:
pip install .
Main Commands:
1. Analyze a Plugin (Full Analysis):
qgis-analyzer analyze /path/to/your/plugin -o ./quality_report
2. Specialized Analysis (NEW in v1.9.0):
# Internationalization audit only
qgis-analyzer analyze i18n /path/to/your/plugin
# Security vulnerability scanning only
qgis-analyzer analyze security /path/to/your/plugin
# Performance and UI blocking detection only
qgis-analyzer analyze performance /path/to/your/plugin
# Dependency and coupling analysis only
qgis-analyzer analyze architecture /path/to/your/plugin
# QGIS metadata validation only
qgis-analyzer analyze metadata /path/to/your/plugin
3. Auto-Fix issues (Dry Run):
qgis-analyzer fix /path/to/your/plugin
4. Legacy Support: The default command remains analysis if no subcommand is specified:
qgis-analyzer /path/to/your/plugin
🔄 Pre-commit Hook
You can run qgis-plugin-analyzer automatically before every commit to ensure quality. Add this to your .pre-commit-config.yaml:
- repo: https://github.com/geociencio/qgis-plugin-analyzer
rev: main # Use 'main' for latest features or a specific tag like v1.5.0
hooks:
- id: qgis-plugin-analyzer
🤖 GitHub Action
Use it directly in your CI/CD workflows:
steps:
- uses: actions/checkout@v4
- name: Run QGIS Quality Check
uses: geociencio/qgis-plugin-analyzer@main
with:
path: .
output: quality_report
args: --profile release
⚙️ Configuration (pyproject.toml)
You can customize the analyzer's behavior using a [tool.qgis-analyzer] section in your pyproject.toml.
[tool.qgis-analyzer]
# Profiles allow different settings for CI vs Local
[tool.qgis-analyzer.profiles.default]
strict = false
generate_html = false # CLI default
[tool.qgis-analyzer.profiles.release]
strict = true
fail_on_error = true
# Portal/CI profile: real tools, metadata rules and a security gate.
[tool.qgis-analyzer.profiles.portal]
linters = ["ruff", "flake8"]
security_tools = ["native", "bandit", "detect-secrets", "flake8"]
security_fail_on = "critical"
metadata_rules = true
fail_on = "critical"
[tool.qgis-analyzer.profiles.default.rules]
GDAL_DIRECT_IMPORT = "error" # Ban direct 'import gdal'
IFACE_AS_ARGUMENT = "warning" # Warn on iface passed as an argument
MANUAL_RESOURCE_PATH = "ignore" # Ignore resource path checks
# i18n rule configuration (AST-based):
[tool.qgis-analyzer.profiles.default.rules.MISSING_I18N]
extra_ignore_calls = ["customSetLabel"]
extra_exact_ignores = ["My App Name"]
Precedence is CLI > profile > default: security --profile portal uses
security_tools/security_fail_on unless --tools/--fail-on are passed;
analyze --profile portal runs linters, metadata_rules and the fail_on
security gate.
⚠️ Technical Limitations
This tool performs Static Analysis (AST & Regex parsing). It does not execute your code or load QGIS libraries.
- Dynamic Imports: Imports inside functions or conditional blocks might be analyzed differently than top-level imports.
- Runtime Validation: Checks like "Missing Resources" rely on static string analysis of
.qrcfiles and path strings. It cannot verify resources loaded dynamically at runtime. - False Positives: While we strive for accuracy, complex meta-programming or unusual patterns might trigger false positives. Use
# noqaor.analyzerignoreto handle these cases. - Optional external tools: Flake8, Bandit and detect-secrets are not bundled. Install
qgis-analyzer[flake8],[security]or[portal]; when a requested tool is missing, the report setstool_unavailable(andsecurityexits2) instead of reporting a silent zero.
⌨️ Full CLI Reference
Note: The Python package is named
qgis-plugin-analyzer, but the command-line tool is installed asqgis-analyzer.
qgis-analyzer analyze [scope] [path]
Audits an existing QGIS plugin repository with optional specialized scopes.
NEW in v1.9.0: Specialized analysis scopes for targeted auditing.
Available Scopes:
i18n- Internationalization and translation audit (detects untranslated strings)security- Security vulnerability scanning (unsafe calls, hardcoded secrets, SQL injection)performance- Performance and UI blocking detection (blocking loops, missing indexes)architecture- Dependency and coupling analysis (imports, QGIS API usage)metadata- QGIS metadata validation (metadata.txt compliance)allor no scope - Full analysis (default, legacy compatible)
Arguments:
| Argument | Description | Default |
|---|---|---|
scope |
(Optional) Analysis scope: i18n, security, performance, architecture, metadata, or all. |
all |
project_path |
(Required) Path to the plugin directory to analyze. | . |
-o, --output |
Directory where HTML/Markdown reports will be saved. | ./analysis_results |
-r, --report |
Explicitly generate detailed HTML/Markdown reports. | False |
-p, --profile |
Configuration profile from pyproject.toml (default, release). |
default |
--json |
Emit machine-readable JSON (project_context.json) to stdout. |
False |
--include-content |
Embed module source content in the JSON output. | False |
--linters LIST |
Comma-separated lint tools (ruff, flake8; use none to disable). |
ruff |
--flake8 |
Shorthand for --linters ruff,flake8. |
False |
--max-cc N |
Fail analysis if any function exceeds this cyclomatic complexity. | off |
--workers N |
Number of parallel worker processes (clamped to 1..4 to bound memory). |
min(4, cpu-1) |
Examples:
# Full analysis (legacy compatible)
qgis-analyzer analyze .
qgis-analyzer analyze /path/to/plugin
# Specialized i18n analysis
qgis-analyzer analyze i18n .
# Security-only scan with reports
qgis-analyzer analyze security . --report
qgis-analyzer fix
Automatically fix common QGIS issues identified during analysis.
| Argument | Description | Default |
|---|---|---|
path |
(Required) Path to the plugin directory. | N/A |
--dry-run |
Show proposed changes without applying them. | True |
--apply |
Apply fixes to the files (disables dry-run). | False |
--auto-approve |
Apply fixes without interactive confirmation. | False |
--rules |
Comma-separated list of rule IDs to fix. | Fix all |
-o, --output |
Directory to read previous analysis from. | ./analysis_results |
qgis-analyzer summary
Shows a professional, color-coded summary of findings directly in your terminal.
| Argument | Description | Default |
|---|---|---|
-b, --by |
Granularity of the summary: total, modules, functions, classes, security. |
total |
-i, --input |
Path to the project_context.json file to summarize. |
analysis_results/project_context.json |
qgis-analyzer security
Performs a focused security scan on a file or directory.
| Argument | Description | Default |
|---|---|---|
path |
(Required) Path to the file or directory to scan. | N/A |
--deep |
Enable the native entropy heuristic (full secret detection). | False |
--tools LIST |
Comma-separated tools: native, bandit, detect-secrets, flake8. |
native |
--json |
Emit a machine-readable security report (findings, summary, tool status). | False |
--fail-on SEV |
Lowest severity that fails: critical, warning, info, none. |
critical |
-p, --profile |
Configuration profile. | default |
Examples:
# Native scan (regex + AST rules); entropy only with --deep
qgis-analyzer security .
# Portal parity: real Bandit + detect-secrets + Flake8, JSON, block on critical findings
qgis-analyzer security . --tools bandit,detect-secrets,flake8 --json --fail-on critical
# Machine-readable native report
qgis-analyzer security . --deep --json
Exit codes: 0 clean, 1 findings at/above --fail-on, 2 invalid input or an unavailable requested tool.
qgis-analyzer version
Shows the current version of the analyzer.
Example:
# Executive summary
qgis-analyzer summary
# Identify high-complexity functions
qgis-analyzer summary --by functions
qgis-analyzer list-rules
Displays the full catalog of implemented QGIS audit rules with their severity and descriptions.
qgis-analyzer graph
Visualizes the project's dependency graph.
| Argument | Description | Default |
|---|---|---|
project_path |
Path to the plugin directory. | . |
--format |
Output format: text or mermaid. |
text |
qgis-analyzer serve
Starts a local web server to view the generated HTML reports.
| Argument | Description | Default |
|---|---|---|
path |
Path to the analysis results directory. | ./analysis_results |
--port |
Port to run the server on. | 8000 |
qgis-analyzer init
Initializes a recommended .analyzerignore file in the current directory with common Python and QGIS development exclusions.
📊 Generated Reports
Written to the --output directory (default ./analysis_results):
project_context.json— machine-readable contract (schema v1):- Top level:
schema/schema_version,analyzer_version,project_name,project_path,analyzed_at,metrics. ruff_findings+ruff_metadata(exit_code,tool_unavailable,command).flake8_findings+flake8_metadata(same fields plusformatter) when--flake8/--lintersincludes Flake8.repository_complianceincludesconstraint_errors(blocking, e.g. binaries) andconstraint_warnings(informational size).patterns(design patterns) andoptimizations(module_too_large,complexity_refactoring).- Per module:
path,lines,complexity,imports,functions,classes,ast_issues,research_metrics(type-hint/docstring coverage,halstead). cc_gate/cc_violationswhen--max-ccis used with--json.security --jsonemits its own envelope:schema/schema_version,analyzer_version,command,scope,tools,tool_status,tool_unavailable,findings(portal_severity,blocking,tool),summaryandexit_code.
- Top level:
PROJECT_SUMMARY.md/PROJECT_SUMMARY.html— human-readable report.analyzer.log— run log.
The JSON is consumed by
ai-context-core; keep the schema keys stable (bumpschema_versionon change).
📜 Audit Rules
For a complete list of all implemented checks, their severity, and recommendations, please refer to the:
👉 Detailed Rules Catalog (RULES.md)
📚 References and Standards
The development of this analyzer is based on official QGIS community guidelines, geospatial standards, and industry best practices:
Official QGIS Documentation
- PyQGIS Developer Cookbook: The primary resource for PyQGIS API usage and standards.
- QGIS Plugin Repository Requirements: Mandatory criteria for plugin approval in the official repository.
- QGIS Coding Standards: Core style and organization guidelines for the QGIS project.
- QGIS HIG (Human Interface Guidelines): Standards for consistent and accessible user interface design.
- QGIS Security Scanning Documentation: Official guide on automated security analysis (Bandit, detect-secrets) for plugins.
Industry & Community Standards
- flake8-qgis Rules: Community-driven linting rules for PyQGIS (QGS101-412).
- PEP 8 Style Guide: The fundamental style guide for Python code.
- PEP 257 Docstring Conventions: Standards for docstring structure and content.
- Maintainability Index (SEI): Methodology for measuring software maintainability.
- Conventional Commits: Standard for clear, machine-readable commit history.
- Keep a Changelog: Best practices for maintainable version history.
Security Standards
- Bandit (PyCQA): The security rules implemented (B1xx - B6xx) are directly derived from the Bandit project's rule set for identifying common security issues in Python code.
- CWE (Common Weakness Enumeration): Security findings are mapped to standard CWE IDs (e.g., CWE-78 Command Injection, CWE-89 SQL Injection) for industry-standard classification.
- OWASP Top 10: The "Hardcoded Secret" and "Injection" checks align with critical OWASP vulnerabilities.
Internal Resources
- Detailed Rules Catalog: Full documentation of all audit rules implemented in this analyzer.
- Standardized Scoring Metrics: Mathematical logic and thresholds for project evaluation.
- Project Roadmap: Current status and future plans for the analyzer.
- Release Process: How to cut a release — versioning, GitHub Release and PyPI publication.
- Comparison vs Alternatives: Internal positioning notes (kept out of this README).
- Documentation Folder: Historical release notes, competitive analysis, and modernization guides.
🛠️ Contributing
Contributions are welcome! Please refer to our Contributing Guide to learn how to report bugs, propose rules, and submit code changes.
Audit rules are located in src/analyzer/rules/ (regex catalog) and src/analyzer/visitors/ (AST visitors). Feel free to add new rules following the existing pattern!
⚖️ License
This project is licensed under the GNU General Public License v3 (GPL v3). See the LICENSE file for details.
Developed for the SecInterp team and the QGIS community.
Metadata
Release files for qgis-plugin-analyzer 1.17.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| qgis_plugin_analyzer-1.17.0.tar.gz | 165.6 kB | Without provenance | Details for qgis_plugin_analyzer-1.17.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| qgis_plugin_analyzer-1.17.0-py3-none-any.whl | Python 3 | none | any | Details for qgis_plugin_analyzer-1.17.0-py3-none-any.whl |
Total release size: 322.9 kB
Release files / qgis_plugin_analyzer-1.17.0.tar.gz
| Download URL | qgis_plugin_analyzer-1.17.0.tar.gz |
|---|---|
| Size | 165.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
82454162f5d94786c783f8c7113da10a4537617c14f4b3a3dee08ca177a1b3a4
|
|
BLAKE2b-256 checksum How to use checksums |
f7c6a706517c61fd6fd18a5b14938f728d243632b00a5ca3ecf36ab02d30f596
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / qgis_plugin_analyzer-1.17.0-py3-none-any.whl
| Download URL | qgis_plugin_analyzer-1.17.0-py3-none-any.whl |
|---|---|
| Size | 157.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c25ff960659d03e89680f3550d1679d99f231b28570856e8e4f0de5127cc93d1
|
|
BLAKE2b-256 checksum How to use checksums |
414b2dc9e707cdffb023ab64b957ac7bc2de9a1ef56a53ac707d9d1d6ded46e4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for qgis-plugin-analyzer 1.17.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.