Skip to content

chore: add agent skills from github/awesome-copilot - #336

Merged
docdyhr merged 4 commits into
mainfrom
chore/add-agent-skills
Oct 19, 2026
Merged

docdyhr merged 4 commits into
mainfrom
chore/add-agent-skills

Conversation

@docdyhr

@docdyhr docdyhr commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

Adds agent skills under .claude/skills/. Claude Code loads them as project skills, and GitHub Copilot (cloud agent, CLI, VS Code agent mode) reads the same path.

  • dependabot
  • docs-sync-audit
  • github-actions-hardening
  • pytest-coverage
  • test-gap-audit

Also in this PR:

  • .gitignore re-includes .claude/skills/ and keeps the rest of .claude/ ignored.
  • The upstream MIT LICENSE ships alongside the skills.

Source: github/awesome-copilot, vendored verbatim at the commit recorded in .claude/skills/README.md. github-release and conventional-commit were deliberately left out because they conflict with this repo's release and commit workflow. No skill pre-approves shell/bash.

The open review threads are about the upstream skill content itself and are waiting on the owner's call.

https://claude.ai/code/session_01CeXGfVNFFTFVtXdALdSQ1g


Generated by Claude Code

Summary by Sourcery

Add and document vendored agent skills that support dependency management, documentation audits, workflow security reviews, and test coverage audits.

New Features:

  • Add vendored Dependabot, documentation-audit, GitHub Actions hardening, and test-gap audit skills for Claude Code and GitHub Copilot.

Enhancements:

  • Define repository-specific precedence and safety rules for using the vendored skills, including report-only audits and the project’s 85% coverage target.
  • Preserve vendored skill files byte-for-byte while excluding them from pre-commit rewriting and documenting their upstream provenance.

Documentation:

  • Document the vendored skills, their supported purposes, upstream source, licensing, and repository-specific usage constraints.

Chores:

  • Include the upstream MIT license and adjust ignore rules to track only the project’s vendored skill directory.

Adds 7 skills under .github/skills/ (source commit cb64ad1).
@sourcery-ai

sourcery-ai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Installs seven agent skills under .github/skills/, copied from a pinned github/awesome-copilot revision, with repository documentation, audit and coverage workflows, Actions security guidance, Dependabot references, and a confirmation-gated GitHub release workflow.

File-Level Changes

Change Details Files
Adds a repository-local catalog and metadata for seven agent skills sourced from a pinned awesome-copilot commit.
  • Documents installation source, commit provenance, supported agent hosts, and each skill’s purpose.
  • Adds skills for conventional commits, Dependabot, documentation audits, Actions security, releases, pytest coverage, and test-gap audits.
.github/skills/README.md
.github/skills/conventional-commit/SKILL.md
.github/skills/dependabot/SKILL.md
.github/skills/docs-sync-audit/SKILL.md
.github/skills/github-actions-hardening/SKILL.md
.github/skills/github-release/SKILL.md
.github/skills/pytest-coverage/SKILL.md
.github/skills/test-gap-audit/SKILL.md
Adds detailed reference material supporting the Dependabot, GitHub Actions hardening, and GitHub release workflows.
  • Provides Dependabot YAML guidance, ecosystem and grouping options, security configuration, PR commands, and example configurations.
  • Documents Actions trigger trust boundaries, injection patterns, token permissions, supply-chain pinning, and review report format.
  • Adds release classification and SemVer rules used by the release workflow.
.github/skills/dependabot/references/dependabot-yml-reference.md
.github/skills/dependabot/references/example-configs.md
.github/skills/dependabot/references/pr-commands.md
.github/skills/github-actions-hardening/references/injection.md
.github/skills/github-actions-hardening/references/permissions-and-tokens.md
.github/skills/github-actions-hardening/references/report-format.md
.github/skills/github-actions-hardening/references/supply-chain.md
.github/skills/github-actions-hardening/references/triggers-and-privilege.md
.github/skills/github-release/references/commit-classification.md
.github/skills/github-release/references/semver-rules.md
Encodes agent workflows for audits and operational tasks, including read-only review behavior and release automation.
  • Defines breadth-first documentation and test-gap audits with evidence standards, severity rubrics, report formats, and read-only constraints.
  • Defines pytest coverage execution and iterative work toward full line coverage.
  • Defines a nine-step GitHub release process using git and gh, with confirmation points before changes and publishing actions.
  • Explicitly reviews bundled content as read-only helpers without pre-approving shell or bash tools.
.github/skills/docs-sync-audit/SKILL.md
.github/skills/test-gap-audit/SKILL.md
.github/skills/pytest-coverage/SKILL.md
.github/skills/github-release/SKILL.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-17T01:10:30.281918Z b5cd3a9 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown

🔒 Security Analysis Report

Security Analysis Report

Generated: Sat Oct 17 01:08:51 UTC 2026
Repository: docdyhr/versiontracker
Commit: e6e1da5

Bandit Security Scan

�[?25l
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m  0%�[0m �[36m-:--:--�[0m
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m  0%�[0m �[36m-:--:--�[0m
�[2KWorking... �[91m━━━�[0m�[90m╺�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m  8%�[0m �[36m-:--:--�[0m
�[2KWorking... �[91m━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m 17%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━�[0m�[90m╺�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m 41%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━�[0m�[90m╺�[0m�[90m━━━━━━━━━━━━━━━━━━�[0m �[35m 53%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━━━�[0m �[35m 67%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m�[90m╺�[0m�[90m━━━━━━━━�[0m �[35m 78%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m�[90m╺�[0m�[90m━━━━━�[0m �[35m 86%�[0m �[36m0:00:01�[0m
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m100%�[0m �[33m0:00:00�[0m
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m100%�[0m �[33m0:00:00�[0m
�[?25hRun started:2026-10-17 01:08:52.108026+00:00

Test results:
>> Issue: [B608:hardcoded_sql_expressions] Possible SQL injection vector through string-based query construction.
   Severity: Medium   Confidence: Low
   CWE: CWE-89 (https://cwe.mitre.org/data/definitions/89.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b608_hardcoded_sql_expressions.html
   Location: versiontracker/advanced_cache.py:693:24
692	                # Use f-string for better readability
693	                msg = f"Failed to delete from cache {key}: {e}"
694	                raise CacheError(msg) from e

--------------------------------------------------
>> Issue: [B105:hardcoded_password_string] Possible hardcoded password: 'homebrew_token'
   Severity: Low   Confidence: Medium
   CWE: CWE-259 (https://cwe.mitre.org/data/definitions/259.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b105_hardcoded_password_string.html
   Location: versiontracker/audit/models.py:161:21
160	    BUNDLE_ID = "bundle_id"
161	    HOMEBREW_TOKEN = "homebrew_token"
162	    DISPLAY_NAME = "display_name"

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/config.py:174:16
173	                cmd = f"{path} --version"
174	                import subprocess
175	

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/config.py:177:29
176	                try:
177	                    result = subprocess.run(cmd.split(), capture_output=True, timeout=2, check=False)
178	                    returncode = result.returncode

--------------------------------------------------
>> Issue: [B110:try_except_pass] Try, Except, Pass detected.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b110_try_except_pass.html
   Location: versiontracker/experimental/analytics.py:645:16
644	                    self.peak_cpu = max(self.peak_cpu, cpu_percent)
645	                except Exception:
646	                    pass
647	                time.sleep(0.05)

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: versiontracker/handlers/outdated_handlers.py:518:8
517	        # Type assertion: apps and brews cannot be None here due to exit_code checks above
518	        assert apps is not None
519	        assert brews is not None

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: versiontracker/handlers/outdated_handlers.py:519:8
518	        assert apps is not None
519	        assert brews is not None
520	        apps = _filter_applications(apps, brews, include_brews)

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: versiontracker/handlers/outdated_handlers.py:539:8
538	        # Type assertion: outdated_info cannot be None here due to exit_code check
539	        assert outdated_info is not None
540	        # Type cast: outdated_info cannot be None here due to exit_code check above

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/homebrew.py:12:0
11	import re
12	import subprocess
13	import time

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/macos_integration.py:12:0
11	import plistlib
12	import subprocess
13	from pathlib import Path

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/menubar_app.py:8:0
7	import logging
8	import subprocess
9	import sys

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/plugins/example_plugins.py:322:16
321	            try:
322	                import subprocess
323	

--------------------------------------------------
>> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b607_start_process_with_partial_path.html
   Location: versiontracker/plugins/example_plugins.py:324:25
323	
324	                result = subprocess.run(["brew", "--version"], capture_output=True, text=True, timeout=5)
325	                if result.returncode == 0:

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/plugins/example_plugins.py:324:25
323	
324	                result = subprocess.run(["brew", "--version"], capture_output=True, text=True, timeout=5)
325	                if result.returncode == 0:

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/utils.py:16:0
15	import shutil
16	import subprocess
17	import sys

--------------------------------------------------
>> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b607_start_process_with_partial_path.html
   Location: versiontracker/utils.py:804:17
803	    try:
804	        result = subprocess.run(["which", "brew"], capture_output=True, text=True, timeout=5)
805	        return result.returncode == 0

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/utils.py:804:17
803	    try:
804	        result = subprocess.run(["which", "brew"], capture_output=True, text=True, timeout=5)
805	        return result.returncode == 0

--------------------------------------------------
>> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b607_start_process_with_partial_path.html
   Location: versiontracker/utils.py:820:17
819	    try:
820	        result = subprocess.run(["brew", "--prefix"], capture_output=True, text=True, timeout=5)
821	        if result.returncode == 0:

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/utils.py:820:17
819	    try:
820	        result = subprocess.run(["brew", "--prefix"], capture_output=True, text=True, timeout=5)
821	        if result.returncode == 0:

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/utils.py:853:15
852	    try:
853	        return subprocess.run(command, capture_output=True, text=True, timeout=timeout, check=check)
854	    except subprocess.TimeoutExpired as e:

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/version/batch.py:11:0
10	import logging
11	import subprocess
12	from concurrent.futures import ThreadPoolExecutor

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/version/homebrew.py:18:0
17	import logging
18	import subprocess
19	

--------------------------------------------------

Code scanned:
	Total lines of code: 17221
	Total lines skipped (#nosec): 0
	Total potential issues skipped due to specifically being disabled (e.g., #nosec BXXX): 30

Run metrics:
	Total issues (by severity):
		Undefined: 0
		Low: 21
		Medium: 1
		High: 0
	Total issues (by confidence):
		Undefined: 0
		Low: 1
		Medium: 1
		High: 20
Files skipped (0):

Pip-Audit Results

�[?25l�[32m-�[0m Collecting inputs
�[2K�[32m-�[0m Collecting inputs
�[2K�[32m-�[0m Collecting inputs
�[2K�[32m-�[0m Collecting inputs
�[2K�[32m\�[0m Collecting inputs
�[2K�[32m\�[0m Collecting inputs
�[2K�[32m\�[0m Collecting aiohappyeyeballs (2.7.1)
�[2K�[32m\�[0m Auditing aiohappyeyeballs (2.7.1)
�[2K�[32m\�[0m Collecting aiohttp (3.14.5)
�[2K�[32m\�[0m Auditing aiohttp (3.14.5)
�[2K�[32m\�[0m Collecting aiosignal (1.4.0)
�[2K�[32m\�[0m Auditing aiosignal (1.4.0)
�[2K�[32m\�[0m Collecting ast_serialize (0.13.1)
�[2K�[32m\�[0m Auditing ast_serialize (0.13.1)
�[2K�[32m\�[0m Collecting attrs (26.1.0)
�[2K�[32m\�[0m Auditing attrs (26.1.0)
�[2K�[32m\�[0m Collecting bandit (1.9.4)
�[2K�[32m\�[0m Auditing bandit (1.9.4)
�[2K�[32m\�[0m Collecting boolean.py (5.0)
�[2K�[32m\�[0m Auditing boolean.py (5.0)
�[2K�[32m\�[0m Collecting build (1.6.1)
�[2K�[32m\�[0m Auditing build (1.6.1)
�[2K�[32m\�[0m Collecting CacheControl (0.14.4)
�[2K�[32m\�[0m Auditing CacheControl (0.14.4)
�[2K�[32m\�[0m Collecting certifi (2026.7.22)
�[2K�[32m\�[0m Auditing certifi (2026.7.22)
�[2K�[32m\�[0m Collecting cffi (2.1.1)
�[2K�[32m\�[0m Auditing cffi (2.1.1)
�[2K�[32m\�[0m Collecting charset-normalizer (3.5.2)
�[2K�[32m\�[0m Auditing charset-normalizer (3.5.2)
�[2K�[32m\�[0m Collecting coverage (7.16.2)
�[2K�[32m\�[0m Auditing coverage (7.16.2)
�[2K�[32m\�[0m Collecting cryptography (50.0.2)
�[2K�[32m\�[0m Auditing cryptography (50.0.2)
�[2K�[32m\�[0m Collecting cyclonedx-python-lib (11.12.0)
�[2K�[32m\�[0m Auditing cyclonedx-python-lib (11.12.0)
�[2K�[32m\�[0m Collecting defusedxml (0.7.1)
�[2K�[32m\�[0m Auditing defusedxml (0.7.1)
�[2K�[32m\�[0m Collecting docutils (0.23)
�[2K�[32m\�[0m Auditing docutils (0.23)
�[2K�[32m\�[0m Collecting filelock (4.2.0)
�[2K�[32m\�[0m Auditing filelock (4.2.0)
�[2K�[32m\�[0m Collecting frozenlist (1.8.0)
�[2K�[32m\�[0m Auditing frozenlist (1.8.0)
�[2K�[32m\�[0m Collecting id (1.6.1)
�[2K�[32m\�[0m Auditing id (1.6.1)
�[2K�[32m\�[0m Collecting idna (3.20)
�[2K�[32m\�[0m Auditing idna (3.20)
�[2K�[32m\�[0m Collecting iniconfig (2.3.1)
�[2K�[32m\�[0m Auditing iniconfig (2.3.1)
�[2K�[32m\�[0m Collecting jaraco.classes (3.4.0)
�[2K�[32m\�[0m Auditing jaraco.classes (3.4.0)
�[2K�[32m\�[0m Collecting jaraco.context (6.1.2)
�[2K�[32m\�[0m Auditing jaraco.context (6.1.2)
�[2K�[32m\�[0m Collecting jaraco.functools (4.6.0)
�[2K�[32m\�[0m Auditing jaraco.functools (4.6.0)
�[2K�[32m\�[0m Collecting jeepney (0.9.0)
�[2K�[32m\�[0m Auditing jeepney (0.9.0)
�[2K�[32m\�[0m Collecting keyring (25.7.0)
�[2K�[32m\�[0m Collecting keyring (25.7.0)
�[2K�[32m\�[0m Auditing keyring (25.7.0)
�[2K�[32m\�[0m Collecting librt (0.16.0)
�[2K�[32m\�[0m Auditing librt (0.16.0)
�[2K�[32m\�[0m Collecting license-expression (30.4.4)
�[2K�[32m\�[0m Auditing license-expression (30.4.4)
�[2K�[32m\�[0m Collecting macversiontracker (1.2.0)
�[2K�[32m\�[0m Auditing macversiontracker (1.2.0)
�[2K�[32m\�[0m Collecting markdown-it-py (4.2.0)
�[2K�[32m\�[0m Auditing markdown-it-py (4.2.0)
�[2K�[32m\�[0m Collecting mdurl (0.1.2)
�[2K�[32m\�[0m Auditing mdurl (0.1.2)
�[2K�[32m\�[0m Collecting more-itertools (11.2.1)
�[2K�[32m\�[0m Auditing more-itertools (11.2.1)
�[2K�[32m\�[0m Collecting msgpack (1.2.3)
�[2K�[32m\�[0m Auditing msgpack (1.2.3)
�[2K�[32m\�[0m Collecting multidict (7.1.0)
�[2K�[32m\�[0m Auditing multidict (7.1.0)
�[2K�[32m\�[0m Collecting mypy (2.4.0)
�[2K�[32m\�[0m Auditing mypy (2.4.0)
�[2K�[32m\�[0m Collecting mypy_extensions (1.1.0)
�[2K�[32m\�[0m Auditing mypy_extensions (1.1.0)
�[2K�[32m\�[0m Collecting nh3 (0.3.7)
�[2K�[32m\�[0m Auditing nh3 (0.3.7)
�[2K�[32m\�[0m Collecting packageurl-python (0.17.6)
�[2K�[32m\�[0m Auditing packageurl-python (0.17.6)
�[2K�[32m\�[0m Collecting packaging (26.3)
�[2K�[32m\�[0m Auditing packaging (26.3)
�[2K�[32m\�[0m Collecting pathspec (1.1.1)
�[2K�[32m\�[0m Auditing pathspec (1.1.1)
�[2K�[32m\�[0m Collecting pip (26.2.1)
�[2K�[32m\�[0m Auditing pip (26.2.1)
�[2K�[32m\�[0m Collecting pip_api (0.0.35)
�[2K�[32m\�[0m Auditing pip_api (0.0.35)
�[2K�[32m\�[0m Collecting pip_audit (2.10.1)
�[2K�[32m\�[0m Auditing pip_audit (2.10.1)
�[2K�[32m\�[0m Collecting pip-requirements-parser (32.0.1)
�[2K�[32m\�[0m Auditing pip-requirements-parser (32.0.1)
�[2K�[32m\�[0m Collecting platformdirs (4.13.3)
�[2K�[32m\�[0m Auditing platformdirs (4.13.3)
�[2K�[32m\�[0m Collecting pluggy (1.7.0)
�[2K�[32m\�[0m Auditing pluggy (1.7.0)
�[2K�[32m\�[0m Collecting propcache (0.5.4)
�[2K�[32m\�[0m Auditing propcache (0.5.4)
�[2K�[32m\�[0m Collecting psutil (7.2.2)
�[2K�[32m\�[0m Auditing psutil (7.2.2)
�[2K�[32m\�[0m Collecting py-serializable (2.1.0)
�[2K�[32m\�[0m Auditing py-serializable (2.1.0)
�[2K�[32m\�[0m Collecting pycparser (3.11)
�[2K�[32m\�[0m Auditing pycparser (3.11)
�[2K�[32m\�[0m Collecting Pygments (2.21.0)
�[2K�[32m\�[0m Auditing Pygments (2.21.0)
�[2K�[32m\�[0m Collecting pyparsing (3.3.3)
�[2K�[32m\�[0m Auditing pyparsing (3.3.3)
�[2K�[32m\�[0m Collecting pyproject_hooks (1.3.3)
�[2K�[32m\�[0m Auditing pyproject_hooks (1.3.3)
�[2K�[32m\�[0m Collecting pytest (9.1.1)
�[2K�[32m\�[0m Auditing pytest (9.1.1)
�[2K�[32m\�[0m Collecting pytest-asyncio (1.4.0)
�[2K�[32m\�[0m Auditing pytest-asyncio (1.4.0)
�[2K�[32m\�[0m Collecting pytest-cov (7.1.0)
�[2K�[32m\�[0m Auditing pytest-cov (7.1.0)
�[2K�[32m\�[0m Collecting pytest-mock (3.16.0)
�[2K�[32m\�[0m Auditing pytest-mock (3.16.0)
�[2K�[32m\�[0m Collecting pytest-timeout (2.4.0)
�[2K�[32m\�[0m Auditing pytest-timeout (2.4.0)
�[2K�[32m\�[0m Collecting PyYAML (6.0.3)
�[2K�[32m\�[0m Auditing PyYAML (6.0.3)
�[2K�[32m\�[0m Collecting readme_renderer (46.0)
�[2K�[32m\�[0m Collecting readme_renderer (46.0)
�[2K�[32m\�[0m Auditing readme_renderer (46.0)
�[2K�[32m\�[0m Collecting requests (2.34.2)
�[2K�[32m\�[0m Auditing requests (2.34.2)
�[2K�[32m\�[0m Collecting requests-toolbelt (1.0.0)
�[2K�[32m\�[0m Auditing requests-toolbelt (1.0.0)
�[2K�[32m\�[0m Collecting rfc3986 (2.0.0)
�[2K�[32m\�[0m Auditing rfc3986 (2.0.0)
�[2K�[32m\�[0m Collecting rich (15.0.0)
�[2K�[32m\�[0m Auditing rich (15.0.0)
�[2K�[32m\�[0m Collecting ruff (0.17.0)
�[2K�[32m\�[0m Auditing ruff (0.17.0)
�[2K�[32m\�[0m Collecting SecretStorage (3.5.0)
�[2K�[32m\�[0m Auditing SecretStorage (3.5.0)
�[2K�[32m\�[0m Collecting setuptools (84.0.0)
�[2K�[32m\�[0m Auditing setuptools (84.0.0)
�[2K�[32m\�[0m Collecting sortedcontainers (2.4.0)
�[2K�[32m\�[0m Auditing sortedcontainers (2.4.0)
�[2K�[32m\�[0m Collecting stevedore (5.9.1)
�[2K�[32m\�[0m Auditing stevedore (5.9.1)
�[2K�[32m\�[0m Collecting tabulate (0.10.0)
�[2K�[32m\�[0m Auditing tabulate (0.10.0)
�[2K�[32m\�[0m Collecting termcolor (3.3.0)
�[2K�[32m\�[0m Auditing termcolor (3.3.0)
�[2K�[32m\�[0m Collecting tomli (2.5.0)
�[2K�[32m\�[0m Auditing tomli (2.5.0)
�[2K�[32m\�[0m Collecting tomli_w (1.2.0)
�[2K�[32m\�[0m Auditing tomli_w (1.2.0)
�[2K�[32m\�[0m Collecting tqdm (4.70.1)
�[2K�[32m\�[0m Auditing tqdm (4.70.1)
�[2K�[32m\�[0m Collecting twine (7.0.0)
�[2K�[32m\�[0m Auditing twine (7.0.0)
�[2K�[32m\�[0m Collecting types-PyYAML (6.0.12.20260906)
�[2K�[32m\�[0m Auditing types-PyYAML (6.0.12.20260906)
�[2K�[32m\�[0m Collecting typing_extensions (4.16.0)
�[2K�[32m\�[0m Auditing typing_extensions (4.16.0)
�[2K�[32m\�[0m Collecting urllib3 (2.8.0)
�[2K�[32m\�[0m Auditing urllib3 (2.8.0)
�[2K�[32m\�[0m Collecting wheel (0.48.0)
�[2K�[32m\�[0m Auditing wheel (0.48.0)
�[2K�[32m\�[0m Collecting yarl (1.25.1)
�[2K�[32m\�[0m Auditing yarl (1.25.1)
�[2K�[32m\�[0m Auditing yarl (1.25.1)
�[?25h
�[1A�[2K```

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b5cd3a99bb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/skills/github-release/SKILL.md Outdated
Comment thread .github/skills/github-release/SKILL.md Outdated
Comment thread .github/skills/github-release/SKILL.md Outdated
Comment thread .claude/skills/docs-sync-audit/SKILL.md
Comment thread .github/skills/conventional-commit/SKILL.md Outdated
@codecov

codecov Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path=".github/skills/conventional-commit/SKILL.md" line_range="20-23" />
<code_context>
+2. Run `git diff` or `git diff --cached` to inspect changes.
+3. Stage your changes with `git add <file>`.
+4. Construct your commit message using the following XML structure.
+5. After generating your commit message, Copilot will automatically run the following command in your integrated terminal (no confirmation needed):
+
+```bash
+git commit -m "type(scope): description"
+```
+
</code_context>
<issue_to_address>
**Commit message runs shell commands**

When an attacker-controlled diff influences the generated commit message and the agent executes the suggested command, copilot inserts the generated message into a double-quoted shell command, so shell metacharacters in a message influenced by attacker-controlled changes execute as commands before the commit. This gives those commands the agent's local access.

Pass the message as a separately quoted process argument or through a message file rather than interpolating it into a shell command.
</issue_to_address>

### Comment 2
<location path=".github/skills/github-actions-hardening/references/triggers-and-privilege.md" line_range="13-14" />
<code_context>
+| --- | --- | --- | --- | --- |
+| `push` | Repo collaborators | read/write | yes | Low — trusted authors |
+| `pull_request` (same-repo branch) | Collaborators | read/write | yes | Low |
+| `pull_request` (from a fork) | **Anyone** | **read-only** | **no** | Low by design — even malicious code can't steal anything |
+| `pull_request_target` | **Anyone with a fork** | **read/write** | **yes** | **High** — runs in base-repo context |
+| `workflow_run` | Fires after another workflow | **read/write** | **yes** | **High** |
+| `issue_comment`, `issues` | **Anyone** | **read/write** | **yes** | **High** |
</code_context>
<issue_to_address>
**Trigger labels ignore effective permissions**

When fork PRs are configured to receive write tokens or secrets, or a privileged-trigger workflow/job explicitly restricts its token permissions, the trust guidance assigns privileges solely by trigger: it says fork `pull_request` runs have no secrets and a read-only token, and treats privileged-trigger code execution as CRITICAL. Repository settings can grant write tokens to fork PRs, while workflow or job `permissions` can restrict a privileged-trigger token, so the audit can both suppress real exposure findings and raise false CRITICALs.

Assess repository fork-PR settings and the effective workflow/job permissions before assigning trust or severity, and state assumptions when those settings cannot be inspected.

Also at `.github/skills/github-actions-hardening/SKILL.md:78`, `.github/skills/github-actions-hardening/SKILL.md:138-139`.
</issue_to_address>

### Comment 3
<location path=".github/skills/README.md" line_range="4" />
<code_context>
+# Agent skills
+
+Installed from [github/awesome-copilot](https://github.com/github/awesome-copilot) at commit `cb64ad12e32737ae764becd6885462e9b4c7cb00`.
+Read by Copilot (cloud agent, code review, CLI, VS Code agent mode) and Claude Code.
+
+| Skill | Purpose |
</code_context>
<issue_to_address>
**Claude Code does not load the skills**

When claude Code uses its default project skill discovery, claude Code looks for project skills under `.claude/skills/`, but the README advertises skills installed only under `.github/skills/`; Claude Code ignores them, so users cannot invoke the advertised skills.

Install or link the skills under `.claude/skills/`, or remove the Claude Code support claim.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 3 findings to address first, and the conventional-commit skill tells the agent to stage changes and commit without confirmation, and the release skill can push a branch and open a PR. Those repository changes and any resulting notifications remain after reverting this PR, though they are normally bounded and repairable.

Blocking findings: .github/skills/conventional-commit/SKILL.md:23, .github/skills/github-actions-hardening/references/triggers-and-privilege.md:14, .github/skills/README.md:4


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread .github/skills/conventional-commit/SKILL.md Outdated
Comment thread .github/skills/README.md Outdated
docdyhr and others added 2 commits October 17, 2026 03:35
Claude Code only loads project skills from .claude/skills/, and Copilot
reads that path too, so one copy now serves both. .gitignore re-includes
.claude/skills/ while keeping the rest of .claude/ ignored.

github-release and conventional-commit are removed: they bypass this
repo's own release automation, and conventional-commit commits without
confirmation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CeXGfVNFFTFVtXdALdSQ1g
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CeXGfVNFFTFVtXdALdSQ1g
@github-actions

Copy link
Copy Markdown

🔒 Security Analysis Report

Security Analysis Report

Generated: Sat Oct 17 01:36:38 UTC 2026
Repository: docdyhr/versiontracker
Commit: 17dbfe6

Bandit Security Scan

�[?25l
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m  0%�[0m �[36m-:--:--�[0m
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m  0%�[0m �[36m-:--:--�[0m
�[2KWorking... �[91m━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m  5%�[0m �[36m-:--:--�[0m
�[2KWorking... �[91m━━━━�[0m�[90m╺�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m 11%�[0m �[36m0:00:02�[0m
�[2KWorking... �[91m━━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m 19%�[0m �[36m0:00:02�[0m
�[2KWorking... �[91m━━━━━━━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m 31%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━�[0m�[90m╺�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m 36%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━━━━━━━━━�[0m �[35m 52%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━━�[0m�[90m╺�[0m�[90m━━━━━━━━━━━━━━━�[0m �[35m 61%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━�[0m �[35m 72%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m�[90m╺�[0m�[90m━━━━━━━━�[0m �[35m 78%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m�[90m╺�[0m�[90m━━━━━━�[0m �[35m 83%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m�[91m╸�[0m�[90m━━━━�[0m �[35m 89%�[0m �[36m0:00:01�[0m
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m100%�[0m �[33m0:00:01�[0m
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m100%�[0m �[33m0:00:01�[0m
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m100%�[0m �[33m0:00:01�[0m
�[?25hRun started:2026-10-17 01:36:40.261267+00:00

Test results:
>> Issue: [B608:hardcoded_sql_expressions] Possible SQL injection vector through string-based query construction.
   Severity: Medium   Confidence: Low
   CWE: CWE-89 (https://cwe.mitre.org/data/definitions/89.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b608_hardcoded_sql_expressions.html
   Location: versiontracker/advanced_cache.py:693:24
692	                # Use f-string for better readability
693	                msg = f"Failed to delete from cache {key}: {e}"
694	                raise CacheError(msg) from e

--------------------------------------------------
>> Issue: [B105:hardcoded_password_string] Possible hardcoded password: 'homebrew_token'
   Severity: Low   Confidence: Medium
   CWE: CWE-259 (https://cwe.mitre.org/data/definitions/259.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b105_hardcoded_password_string.html
   Location: versiontracker/audit/models.py:161:21
160	    BUNDLE_ID = "bundle_id"
161	    HOMEBREW_TOKEN = "homebrew_token"
162	    DISPLAY_NAME = "display_name"

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/config.py:174:16
173	                cmd = f"{path} --version"
174	                import subprocess
175	

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/config.py:177:29
176	                try:
177	                    result = subprocess.run(cmd.split(), capture_output=True, timeout=2, check=False)
178	                    returncode = result.returncode

--------------------------------------------------
>> Issue: [B110:try_except_pass] Try, Except, Pass detected.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b110_try_except_pass.html
   Location: versiontracker/experimental/analytics.py:645:16
644	                    self.peak_cpu = max(self.peak_cpu, cpu_percent)
645	                except Exception:
646	                    pass
647	                time.sleep(0.05)

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: versiontracker/handlers/outdated_handlers.py:518:8
517	        # Type assertion: apps and brews cannot be None here due to exit_code checks above
518	        assert apps is not None
519	        assert brews is not None

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: versiontracker/handlers/outdated_handlers.py:519:8
518	        assert apps is not None
519	        assert brews is not None
520	        apps = _filter_applications(apps, brews, include_brews)

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: versiontracker/handlers/outdated_handlers.py:539:8
538	        # Type assertion: outdated_info cannot be None here due to exit_code check
539	        assert outdated_info is not None
540	        # Type cast: outdated_info cannot be None here due to exit_code check above

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/homebrew.py:12:0
11	import re
12	import subprocess
13	import time

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/macos_integration.py:12:0
11	import plistlib
12	import subprocess
13	from pathlib import Path

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/menubar_app.py:8:0
7	import logging
8	import subprocess
9	import sys

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/plugins/example_plugins.py:322:16
321	            try:
322	                import subprocess
323	

--------------------------------------------------
>> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b607_start_process_with_partial_path.html
   Location: versiontracker/plugins/example_plugins.py:324:25
323	
324	                result = subprocess.run(["brew", "--version"], capture_output=True, text=True, timeout=5)
325	                if result.returncode == 0:

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/plugins/example_plugins.py:324:25
323	
324	                result = subprocess.run(["brew", "--version"], capture_output=True, text=True, timeout=5)
325	                if result.returncode == 0:

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/utils.py:16:0
15	import shutil
16	import subprocess
17	import sys

--------------------------------------------------
>> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b607_start_process_with_partial_path.html
   Location: versiontracker/utils.py:804:17
803	    try:
804	        result = subprocess.run(["which", "brew"], capture_output=True, text=True, timeout=5)
805	        return result.returncode == 0

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/utils.py:804:17
803	    try:
804	        result = subprocess.run(["which", "brew"], capture_output=True, text=True, timeout=5)
805	        return result.returncode == 0

--------------------------------------------------
>> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b607_start_process_with_partial_path.html
   Location: versiontracker/utils.py:820:17
819	    try:
820	        result = subprocess.run(["brew", "--prefix"], capture_output=True, text=True, timeout=5)
821	        if result.returncode == 0:

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/utils.py:820:17
819	    try:
820	        result = subprocess.run(["brew", "--prefix"], capture_output=True, text=True, timeout=5)
821	        if result.returncode == 0:

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/utils.py:853:15
852	    try:
853	        return subprocess.run(command, capture_output=True, text=True, timeout=timeout, check=check)
854	    except subprocess.TimeoutExpired as e:

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/version/batch.py:11:0
10	import logging
11	import subprocess
12	from concurrent.futures import ThreadPoolExecutor

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/version/homebrew.py:18:0
17	import logging
18	import subprocess
19	

--------------------------------------------------

Code scanned:
	Total lines of code: 17221
	Total lines skipped (#nosec): 0
	Total potential issues skipped due to specifically being disabled (e.g., #nosec BXXX): 30

Run metrics:
	Total issues (by severity):
		Undefined: 0
		Low: 21
		Medium: 1
		High: 0
	Total issues (by confidence):
		Undefined: 0
		Low: 1
		Medium: 1
		High: 20
Files skipped (0):

Pip-Audit Results

�[?25l�[32m-�[0m Collecting inputs
�[2K�[32m-�[0m Collecting inputs
�[2K�[32m-�[0m Collecting inputs
�[2K�[32m-�[0m Collecting inputs
�[2K�[32m\�[0m Collecting inputs
�[2K�[32m\�[0m Collecting inputs
�[2K�[32m\�[0m Collecting inputs
�[2K�[32m\�[0m Collecting aiohappyeyeballs (2.7.1)
�[2K�[32m\�[0m Auditing aiohappyeyeballs (2.7.1)
�[2K�[32m\�[0m Collecting aiohttp (3.14.5)
�[2K�[32m\�[0m Auditing aiohttp (3.14.5)
�[2K�[32m\�[0m Collecting aiosignal (1.4.0)
�[2K�[32m\�[0m Auditing aiosignal (1.4.0)
�[2K�[32m\�[0m Collecting ast_serialize (0.13.1)
�[2K�[32m\�[0m Auditing ast_serialize (0.13.1)
�[2K�[32m\�[0m Collecting attrs (26.1.0)
�[2K�[32m\�[0m Auditing attrs (26.1.0)
�[2K�[32m\�[0m Collecting bandit (1.9.4)
�[2K�[32m\�[0m Auditing bandit (1.9.4)
�[2K�[32m\�[0m Collecting boolean.py (5.0)
�[2K�[32m\�[0m Auditing boolean.py (5.0)
�[2K�[32m\�[0m Collecting build (1.6.1)
�[2K�[32m\�[0m Auditing build (1.6.1)
�[2K�[32m\�[0m Collecting CacheControl (0.14.4)
�[2K�[32m\�[0m Auditing CacheControl (0.14.4)
�[2K�[32m\�[0m Collecting certifi (2026.7.22)
�[2K�[32m\�[0m Auditing certifi (2026.7.22)
�[2K�[32m\�[0m Collecting cffi (2.1.1)
�[2K�[32m\�[0m Auditing cffi (2.1.1)
�[2K�[32m\�[0m Collecting charset-normalizer (3.5.2)
�[2K�[32m\�[0m Collecting charset-normalizer (3.5.2)
�[2K�[32m\�[0m Auditing charset-normalizer (3.5.2)
�[2K�[32m\�[0m Collecting coverage (7.16.2)
�[2K�[32m\�[0m Auditing coverage (7.16.2)
�[2K�[32m\�[0m Collecting cryptography (50.0.2)
�[2K�[32m\�[0m Auditing cryptography (50.0.2)
�[2K�[32m\�[0m Collecting cyclonedx-python-lib (11.12.0)
�[2K�[32m\�[0m Auditing cyclonedx-python-lib (11.12.0)
�[2K�[32m\�[0m Collecting defusedxml (0.7.1)
�[2K�[32m\�[0m Auditing defusedxml (0.7.1)
�[2K�[32m\�[0m Collecting docutils (0.23)
�[2K�[32m\�[0m Auditing docutils (0.23)
�[2K�[32m\�[0m Collecting filelock (4.2.0)
�[2K�[32m\�[0m Auditing filelock (4.2.0)
�[2K�[32m\�[0m Collecting frozenlist (1.8.0)
�[2K�[32m\�[0m Auditing frozenlist (1.8.0)
�[2K�[32m\�[0m Collecting id (1.6.1)
�[2K�[32m\�[0m Auditing id (1.6.1)
�[2K�[32m\�[0m Collecting idna (3.20)
�[2K�[32m\�[0m Auditing idna (3.20)
�[2K�[32m\�[0m Collecting iniconfig (2.3.1)
�[2K�[32m\�[0m Auditing iniconfig (2.3.1)
�[2K�[32m\�[0m Collecting jaraco.classes (3.4.0)
�[2K�[32m\�[0m Auditing jaraco.classes (3.4.0)
�[2K�[32m\�[0m Collecting jaraco.context (6.1.2)
�[2K�[32m\�[0m Auditing jaraco.context (6.1.2)
�[2K�[32m\�[0m Collecting jaraco.functools (4.6.0)
�[2K�[32m\�[0m Auditing jaraco.functools (4.6.0)
�[2K�[32m\�[0m Collecting jeepney (0.9.0)
�[2K�[32m|�[0m Auditing jeepney (0.9.0)
�[2K�[32m|�[0m Collecting keyring (25.7.0)
�[2K�[32m|�[0m Auditing keyring (25.7.0)
�[2K�[32m|�[0m Collecting librt (0.16.0)
�[2K�[32m|�[0m Auditing librt (0.16.0)
�[2K�[32m|�[0m Collecting license-expression (30.4.4)
�[2K�[32m|�[0m Auditing license-expression (30.4.4)
�[2K�[32m|�[0m Collecting macversiontracker (1.2.0)
�[2K�[32m|�[0m Auditing macversiontracker (1.2.0)
�[2K�[32m|�[0m Collecting markdown-it-py (4.2.0)
�[2K�[32m|�[0m Auditing markdown-it-py (4.2.0)
�[2K�[32m|�[0m Collecting mdurl (0.1.2)
�[2K�[32m|�[0m Auditing mdurl (0.1.2)
�[2K�[32m|�[0m Collecting more-itertools (11.2.1)
�[2K�[32m|�[0m Auditing more-itertools (11.2.1)
�[2K�[32m|�[0m Collecting msgpack (1.2.3)
�[2K�[32m|�[0m Auditing msgpack (1.2.3)
�[2K�[32m|�[0m Collecting multidict (7.1.0)
�[2K�[32m|�[0m Collecting multidict (7.1.0)
�[2K�[32m|�[0m Auditing multidict (7.1.0)
�[2K�[32m|�[0m Collecting mypy (2.4.0)
�[2K�[32m|�[0m Auditing mypy (2.4.0)
�[2K�[32m|�[0m Collecting mypy_extensions (1.1.0)
�[2K�[32m|�[0m Auditing mypy_extensions (1.1.0)
�[2K�[32m|�[0m Collecting nh3 (0.3.7)
�[2K�[32m|�[0m Auditing nh3 (0.3.7)
�[2K�[32m|�[0m Collecting packageurl-python (0.17.6)
�[2K�[32m|�[0m Auditing packageurl-python (0.17.6)
�[2K�[32m|�[0m Collecting packaging (26.3)
�[2K�[32m|�[0m Auditing packaging (26.3)
�[2K�[32m|�[0m Collecting pathspec (1.1.1)
�[2K�[32m|�[0m Auditing pathspec (1.1.1)
�[2K�[32m|�[0m Collecting pip (26.2.1)
�[2K�[32m|�[0m Auditing pip (26.2.1)
�[2K�[32m|�[0m Collecting pip_api (0.0.35)
�[2K�[32m|�[0m Auditing pip_api (0.0.35)
�[2K�[32m|�[0m Collecting pip_audit (2.10.1)
�[2K�[32m|�[0m Auditing pip_audit (2.10.1)
�[2K�[32m|�[0m Collecting pip-requirements-parser (32.0.1)
�[2K�[32m|�[0m Auditing pip-requirements-parser (32.0.1)
�[2K�[32m|�[0m Collecting platformdirs (4.13.3)
�[2K�[32m|�[0m Auditing platformdirs (4.13.3)
�[2K�[32m|�[0m Collecting pluggy (1.7.0)
�[2K�[32m|�[0m Auditing pluggy (1.7.0)
�[2K�[32m|�[0m Collecting propcache (0.5.4)
�[2K�[32m|�[0m Auditing propcache (0.5.4)
�[2K�[32m|�[0m Collecting psutil (7.2.2)
�[2K�[32m|�[0m Auditing psutil (7.2.2)
�[2K�[32m|�[0m Collecting py-serializable (2.1.0)
�[2K�[32m|�[0m Auditing py-serializable (2.1.0)
�[2K�[32m|�[0m Collecting pycparser (3.11)
�[2K�[32m|�[0m Auditing pycparser (3.11)
�[2K�[32m|�[0m Collecting Pygments (2.21.0)
�[2K�[32m|�[0m Auditing Pygments (2.21.0)
�[2K�[32m|�[0m Collecting pyparsing (3.3.3)
�[2K�[32m|�[0m Auditing pyparsing (3.3.3)
�[2K�[32m|�[0m Collecting pyproject_hooks (1.3.3)
�[2K�[32m|�[0m Auditing pyproject_hooks (1.3.3)
�[2K�[32m|�[0m Collecting pytest (9.1.1)
�[2K�[32m|�[0m Auditing pytest (9.1.1)
�[2K�[32m|�[0m Collecting pytest-asyncio (1.4.0)
�[2K�[32m|�[0m Auditing pytest-asyncio (1.4.0)
�[2K�[32m|�[0m Collecting pytest-cov (7.1.0)
�[2K�[32m|�[0m Auditing pytest-cov (7.1.0)
�[2K�[32m|�[0m Collecting pytest-mock (3.16.0)
�[2K�[32m|�[0m Auditing pytest-mock (3.16.0)
�[2K�[32m|�[0m Collecting pytest-timeout (2.4.0)
�[2K�[32m|�[0m Auditing pytest-timeout (2.4.0)
�[2K�[32m|�[0m Collecting PyYAML (6.0.3)
�[2K�[32m|�[0m Auditing PyYAML (6.0.3)
�[2K�[32m|�[0m Collecting readme_renderer (46.0)
�[2K�[32m|�[0m Collecting readme_renderer (46.0)
�[2K�[32m|�[0m Auditing readme_renderer (46.0)
�[2K�[32m|�[0m Collecting requests (2.34.2)
�[2K�[32m|�[0m Auditing requests (2.34.2)
�[2K�[32m|�[0m Collecting requests-toolbelt (1.0.0)
�[2K�[32m|�[0m Auditing requests-toolbelt (1.0.0)
�[2K�[32m|�[0m Collecting rfc3986 (2.0.0)
�[2K�[32m|�[0m Auditing rfc3986 (2.0.0)
�[2K�[32m|�[0m Collecting rich (15.0.0)
�[2K�[32m|�[0m Auditing rich (15.0.0)
�[2K�[32m|�[0m Collecting ruff (0.17.0)
�[2K�[32m|�[0m Auditing ruff (0.17.0)
�[2K�[32m|�[0m Collecting SecretStorage (3.5.0)
�[2K�[32m|�[0m Auditing SecretStorage (3.5.0)
�[2K�[32m|�[0m Collecting setuptools (84.0.0)
�[2K�[32m|�[0m Auditing setuptools (84.0.0)
�[2K�[32m|�[0m Collecting sortedcontainers (2.4.0)
�[2K�[32m|�[0m Auditing sortedcontainers (2.4.0)
�[2K�[32m|�[0m Collecting stevedore (5.9.1)
�[2K�[32m|�[0m Auditing stevedore (5.9.1)
�[2K�[32m|�[0m Collecting tabulate (0.10.0)
�[2K�[32m|�[0m Auditing tabulate (0.10.0)
�[2K�[32m|�[0m Collecting termcolor (3.3.0)
�[2K�[32m|�[0m Auditing termcolor (3.3.0)
�[2K�[32m|�[0m Collecting tomli (2.5.0)
�[2K�[32m|�[0m Auditing tomli (2.5.0)
�[2K�[32m|�[0m Collecting tomli_w (1.2.0)
�[2K�[32m|�[0m Auditing tomli_w (1.2.0)
�[2K�[32m|�[0m Collecting tqdm (4.70.1)
�[2K�[32m|�[0m Auditing tqdm (4.70.1)
�[2K�[32m|�[0m Collecting twine (7.0.0)
�[2K�[32m|�[0m Auditing twine (7.0.0)
�[2K�[32m|�[0m Collecting types-PyYAML (6.0.12.20260906)
�[2K�[32m|�[0m Auditing types-PyYAML (6.0.12.20260906)
�[2K�[32m|�[0m Collecting typing_extensions (4.16.0)
�[2K�[32m|�[0m Auditing typing_extensions (4.16.0)
�[2K�[32m|�[0m Collecting urllib3 (2.8.0)
�[2K�[32m|�[0m Auditing urllib3 (2.8.0)
�[2K�[32m|�[0m Collecting wheel (0.48.0)
�[2K�[32m|�[0m Auditing wheel (0.48.0)
�[2K�[32m|�[0m Collecting yarl (1.25.1)
�[2K�[32m|�[0m Auditing yarl (1.25.1)
�[2K�[32m|�[0m Auditing yarl (1.25.1)
�[?25h
�[1A�[2K```

@github-actions

Copy link
Copy Markdown

🔒 Security Analysis Report

Security Analysis Report

Generated: Sat Oct 17 01:37:35 UTC 2026
Repository: docdyhr/versiontracker
Commit: 0d43544

Bandit Security Scan

�[?25l
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m  0%�[0m �[36m-:--:--�[0m
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m  0%�[0m �[36m-:--:--�[0m
�[2KWorking... �[91m━━━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m  9%�[0m �[36m0:00:03�[0m
�[2KWorking... �[91m━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m 17%�[0m �[36m0:00:02�[0m
�[2KWorking... �[91m━━━━━━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m 30%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━�[0m�[90m╺�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m 36%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━�[0m�[90m╺�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m 41%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━�[0m�[90m╺�[0m�[90m━━━━━━━━━━━━━━━━━━━━�[0m �[35m 48%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━━━━━━�[0m �[35m 59%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━━━�[0m �[35m 67%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━━━━�[0m �[35m 77%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━━�[0m �[35m 81%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m�[90m╺�[0m�[90m━━━━━�[0m �[35m 86%�[0m �[36m0:00:01�[0m
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m100%�[0m �[33m0:00:01�[0m
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m100%�[0m �[33m0:00:01�[0m
�[?25hRun started:2026-10-17 01:37:37.067867+00:00

Test results:
>> Issue: [B608:hardcoded_sql_expressions] Possible SQL injection vector through string-based query construction.
   Severity: Medium   Confidence: Low
   CWE: CWE-89 (https://cwe.mitre.org/data/definitions/89.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b608_hardcoded_sql_expressions.html
   Location: versiontracker/advanced_cache.py:693:24
692	                # Use f-string for better readability
693	                msg = f"Failed to delete from cache {key}: {e}"
694	                raise CacheError(msg) from e

--------------------------------------------------
>> Issue: [B105:hardcoded_password_string] Possible hardcoded password: 'homebrew_token'
   Severity: Low   Confidence: Medium
   CWE: CWE-259 (https://cwe.mitre.org/data/definitions/259.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b105_hardcoded_password_string.html
   Location: versiontracker/audit/models.py:161:21
160	    BUNDLE_ID = "bundle_id"
161	    HOMEBREW_TOKEN = "homebrew_token"
162	    DISPLAY_NAME = "display_name"

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/config.py:174:16
173	                cmd = f"{path} --version"
174	                import subprocess
175	

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/config.py:177:29
176	                try:
177	                    result = subprocess.run(cmd.split(), capture_output=True, timeout=2, check=False)
178	                    returncode = result.returncode

--------------------------------------------------
>> Issue: [B110:try_except_pass] Try, Except, Pass detected.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b110_try_except_pass.html
   Location: versiontracker/experimental/analytics.py:645:16
644	                    self.peak_cpu = max(self.peak_cpu, cpu_percent)
645	                except Exception:
646	                    pass
647	                time.sleep(0.05)

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: versiontracker/handlers/outdated_handlers.py:518:8
517	        # Type assertion: apps and brews cannot be None here due to exit_code checks above
518	        assert apps is not None
519	        assert brews is not None

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: versiontracker/handlers/outdated_handlers.py:519:8
518	        assert apps is not None
519	        assert brews is not None
520	        apps = _filter_applications(apps, brews, include_brews)

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: versiontracker/handlers/outdated_handlers.py:539:8
538	        # Type assertion: outdated_info cannot be None here due to exit_code check
539	        assert outdated_info is not None
540	        # Type cast: outdated_info cannot be None here due to exit_code check above

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/homebrew.py:12:0
11	import re
12	import subprocess
13	import time

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/macos_integration.py:12:0
11	import plistlib
12	import subprocess
13	from pathlib import Path

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/menubar_app.py:8:0
7	import logging
8	import subprocess
9	import sys

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/plugins/example_plugins.py:322:16
321	            try:
322	                import subprocess
323	

--------------------------------------------------
>> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b607_start_process_with_partial_path.html
   Location: versiontracker/plugins/example_plugins.py:324:25
323	
324	                result = subprocess.run(["brew", "--version"], capture_output=True, text=True, timeout=5)
325	                if result.returncode == 0:

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/plugins/example_plugins.py:324:25
323	
324	                result = subprocess.run(["brew", "--version"], capture_output=True, text=True, timeout=5)
325	                if result.returncode == 0:

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/utils.py:16:0
15	import shutil
16	import subprocess
17	import sys

--------------------------------------------------
>> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b607_start_process_with_partial_path.html
   Location: versiontracker/utils.py:804:17
803	    try:
804	        result = subprocess.run(["which", "brew"], capture_output=True, text=True, timeout=5)
805	        return result.returncode == 0

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/utils.py:804:17
803	    try:
804	        result = subprocess.run(["which", "brew"], capture_output=True, text=True, timeout=5)
805	        return result.returncode == 0

--------------------------------------------------
>> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b607_start_process_with_partial_path.html
   Location: versiontracker/utils.py:820:17
819	    try:
820	        result = subprocess.run(["brew", "--prefix"], capture_output=True, text=True, timeout=5)
821	        if result.returncode == 0:

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/utils.py:820:17
819	    try:
820	        result = subprocess.run(["brew", "--prefix"], capture_output=True, text=True, timeout=5)
821	        if result.returncode == 0:

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/utils.py:853:15
852	    try:
853	        return subprocess.run(command, capture_output=True, text=True, timeout=timeout, check=check)
854	    except subprocess.TimeoutExpired as e:

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/version/batch.py:11:0
10	import logging
11	import subprocess
12	from concurrent.futures import ThreadPoolExecutor

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/version/homebrew.py:18:0
17	import logging
18	import subprocess
19	

--------------------------------------------------

Code scanned:
	Total lines of code: 17221
	Total lines skipped (#nosec): 0
	Total potential issues skipped due to specifically being disabled (e.g., #nosec BXXX): 30

Run metrics:
	Total issues (by severity):
		Undefined: 0
		Low: 21
		Medium: 1
		High: 0
	Total issues (by confidence):
		Undefined: 0
		Low: 1
		Medium: 1
		High: 20
Files skipped (0):

Pip-Audit Results

�[?25l�[32m-�[0m Collecting inputs
�[2K�[32m-�[0m Collecting inputs
�[2K�[32m-�[0m Collecting inputs
�[2K�[32m-�[0m Collecting inputs
�[2K�[32m\�[0m Collecting inputs
�[2K�[32m\�[0m Collecting inputs
�[2K�[32m\�[0m Collecting inputs
�[2K�[32m\�[0m Collecting inputs
�[2K�[32m\�[0m Collecting aiohappyeyeballs (2.7.1)
�[2K�[32m\�[0m Auditing aiohappyeyeballs (2.7.1)
�[2K�[32m\�[0m Collecting aiohttp (3.14.5)
�[2K�[32m\�[0m Auditing aiohttp (3.14.5)
�[2K�[32m\�[0m Collecting aiosignal (1.4.0)
�[2K�[32m\�[0m Auditing aiosignal (1.4.0)
�[2K�[32m\�[0m Collecting ast_serialize (0.13.1)
�[2K�[32m\�[0m Auditing ast_serialize (0.13.1)
�[2K�[32m\�[0m Collecting attrs (26.1.0)
�[2K�[32m\�[0m Auditing attrs (26.1.0)
�[2K�[32m\�[0m Collecting bandit (1.9.4)
�[2K�[32m\�[0m Auditing bandit (1.9.4)
�[2K�[32m\�[0m Collecting boolean.py (5.0)
�[2K�[32m\�[0m Auditing boolean.py (5.0)
�[2K�[32m|�[0m Collecting build (1.6.1)
�[2K�[32m|�[0m Auditing build (1.6.1)
�[2K�[32m|�[0m Collecting CacheControl (0.14.4)
�[2K�[32m|�[0m Auditing CacheControl (0.14.4)
�[2K�[32m|�[0m Collecting certifi (2026.7.22)
�[2K�[32m|�[0m Auditing certifi (2026.7.22)
�[2K�[32m|�[0m Collecting cffi (2.1.1)
�[2K�[32m|�[0m Auditing cffi (2.1.1)
�[2K�[32m|�[0m Collecting charset-normalizer (3.5.2)
�[2K�[32m|�[0m Auditing charset-normalizer (3.5.2)
�[2K�[32m|�[0m Collecting coverage (7.16.2)
�[2K�[32m|�[0m Collecting coverage (7.16.2)
�[2K�[32m|�[0m Auditing coverage (7.16.2)
�[2K�[32m|�[0m Collecting cryptography (50.0.2)
�[2K�[32m|�[0m Auditing cryptography (50.0.2)
�[2K�[32m|�[0m Collecting cyclonedx-python-lib (11.12.0)
�[2K�[32m|�[0m Auditing cyclonedx-python-lib (11.12.0)
�[2K�[32m|�[0m Collecting defusedxml (0.7.1)
�[2K�[32m|�[0m Auditing defusedxml (0.7.1)
�[2K�[32m|�[0m Collecting docutils (0.23)
�[2K�[32m|�[0m Auditing docutils (0.23)
�[2K�[32m|�[0m Collecting filelock (4.2.0)
�[2K�[32m|�[0m Auditing filelock (4.2.0)
�[2K�[32m|�[0m Collecting frozenlist (1.8.0)
�[2K�[32m|�[0m Auditing frozenlist (1.8.0)
�[2K�[32m|�[0m Collecting id (1.6.1)
�[2K�[32m|�[0m Auditing id (1.6.1)
�[2K�[32m|�[0m Collecting idna (3.20)
�[2K�[32m|�[0m Auditing idna (3.20)
�[2K�[32m|�[0m Collecting iniconfig (2.3.1)
�[2K�[32m|�[0m Auditing iniconfig (2.3.1)
�[2K�[32m|�[0m Collecting jaraco.classes (3.4.0)
�[2K�[32m|�[0m Auditing jaraco.classes (3.4.0)
�[2K�[32m|�[0m Collecting jaraco.context (6.1.2)
�[2K�[32m|�[0m Auditing jaraco.context (6.1.2)
�[2K�[32m|�[0m Collecting jaraco.functools (4.6.0)
�[2K�[32m|�[0m Auditing jaraco.functools (4.6.0)
�[2K�[32m|�[0m Collecting jeepney (0.9.0)
�[2K�[32m|�[0m Auditing jeepney (0.9.0)
�[2K�[32m|�[0m Collecting keyring (25.7.0)
�[2K�[32m|�[0m Auditing keyring (25.7.0)
�[2K�[32m|�[0m Collecting librt (0.16.0)
�[2K�[32m|�[0m Auditing librt (0.16.0)
�[2K�[32m|�[0m Collecting license-expression (30.4.4)
�[2K�[32m|�[0m Auditing license-expression (30.4.4)
�[2K�[32m|�[0m Collecting macversiontracker (1.2.0)
�[2K�[32m|�[0m Auditing macversiontracker (1.2.0)
�[2K�[32m|�[0m Collecting markdown-it-py (4.2.0)
�[2K�[32m|�[0m Auditing markdown-it-py (4.2.0)
�[2K�[32m|�[0m Collecting mdurl (0.1.2)
�[2K�[32m|�[0m Auditing mdurl (0.1.2)
�[2K�[32m|�[0m Collecting more-itertools (11.2.1)
�[2K�[32m|�[0m Auditing more-itertools (11.2.1)
�[2K�[32m|�[0m Collecting msgpack (1.2.3)
�[2K�[32m|�[0m Auditing msgpack (1.2.3)
�[2K�[32m|�[0m Collecting multidict (7.1.0)
�[2K�[32m|�[0m Auditing multidict (7.1.0)
�[2K�[32m|�[0m Collecting mypy (2.4.0)
�[2K�[32m|�[0m Collecting mypy (2.4.0)
�[2K�[32m|�[0m Auditing mypy (2.4.0)
�[2K�[32m|�[0m Collecting mypy_extensions (1.1.0)
�[2K�[32m|�[0m Auditing mypy_extensions (1.1.0)
�[2K�[32m|�[0m Collecting nh3 (0.3.7)
�[2K�[32m|�[0m Auditing nh3 (0.3.7)
�[2K�[32m|�[0m Collecting packageurl-python (0.17.6)
�[2K�[32m|�[0m Auditing packageurl-python (0.17.6)
�[2K�[32m|�[0m Collecting packaging (26.3)
�[2K�[32m|�[0m Auditing packaging (26.3)
�[2K�[32m|�[0m Collecting pathspec (1.1.1)
�[2K�[32m|�[0m Auditing pathspec (1.1.1)
�[2K�[32m|�[0m Collecting pip (26.2.1)
�[2K�[32m|�[0m Auditing pip (26.2.1)
�[2K�[32m|�[0m Collecting pip_api (0.0.35)
�[2K�[32m|�[0m Auditing pip_api (0.0.35)
�[2K�[32m|�[0m Collecting pip_audit (2.10.1)
�[2K�[32m|�[0m Auditing pip_audit (2.10.1)
�[2K�[32m|�[0m Collecting pip-requirements-parser (32.0.1)
�[2K�[32m|�[0m Auditing pip-requirements-parser (32.0.1)
�[2K�[32m|�[0m Collecting platformdirs (4.13.3)
�[2K�[32m|�[0m Auditing platformdirs (4.13.3)
�[2K�[32m|�[0m Collecting pluggy (1.7.0)
�[2K�[32m|�[0m Auditing pluggy (1.7.0)
�[2K�[32m|�[0m Collecting propcache (0.5.4)
�[2K�[32m|�[0m Auditing propcache (0.5.4)
�[2K�[32m|�[0m Collecting psutil (7.2.2)
�[2K�[32m|�[0m Auditing psutil (7.2.2)
�[2K�[32m|�[0m Collecting py-serializable (2.1.0)
�[2K�[32m|�[0m Auditing py-serializable (2.1.0)
�[2K�[32m|�[0m Collecting pycparser (3.11)
�[2K�[32m|�[0m Auditing pycparser (3.11)
�[2K�[32m|�[0m Collecting Pygments (2.21.0)
�[2K�[32m|�[0m Auditing Pygments (2.21.0)
�[2K�[32m|�[0m Collecting pyparsing (3.3.3)
�[2K�[32m|�[0m Auditing pyparsing (3.3.3)
�[2K�[32m|�[0m Collecting pyproject_hooks (1.3.3)
�[2K�[32m|�[0m Auditing pyproject_hooks (1.3.3)
�[2K�[32m|�[0m Collecting pytest (9.1.1)
�[2K�[32m|�[0m Auditing pytest (9.1.1)
�[2K�[32m|�[0m Collecting pytest-asyncio (1.4.0)
�[2K�[32m|�[0m Auditing pytest-asyncio (1.4.0)
�[2K�[32m|�[0m Collecting pytest-cov (7.1.0)
�[2K�[32m|�[0m Auditing pytest-cov (7.1.0)
�[2K�[32m|�[0m Collecting pytest-mock (3.16.0)
�[2K�[32m|�[0m Auditing pytest-mock (3.16.0)
�[2K�[32m|�[0m Collecting pytest-timeout (2.4.0)
�[2K�[32m|�[0m Auditing pytest-timeout (2.4.0)
�[2K�[32m|�[0m Collecting PyYAML (6.0.3)
�[2K�[32m|�[0m Auditing PyYAML (6.0.3)
�[2K�[32m|�[0m Collecting readme_renderer (46.0)
�[2K�[32m|�[0m Collecting readme_renderer (46.0)
�[2K�[32m|�[0m Auditing readme_renderer (46.0)
�[2K�[32m|�[0m Collecting requests (2.34.2)
�[2K�[32m|�[0m Auditing requests (2.34.2)
�[2K�[32m|�[0m Collecting requests-toolbelt (1.0.0)
�[2K�[32m|�[0m Auditing requests-toolbelt (1.0.0)
�[2K�[32m|�[0m Collecting rfc3986 (2.0.0)
�[2K�[32m|�[0m Auditing rfc3986 (2.0.0)
�[2K�[32m|�[0m Collecting rich (15.0.0)
�[2K�[32m|�[0m Auditing rich (15.0.0)
�[2K�[32m|�[0m Collecting ruff (0.17.0)
�[2K�[32m|�[0m Auditing ruff (0.17.0)
�[2K�[32m|�[0m Collecting SecretStorage (3.5.0)
�[2K�[32m|�[0m Auditing SecretStorage (3.5.0)
�[2K�[32m|�[0m Collecting setuptools (84.0.0)
�[2K�[32m|�[0m Auditing setuptools (84.0.0)
�[2K�[32m|�[0m Collecting sortedcontainers (2.4.0)
�[2K�[32m|�[0m Auditing sortedcontainers (2.4.0)
�[2K�[32m|�[0m Collecting stevedore (5.9.1)
�[2K�[32m|�[0m Auditing stevedore (5.9.1)
�[2K�[32m|�[0m Collecting tabulate (0.10.0)
�[2K�[32m|�[0m Auditing tabulate (0.10.0)
�[2K�[32m|�[0m Collecting termcolor (3.3.0)
�[2K�[32m|�[0m Auditing termcolor (3.3.0)
�[2K�[32m|�[0m Collecting tomli (2.5.0)
�[2K�[32m|�[0m Auditing tomli (2.5.0)
�[2K�[32m|�[0m Collecting tomli_w (1.2.0)
�[2K�[32m|�[0m Auditing tomli_w (1.2.0)
�[2K�[32m|�[0m Collecting tqdm (4.70.1)
�[2K�[32m|�[0m Auditing tqdm (4.70.1)
�[2K�[32m|�[0m Collecting twine (7.0.0)
�[2K�[32m|�[0m Auditing twine (7.0.0)
�[2K�[32m|�[0m Collecting types-PyYAML (6.0.12.20260906)
�[2K�[32m|�[0m Auditing types-PyYAML (6.0.12.20260906)
�[2K�[32m|�[0m Collecting typing_extensions (4.16.0)
�[2K�[32m|�[0m Auditing typing_extensions (4.16.0)
�[2K�[32m|�[0m Collecting urllib3 (2.8.0)
�[2K�[32m|�[0m Auditing urllib3 (2.8.0)
�[2K�[32m|�[0m Collecting wheel (0.48.0)
�[2K�[32m|�[0m Auditing wheel (0.48.0)
�[2K�[32m|�[0m Collecting yarl (1.25.1)
�[2K�[32m|�[0m Auditing yarl (1.25.1)
�[2K�[32m|�[0m Auditing yarl (1.25.1)
�[?25h
�[1A�[2K```

…ils, drop pytest-coverage

- .gitignore: `.claude/*` only matched the repo root, so nested `.claude/`
  folders (tests/.claude/settings.json, docs/**/.claude/worktrees/...) became
  trackable. Use `**/.claude/*` and re-include only `/.claude/skills/`
  (verified with core.ignorecase on and off).
- .pre-commit-config.yaml: top-level `exclude: '^\.claude/skills/(?!README\.md$)'`
  so markdownlint --fix / end-of-file-fixer cannot rewrite (74 fixable) or
  reject (82 unfixable) the verbatim upstream copy; the repo-owned README
  stays linted.
- CLAUDE.md "Vendored Agent Skills" + AGENTS.md "Agent Skills": repo rules
  win over the skills; no PR merges/approvals/auto-merge or @dependabot
  commands (the dependabot skill suggests `gh pr merge --auto`), no installs,
  report-only audits, the 85% coverage floor and --maxfail CLI override.
- Drop pytest-coverage: its 100% target and loop conflict with the 85% floor
  and the ML-extras skips.
- README.md rewritten (it failed markdownlint) and records what is not
  vendored, including the docs-sync-audit/test-gap-audit helper scripts that
  `.gitignore`'s `[Ss]cripts` rule had silently left out.
- AGENTS.md: correct the stale "pytest.ini … 70% baseline" line.
- CHANGELOG: Unreleased → Added entry.

Remaining vendored files unchanged (byte-identical to upstream cb64ad1).
Full suite: 2833 passed, 16 skipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🔒 Security Analysis Report

Security Analysis Report

Generated: Mon Oct 19 04:26:25 UTC 2026
Repository: docdyhr/versiontracker
Commit: a095609

Bandit Security Scan

�[?25l
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m  0%�[0m �[36m-:--:--�[0m
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m  0%�[0m �[36m-:--:--�[0m
�[2KWorking... �[91m━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m  5%�[0m �[36m-:--:--�[0m
�[2KWorking... �[91m━━━━�[0m�[90m╺�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m 11%�[0m �[36m0:00:02�[0m
�[2KWorking... �[91m━━━━━━━━�[0m�[90m╺�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m 20%�[0m �[36m0:00:02�[0m
�[2KWorking... �[91m━━━━━━━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m 31%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━�[0m�[90m╺�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m 38%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m 42%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━━━━━━━━�[0m �[35m 55%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━━━━━━━━━�[0m �[35m 64%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m�[90m╺�[0m�[90m━━━━━━━━━━�[0m �[35m 73%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m�[90m╺�[0m�[90m━━━━━━━━�[0m �[35m 78%�[0m �[36m0:00:01�[0m
�[2KWorking... �[91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m�[91m╸�[0m�[90m━━━━━━�[0m �[35m 84%�[0m �[36m0:00:01�[0m
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m100%�[0m �[33m0:00:01�[0m
�[2KWorking... �[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[35m100%�[0m �[33m0:00:01�[0m
�[?25hRun started:2026-10-19 04:26:26.521786+00:00

Test results:
>> Issue: [B608:hardcoded_sql_expressions] Possible SQL injection vector through string-based query construction.
   Severity: Medium   Confidence: Low
   CWE: CWE-89 (https://cwe.mitre.org/data/definitions/89.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b608_hardcoded_sql_expressions.html
   Location: versiontracker/advanced_cache.py:693:24
692	                # Use f-string for better readability
693	                msg = f"Failed to delete from cache {key}: {e}"
694	                raise CacheError(msg) from e

--------------------------------------------------
>> Issue: [B105:hardcoded_password_string] Possible hardcoded password: 'homebrew_token'
   Severity: Low   Confidence: Medium
   CWE: CWE-259 (https://cwe.mitre.org/data/definitions/259.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b105_hardcoded_password_string.html
   Location: versiontracker/audit/models.py:161:21
160	    BUNDLE_ID = "bundle_id"
161	    HOMEBREW_TOKEN = "homebrew_token"
162	    DISPLAY_NAME = "display_name"

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/config.py:174:16
173	                cmd = f"{path} --version"
174	                import subprocess
175	

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/config.py:177:29
176	                try:
177	                    result = subprocess.run(cmd.split(), capture_output=True, timeout=2, check=False)
178	                    returncode = result.returncode

--------------------------------------------------
>> Issue: [B110:try_except_pass] Try, Except, Pass detected.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b110_try_except_pass.html
   Location: versiontracker/experimental/analytics.py:645:16
644	                    self.peak_cpu = max(self.peak_cpu, cpu_percent)
645	                except Exception:
646	                    pass
647	                time.sleep(0.05)

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: versiontracker/handlers/outdated_handlers.py:518:8
517	        # Type assertion: apps and brews cannot be None here due to exit_code checks above
518	        assert apps is not None
519	        assert brews is not None

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: versiontracker/handlers/outdated_handlers.py:519:8
518	        assert apps is not None
519	        assert brews is not None
520	        apps = _filter_applications(apps, brews, include_brews)

--------------------------------------------------
>> Issue: [B101:assert_used] Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
   Severity: Low   Confidence: High
   CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b101_assert_used.html
   Location: versiontracker/handlers/outdated_handlers.py:539:8
538	        # Type assertion: outdated_info cannot be None here due to exit_code check
539	        assert outdated_info is not None
540	        # Type cast: outdated_info cannot be None here due to exit_code check above

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/homebrew.py:12:0
11	import re
12	import subprocess
13	import time

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/macos_integration.py:12:0
11	import plistlib
12	import subprocess
13	from pathlib import Path

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/menubar_app.py:8:0
7	import logging
8	import subprocess
9	import sys

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/plugins/example_plugins.py:322:16
321	            try:
322	                import subprocess
323	

--------------------------------------------------
>> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b607_start_process_with_partial_path.html
   Location: versiontracker/plugins/example_plugins.py:324:25
323	
324	                result = subprocess.run(["brew", "--version"], capture_output=True, text=True, timeout=5)
325	                if result.returncode == 0:

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/plugins/example_plugins.py:324:25
323	
324	                result = subprocess.run(["brew", "--version"], capture_output=True, text=True, timeout=5)
325	                if result.returncode == 0:

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/utils.py:16:0
15	import shutil
16	import subprocess
17	import sys

--------------------------------------------------
>> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b607_start_process_with_partial_path.html
   Location: versiontracker/utils.py:804:17
803	    try:
804	        result = subprocess.run(["which", "brew"], capture_output=True, text=True, timeout=5)
805	        return result.returncode == 0

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/utils.py:804:17
803	    try:
804	        result = subprocess.run(["which", "brew"], capture_output=True, text=True, timeout=5)
805	        return result.returncode == 0

--------------------------------------------------
>> Issue: [B607:start_process_with_partial_path] Starting a process with a partial executable path
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b607_start_process_with_partial_path.html
   Location: versiontracker/utils.py:820:17
819	    try:
820	        result = subprocess.run(["brew", "--prefix"], capture_output=True, text=True, timeout=5)
821	        if result.returncode == 0:

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/utils.py:820:17
819	    try:
820	        result = subprocess.run(["brew", "--prefix"], capture_output=True, text=True, timeout=5)
821	        if result.returncode == 0:

--------------------------------------------------
>> Issue: [B603:subprocess_without_shell_equals_true] subprocess call - check for execution of untrusted input.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/plugins/b603_subprocess_without_shell_equals_true.html
   Location: versiontracker/utils.py:853:15
852	    try:
853	        return subprocess.run(command, capture_output=True, text=True, timeout=timeout, check=check)
854	    except subprocess.TimeoutExpired as e:

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/version/batch.py:11:0
10	import logging
11	import subprocess
12	from concurrent.futures import ThreadPoolExecutor

--------------------------------------------------
>> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
   Severity: Low   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   More Info: https://bandit.readthedocs.io/en/1.9.4/blacklists/blacklist_imports.html#b404-import-subprocess
   Location: versiontracker/version/homebrew.py:18:0
17	import logging
18	import subprocess
19	

--------------------------------------------------

Code scanned:
	Total lines of code: 17221
	Total lines skipped (#nosec): 0
	Total potential issues skipped due to specifically being disabled (e.g., #nosec BXXX): 30

Run metrics:
	Total issues (by severity):
		Undefined: 0
		Low: 21
		Medium: 1
		High: 0
	Total issues (by confidence):
		Undefined: 0
		Low: 1
		Medium: 1
		High: 20
Files skipped (0):

Pip-Audit Results

�[?25l�[32m-�[0m Collecting inputs
�[2K�[32m-�[0m Collecting inputs
�[2K�[32m-�[0m Collecting inputs
�[2K�[32m-�[0m Collecting inputs
�[2K�[32m\�[0m Collecting inputs
�[2K�[32m\�[0m Collecting inputs
�[2K�[32m\�[0m Collecting inputs
�[2K�[32m\�[0m Collecting aiohappyeyeballs (2.7.1)
�[2K�[32m\�[0m Auditing aiohappyeyeballs (2.7.1)
�[2K�[32m\�[0m Collecting aiohttp (3.14.5)
�[2K�[32m\�[0m Auditing aiohttp (3.14.5)
�[2K�[32m\�[0m Collecting aiosignal (1.4.0)
�[2K�[32m\�[0m Auditing aiosignal (1.4.0)
�[2K�[32m\�[0m Collecting ast_serialize (0.13.1)
�[2K�[32m\�[0m Auditing ast_serialize (0.13.1)
�[2K�[32m\�[0m Collecting attrs (26.1.0)
�[2K�[32m\�[0m Auditing attrs (26.1.0)
�[2K�[32m\�[0m Collecting bandit (1.9.4)
�[2K�[32m\�[0m Auditing bandit (1.9.4)
�[2K�[32m\�[0m Collecting boolean.py (5.0)
�[2K�[32m\�[0m Auditing boolean.py (5.0)
�[2K�[32m\�[0m Collecting build (1.6.1)
�[2K�[32m\�[0m Auditing build (1.6.1)
�[2K�[32m\�[0m Collecting CacheControl (0.14.4)
�[2K�[32m\�[0m Auditing CacheControl (0.14.4)
�[2K�[32m\�[0m Collecting certifi (2026.10.18)
�[2K�[32m\�[0m Auditing certifi (2026.10.18)
�[2K�[32m\�[0m Collecting cffi (2.1.1)
�[2K�[32m\�[0m Auditing cffi (2.1.1)
�[2K�[32m\�[0m Collecting charset-normalizer (3.5.2)
�[2K�[32m\�[0m Auditing charset-normalizer (3.5.2)
�[2K�[32m\�[0m Collecting coverage (7.16.2)
�[2K�[32m\�[0m Auditing coverage (7.16.2)
�[2K�[32m\�[0m Collecting cryptography (50.0.2)
�[2K�[32m\�[0m Collecting cryptography (50.0.2)
�[2K�[32m\�[0m Auditing cryptography (50.0.2)
�[2K�[32m\�[0m Collecting cyclonedx-python-lib (11.12.0)
�[2K�[32m\�[0m Auditing cyclonedx-python-lib (11.12.0)
�[2K�[32m\�[0m Collecting defusedxml (0.7.1)
�[2K�[32m\�[0m Auditing defusedxml (0.7.1)
�[2K�[32m\�[0m Collecting docutils (0.23)
�[2K�[32m\�[0m Auditing docutils (0.23)
�[2K�[32m\�[0m Collecting filelock (4.2.4)
�[2K�[32m\�[0m Auditing filelock (4.2.4)
�[2K�[32m\�[0m Collecting frozenlist (1.8.0)
�[2K�[32m\�[0m Auditing frozenlist (1.8.0)
�[2K�[32m\�[0m Collecting id (1.6.1)
�[2K�[32m\�[0m Auditing id (1.6.1)
�[2K�[32m\�[0m Collecting idna (3.20)
�[2K�[32m\�[0m Auditing idna (3.20)
�[2K�[32m\�[0m Collecting iniconfig (2.3.1)
�[2K�[32m\�[0m Auditing iniconfig (2.3.1)
�[2K�[32m\�[0m Collecting jaraco.classes (3.4.0)
�[2K�[32m\�[0m Auditing jaraco.classes (3.4.0)
�[2K�[32m\�[0m Collecting jaraco.context (6.1.2)
�[2K�[32m\�[0m Auditing jaraco.context (6.1.2)
�[2K�[32m\�[0m Collecting jaraco.functools (4.6.0)
�[2K�[32m\�[0m Auditing jaraco.functools (4.6.0)
�[2K�[32m\�[0m Collecting jeepney (0.9.0)
�[2K�[32m\�[0m Auditing jeepney (0.9.0)
�[2K�[32m\�[0m Collecting keyring (25.7.0)
�[2K�[32m\�[0m Auditing keyring (25.7.0)
�[2K�[32m\�[0m Collecting librt (0.16.0)
�[2K�[32m\�[0m Auditing librt (0.16.0)
�[2K�[32m\�[0m Collecting license-expression (30.4.4)
�[2K�[32m\�[0m Auditing license-expression (30.4.4)
�[2K�[32m\�[0m Collecting macversiontracker (1.2.0)
�[2K�[32m\�[0m Auditing macversiontracker (1.2.0)
�[2K�[32m\�[0m Collecting markdown-it-py (4.2.0)
�[2K�[32m|�[0m Auditing markdown-it-py (4.2.0)
�[2K�[32m|�[0m Collecting mdurl (0.1.2)
�[2K�[32m|�[0m Auditing mdurl (0.1.2)
�[2K�[32m|�[0m Collecting more-itertools (11.2.1)
�[2K�[32m|�[0m Auditing more-itertools (11.2.1)
�[2K�[32m|�[0m Collecting msgpack (1.2.3)
�[2K�[32m|�[0m Auditing msgpack (1.2.3)
�[2K�[32m|�[0m Collecting multidict (7.1.0)
�[2K�[32m|�[0m Auditing multidict (7.1.0)
�[2K�[32m|�[0m Collecting mypy (2.4.0)
�[2K�[32m|�[0m Auditing mypy (2.4.0)
�[2K�[32m|�[0m Collecting mypy_extensions (1.1.0)
�[2K�[32m|�[0m Auditing mypy_extensions (1.1.0)
�[2K�[32m|�[0m Collecting nh3 (0.3.7)
�[2K�[32m|�[0m Auditing nh3 (0.3.7)
�[2K�[32m|�[0m Collecting packageurl-python (0.17.6)
�[2K�[32m|�[0m Collecting packageurl-python (0.17.6)
�[2K�[32m|�[0m Auditing packageurl-python (0.17.6)
�[2K�[32m|�[0m Collecting packaging (26.3)
�[2K�[32m|�[0m Auditing packaging (26.3)
�[2K�[32m|�[0m Collecting pathspec (1.1.1)
�[2K�[32m|�[0m Auditing pathspec (1.1.1)
�[2K�[32m|�[0m Collecting pip (26.2.1)
�[2K�[32m|�[0m Auditing pip (26.2.1)
�[2K�[32m|�[0m Collecting pip_api (0.0.35)
�[2K�[32m|�[0m Auditing pip_api (0.0.35)
�[2K�[32m|�[0m Collecting pip_audit (2.10.1)
�[2K�[32m|�[0m Auditing pip_audit (2.10.1)
�[2K�[32m|�[0m Collecting pip-requirements-parser (32.0.1)
�[2K�[32m|�[0m Auditing pip-requirements-parser (32.0.1)
�[2K�[32m|�[0m Collecting platformdirs (4.13.5)
�[2K�[32m|�[0m Auditing platformdirs (4.13.5)
�[2K�[32m|�[0m Collecting pluggy (1.7.0)
�[2K�[32m|�[0m Auditing pluggy (1.7.0)
�[2K�[32m|�[0m Collecting propcache (0.5.4)
�[2K�[32m|�[0m Auditing propcache (0.5.4)
�[2K�[32m|�[0m Collecting psutil (7.2.2)
�[2K�[32m|�[0m Auditing psutil (7.2.2)
�[2K�[32m|�[0m Collecting py-serializable (2.1.0)
�[2K�[32m|�[0m Auditing py-serializable (2.1.0)
�[2K�[32m|�[0m Collecting pycparser (3.11)
�[2K�[32m|�[0m Auditing pycparser (3.11)
�[2K�[32m|�[0m Collecting Pygments (2.21.0)
�[2K�[32m|�[0m Auditing Pygments (2.21.0)
�[2K�[32m|�[0m Collecting pyparsing (3.3.3)
�[2K�[32m|�[0m Auditing pyparsing (3.3.3)
�[2K�[32m|�[0m Collecting pyproject_hooks (1.3.3)
�[2K�[32m|�[0m Auditing pyproject_hooks (1.3.3)
�[2K�[32m|�[0m Collecting pytest (9.1.1)
�[2K�[32m|�[0m Auditing pytest (9.1.1)
�[2K�[32m|�[0m Collecting pytest-asyncio (1.4.0)
�[2K�[32m|�[0m Auditing pytest-asyncio (1.4.0)
�[2K�[32m|�[0m Collecting pytest-cov (7.1.0)
�[2K�[32m|�[0m Auditing pytest-cov (7.1.0)
�[2K�[32m|�[0m Collecting pytest-mock (3.17.0)
�[2K�[32m|�[0m Auditing pytest-mock (3.17.0)
�[2K�[32m|�[0m Collecting pytest-timeout (2.4.0)
�[2K�[32m|�[0m Auditing pytest-timeout (2.4.0)
�[2K�[32m|�[0m Collecting PyYAML (6.0.3)
�[2K�[32m|�[0m Auditing PyYAML (6.0.3)
�[2K�[32m|�[0m Collecting readme_renderer (46.0)
�[2K�[32m|�[0m Auditing readme_renderer (46.0)
�[2K�[32m|�[0m Collecting requests (2.34.2)
�[2K�[32m|�[0m Auditing requests (2.34.2)
�[2K�[32m|�[0m Collecting requests-toolbelt (1.0.0)
�[2K�[32m|�[0m Auditing requests-toolbelt (1.0.0)
�[2K�[32m|�[0m Collecting rfc3986 (2.0.0)
�[2K�[32m|�[0m Auditing rfc3986 (2.0.0)
�[2K�[32m|�[0m Collecting rich (15.0.0)
�[2K�[32m|�[0m Auditing rich (15.0.0)
�[2K�[32m|�[0m Collecting ruff (0.17.0)
�[2K�[32m|�[0m Collecting ruff (0.17.0)
�[2K�[32m|�[0m Auditing ruff (0.17.0)
�[2K�[32m|�[0m Collecting SecretStorage (3.5.0)
�[2K�[32m|�[0m Auditing SecretStorage (3.5.0)
�[2K�[32m|�[0m Collecting setuptools (84.0.0)
�[2K�[32m|�[0m Auditing setuptools (84.0.0)
�[2K�[32m|�[0m Collecting sortedcontainers (2.4.0)
�[2K�[32m|�[0m Auditing sortedcontainers (2.4.0)
�[2K�[32m|�[0m Collecting stevedore (5.9.1)
�[2K�[32m|�[0m Auditing stevedore (5.9.1)
�[2K�[32m|�[0m Collecting tabulate (0.10.0)
�[2K�[32m|�[0m Auditing tabulate (0.10.0)
�[2K�[32m|�[0m Collecting termcolor (3.3.0)
�[2K�[32m|�[0m Auditing termcolor (3.3.0)
�[2K�[32m|�[0m Collecting tomli (2.5.0)
�[2K�[32m|�[0m Auditing tomli (2.5.0)
�[2K�[32m|�[0m Collecting tomli_w (1.2.0)
�[2K�[32m|�[0m Auditing tomli_w (1.2.0)
�[2K�[32m|�[0m Collecting tqdm (4.70.1)
�[2K�[32m|�[0m Auditing tqdm (4.70.1)
�[2K�[32m|�[0m Collecting twine (7.0.0)
�[2K�[32m|�[0m Auditing twine (7.0.0)
�[2K�[32m|�[0m Collecting types-PyYAML (6.0.12.20260906)
�[2K�[32m|�[0m Auditing types-PyYAML (6.0.12.20260906)
�[2K�[32m|�[0m Collecting typing_extensions (4.16.0)
�[2K�[32m|�[0m Auditing typing_extensions (4.16.0)
�[2K�[32m|�[0m Collecting urllib3 (2.8.0)
�[2K�[32m|�[0m Auditing urllib3 (2.8.0)
�[2K�[32m|�[0m Collecting wheel (0.48.0)
�[2K�[32m|�[0m Auditing wheel (0.48.0)
�[2K�[32m|�[0m Collecting yarl (1.25.1)
�[2K�[32m|�[0m Auditing yarl (1.25.1)
�[2K�[32m|�[0m Auditing yarl (1.25.1)
�[?25h
�[1A�[2K```

@docdyhr
docdyhr merged commit 52d07ae into main Oct 19, 2026
25 checks passed
@docdyhr
docdyhr deleted the chore/add-agent-skills branch October 19, 2026 07:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant