Skip to main content

QGIS Plugin Analyzer 🛡️

👉 View Full Rules Catalog (RULES.md)

GitHub release (latest by date) PyPI version PyPI downloads Python Version License GitHub stars GitHub forks Maintenance Conventional Commits CI

Quality Metrics:

Module Stability Maintainability Security Score Type Coverage Docstring Coverage Tests

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), unsafe pickle (B301), shell=True (B602), SQL injection (B608) and hardcoded secrets; findings carry CWE ids.
  • External security tools (optional): security --tools bandit,detect-secrets,flake8 runs the real portal tools, normalizes their findings (portal_severity, blocking) and emits --json; missing tools are reported as tool_unavailable with exit code 2.
  • Deep Entropy Secret Scanner: Detect hardcoded API keys, passwords and tokens via regex + information entropy (security --deep).
  • Extended Safety Audit: Unmatched signal connect/disconnect paths, missing slots and UI-blocking loops, with QgsTask suggestions.
  • 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 with flake8-qgis QGS4xx) detecting APIs removed in Qt6.
  • QGIS Metadata Rules: 5 catalog-backed METADATA_* checks (repository, version, QGIS 4 readiness, deprecated supportsQt6, category) in the metadata scope 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 N fails CI when a function exceeds the threshold (cc_gate/cc_violations in --json).
  • Design Patterns & Anti-patterns: Detects singleton/factory/observer/strategy/decorator, plus GOD_OBJECT, SPAGHETTI_CODE, MAGIC_NUMBER, DEAD_CODE.
  • Halstead Metrics & Optimizations: Per-module Halstead metrics and module_too_large / complexity_refactoring suggestions.
  • 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) via pyproject.toml.
  • Integrated Ruff Analysis: Runs Ruff through the active interpreter, exposing ruff_metadata.tool_unavailable when unavailable.

Architecture & performance

  • High-Performance Engine: ProcessPoolExecutor with 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: respects .gitignore, .qgisignore and .analyzerignore (ordered, ! negation), with default excludes for docs/, tests/, .venv/ and build/; add extra patterns with --exclude.

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'd project_context.json (schema/schema_version, analyzer_version, ruff_metadata, patterns, optimizations).
  • Quality Gates in CI: ruff + mypy + pytest (coverage floor) and the --max-cc self-analysis gate on Python 3.11–3.13.
  • Embedded Web Server: Built-in serve command 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, exposing ruff_metadata / flake8_metadata with explicit tool_unavailable flags.
  • 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.19.2

SIGNAL_LEAK parent and loop corrections — follow-up patch for issue #1.

  • 🚫 Translated text is not a parent — QRadioButton(self.tr("A")) no longer becomes a false parent_child.
  • 🔗 Pairing fixed — each sender in a tuple loop keeps its own slot.
  • 📋 List wrappers — for task in list([self.task]) resolves the original connection.
  • 🧾 --signals — detailed unmatched paths with file:line, confidence and lifetime (informational unless emit=true).

📖 Full Release Notes | 🗺️ CLI Commands Roadmap

🚀 Installation and Usage

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
exclude = ["generated/"]  # defaults already exclude docs/, tests/, .venv/, build/

[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"]

# Informational signal matching (opt in to a gate with emit = true):
[tool.qgis-analyzer.profiles.default.rules.SIGNAL_LEAK]
emit = false
scope = "class"           # function | class | file
confidence = "low"        # low shows all; medium/high reduce noise
include_benign = false
ignore_senders = []
ignore_paths = []

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 .qrc files 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 # noqa or .analyzerignore to 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 sets tool_unavailable (and security exits 2) 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 as qgis-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)
  • all or 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. .
--scope SCOPE Explicit scope alternative to the positional form. none
--exclude PATTERN Extra ignore pattern (repeatable/comma-separated; ! re-includes). none
-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)
--no-cache Disable the incremental per-file analysis cache. False
--signals Print detailed unmatched signal paths (informational). False

Ignore files and exclusions

The matcher is ordered and last match wins, so a later !pattern re-includes a path:

defaults → .gitignore → .qgisignore → .analyzerignore → --exclude / profile exclude.

Default excludes include docs/, tests/, .venv/, build/, dist/, caches and generated artifacts. To analyze one of those directories again:

qgis-analyzer analyze . --exclude '!docs/'
qgis-analyzer analyze . --exclude '!tests/'

Examples:

# Full analysis (legacy compatible)
qgis-analyzer analyze .
qgis-analyzer analyze /path/to/plugin

# Specialized i18n analysis (both forms are equivalent)
qgis-analyzer analyze i18n .
qgis-analyzer analyze . --scope i18n

# Analyze docs/ and tests/ explicitly
qgis-analyzer analyze . --exclude '!docs/' --exclude '!tests/'

# 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
--exclude PATTERN Extra ignore pattern (repeatable/comma-separated; ! re-includes). none
-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 plus formatter) when --flake8/--linters includes Flake8.
    • repository_compliance includes constraint_errors (blocking, e.g. binaries) and constraint_warnings (informational size).
    • patterns (design patterns) and optimizations (module_too_large, complexity_refactoring).
    • Per module: path, lines, complexity, imports, functions, classes, ast_issues, research_metrics (type-hint/docstring coverage, halstead).
    • research_metrics.qgis_context.signal_connections: per-connection sender, receiver, slot, status, confidence, lifetime, connection_type; qgis_context_summary.signal_connections aggregates them. signal_leaks remains a deprecated sender-string list.
    • cc_gate / cc_violations when --max-cc is used with --json.
    • security --json emits its own envelope: schema/schema_version, analyzer_version, command, scope, tools, tool_status, tool_unavailable, findings (portal_severity, blocking, tool), summary and exit_code.
  • 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 (bump schema_version on 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

Industry & Community Standards

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

🛠️ 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.19.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for qgis-plugin-analyzer 1.19.2
File Size Uploaded Provenance Actions
qgis_plugin_analyzer-1.19.2.tar.gz 133.0 kB Without provenance Details for qgis_plugin_analyzer-1.19.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for qgis-plugin-analyzer 1.19.2
File Interpreter ABI Platform Actions
qgis_plugin_analyzer-1.19.2-py3-none-any.whl Python 3 none any Details for qgis_plugin_analyzer-1.19.2-py3-none-any.whl

Total release size: 301.8 kB

Release files / qgis_plugin_analyzer-1.19.2.tar.gz

Download URL qgis_plugin_analyzer-1.19.2.tar.gz
Size 133.0 kB
Tags Source
SHA-256 checksum
How to use checksums
84f2ff534445477148ceb10f18bd054e0c938136c97e5b2586802a878310c1ba
BLAKE2b-256 checksum
How to use checksums
d13e78691af7560cd6957bd57376fedc65c24e61e0b7c15433d4d2c86de94aab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","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.19.2-py3-none-any.whl

Download URL qgis_plugin_analyzer-1.19.2-py3-none-any.whl
Size 168.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bf5e453f833018da9f400787092ec5a854be66db4bd4fe9c997158f601bd09f7
BLAKE2b-256 checksum
How to use checksums
feff5337c9eb20f1081bf4602d492258e2bc59d4424bb0f0f22f3c0c4e1f455f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","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.19.2

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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page