Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
29995d5
Bump commons-io:commons-io from 2.10.0 to 2.14.0 in /tests
dependabot[bot] Nov 19, 2024
bbf1bf4
Bump requests from 2.12.1 to 2.32.2
dependabot[bot] Nov 19, 2024
a2b5958
Merge pull request #1 from cognitivegears/dependabot/maven/tests/comm…
cognitivegears Nov 19, 2024
3fe4f10
Merge pull request #2 from cognitivegears/dependabot/pip/requests-2.32.2
cognitivegears Nov 19, 2024
109788d
Fix multiline string formatting in main function
cognitivegears Nov 19, 2024
4a89de0
Handle 404 status code and improve error handling in recv_pkg_info fu…
cognitivegears Nov 19, 2024
ccff951
Add PyPI scanner and update requirements for requirements-parser
cognitivegears Nov 19, 2024
0f9b594
Refactor file path handling in Maven and NPM scanners to use os.path.…
cognitivegears Nov 19, 2024
1fdb1fc
Fix argument parsing and improve error handling in package functions
cognitivegears Nov 20, 2024
8ddbc6a
Refactor package manager handling and error codes; introduce constant…
cognitivegears Nov 20, 2024
a81e6c9
Add logging functionality and improve error handling across package r…
cognitivegears Nov 20, 2024
9a82db0
Add recursive scanning option for package managers and enhance error …
cognitivegears Nov 20, 2024
8d100be
Remove duplicate entries in dependency lists across Maven, NPM, and P…
cognitivegears Nov 20, 2024
bebd71f
Changed packages not exist to a warning
cognitivegears Nov 20, 2024
98aa05d
Add error handling for warnings and new exit code for package not found
cognitivegears Nov 20, 2024
3455502
Update project configuration and dependencies
cognitivegears Nov 20, 2024
05a0c23
Enhance package analysis with detailed docstrings and logging improve…
cognitivegears Nov 20, 2024
b277998
Add request timeout handling and constant for HTTP requests
cognitivegears Nov 20, 2024
11460d6
Moved argument parsing for Combobulator
cognitivegears Nov 20, 2024
f8a39b2
Remove currently unused GitHub token argument
cognitivegears Nov 20, 2024
60dc060
Update README to include 'pypi' as a supported package manager type a…
cognitivegears Nov 20, 2024
5a52358
Possible fix/workaround for old scan issue
cognitivegears Nov 22, 2024
6bdbfcb
Version count conditional backwards
cognitivegears Nov 22, 2024
5593b01
Added rate limiting, added additional npm info for heuristics, and ad…
cognitivegears Nov 22, 2024
2976da0
Remove unused imports from combobulator.py
cognitivegears Nov 22, 2024
ae5c7d7
Add JSON export functionality and update README with new argument
cognitivegears Nov 22, 2024
e12bfa2
Refactor heuristics scoring logic to use default thresholds from Defa…
cognitivegears Nov 22, 2024
e665ccb
Add risk assessment properties and update heuristics logic for packag…
cognitivegears Nov 22, 2024
68ec38d
Add risk assessment check and update export functions to include risk…
cognitivegears Nov 22, 2024
f9a2ea9
Update risk handling in combobulator.py to log identified risks and a…
cognitivegears Nov 22, 2024
65e6d1d
Add quiet mode option to suppress console output and adjust logging c…
cognitivegears Nov 22, 2024
d220fbd
Added CONTRIBUTERS.md file
cognitivegears Nov 24, 2024
676c0a5
Bump requests from 2.32.2 to 2.32.4
dependabot[bot] Jun 10, 2025
e61e19d
Bump org.apache.commons:commons-lang3 from 3.10 to 3.18.0 in /tests
dependabot[bot] Jul 12, 2025
6a8b963
Merge pull request #3 from cognitivegears/dependabot/pip/requests-2.32.4
cognitivegears Sep 3, 2025
37b7f70
Merge pull request #4 from cognitivegears/dependabot/maven/tests/org.…
cognitivegears Sep 3, 2025
ea0a833
Moved to uv
cognitivegears Sep 3, 2025
da2cf7c
Renamed to depgate
cognitivegears Sep 3, 2025
cf8709e
Updates for release
cognitivegears Sep 3, 2025
2d3c6a1
Updated README
cognitivegears Sep 4, 2025
19b3bc3
Bump actions/checkout from 4 to 5
dependabot[bot] Sep 4, 2025
f8e04b1
Update requests requirement from <2.32.5,>=2.32.4 to >=2.32.4,<2.32.6
dependabot[bot] Sep 4, 2025
904f163
Bump actions/download-artifact from 4 to 5
dependabot[bot] Sep 4, 2025
b6729ea
Merge pull request #5 from cognitivegears/dependabot/github_actions/a…
cognitivegears Sep 4, 2025
5e3ebaa
Merge pull request #6 from cognitivegears/dependabot/pip/requests-gte…
cognitivegears Sep 4, 2025
eb81f98
Merge pull request #7 from cognitivegears/dependabot/github_actions/a…
cognitivegears Sep 4, 2025
5a63e28
Small visual improvements
cognitivegears Sep 4, 2025
15e569a
Fixed some pylint warnings
cognitivegears Sep 7, 2025
1c6dd78
Added e2e tests
cognitivegears Sep 7, 2025
993168a
Added github action
cognitivegears Sep 7, 2025
70f259d
refactor(cli): extract helpers to reduce branches; keep lazy imports …
cognitivegears Sep 8, 2025
065f1d1
lint: add targeted pylint disables for data-holder classes; document …
cognitivegears Sep 8, 2025
bbac403
Changes for gitignore
cognitivegears Sep 8, 2025
38ff25d
Initial version of source code repository integration
cognitivegears Sep 8, 2025
346cb27
Modified to reduce duplicate code
cognitivegears Sep 8, 2025
f16666c
Extracted common code and moved
cognitivegears Sep 8, 2025
0b93293
Bump actions/setup-python from 5 to 6
dependabot[bot] Sep 8, 2025
e91dbe8
Bump actions/checkout from 4 to 5
dependabot[bot] Sep 8, 2025
be2b4a1
Added logging
cognitivegears Sep 9, 2025
9e56fc3
Added debug logging
cognitivegears Sep 9, 2025
73acbf2
Fixed lookup of npm repo information
cognitivegears Sep 9, 2025
bf9f95d
Matched version checking fixed
cognitivegears Sep 9, 2025
0d582af
Small change to wording
cognitivegears Sep 9, 2025
12e4657
Setting version information
cognitivegears Sep 9, 2025
95ae5c8
Fixed version lookup
cognitivegears Sep 9, 2025
6d27239
Fixed npm resolution for latest
cognitivegears Sep 9, 2025
abd8b82
Improved release and tag comparisons
cognitivegears Sep 10, 2025
3dffc03
Added tests for recent changes
cognitivegears Sep 10, 2025
ac013f3
General cleanup
cognitivegears Sep 10, 2025
4545380
Enhanced config file
cognitivegears Sep 10, 2025
c9d36a4
Added http rate limiting and retry support
cognitivegears Sep 10, 2025
7bec27f
updated example
cognitivegears Sep 10, 2025
ffadbf1
Initial version of policy based scans
cognitivegears Sep 10, 2025
6d6086e
Changed command line arguments
cognitivegears Sep 10, 2025
25a352d
Fixed pypi license checking
cognitivegears Sep 11, 2025
affbe69
Fixed npm license checking
cognitivegears Sep 11, 2025
02ffa8f
Fixed small bug with maven lookup
cognitivegears Sep 11, 2025
a5abc74
Added depsdev for further enrichment
cognitivegears Sep 11, 2025
5cd1532
Fixes scanning lock files
cognitivegears Sep 11, 2025
7f7735f
Added dev and test dep detection, transitive and direct
cognitivegears Sep 11, 2025
d49ca6f
Fixed regression in npm
cognitivegears Sep 11, 2025
851f8b4
Fixed bug with npm naming
cognitivegears Sep 12, 2025
0ac25b6
Refactoring
cognitivegears Sep 12, 2025
fd8eb39
Added new linked mode to validate package linkage
cognitivegears Sep 12, 2025
a916748
Updated to use scan semantics
cognitivegears Sep 12, 2025
4bdbe42
Updated version
cognitivegears Sep 12, 2025
c5a94fe
Added linked policy type
cognitivegears Sep 12, 2025
93a23b1
Added partial match support
cognitivegears Sep 16, 2025
1ba0c19
Merge pull request #8 from cognitivegears/dependabot/github_actions/a…
cognitivegears Sep 16, 2025
5815d8b
Merge pull request #9 from cognitivegears/dependabot/github_actions/a…
cognitivegears Sep 16, 2025
362e6b5
Initial version of MCP
cognitivegears Oct 18, 2025
878d5f1
Fixed pylint issues
cognitivegears Oct 18, 2025
41a188b
Bump actions/upload-artifact from 4 to 5
dependabot[bot] Oct 27, 2025
3c6742b
Bump actions/download-artifact from 5 to 6
dependabot[bot] Oct 27, 2025
f6813b3
Landed MCP support for Depgate
cognitivegears Nov 5, 2025
beeb516
Merge pull request #10 from cognitivegears/dependabot/github_actions/…
cognitivegears Nov 5, 2025
81b8f14
Merge pull request #11 from cognitivegears/dependabot/github_actions/…
cognitivegears Nov 5, 2025
675e2f9
Code review changes
cognitivegears Nov 5, 2025
9881774
Additional code improvements
cognitivegears Nov 5, 2025
ec9e64c
Additional code cleanup
cognitivegears Nov 5, 2025
7700f1e
Merge pull request #12 from cognitivegears/feature/mcp
cognitivegears Nov 5, 2025
007b190
Added fix for hanging
cognitivegears Nov 6, 2025
a082ed9
Changes to make warnings more obvious
cognitivegears Nov 6, 2025
b0c1d46
Additional code review changes
cognitivegears Nov 6, 2025
88f1f8f
Code review security changes
cognitivegears Nov 6, 2025
f73cb2a
Code review changes
cognitivegears Nov 6, 2025
dd92f25
Merge pull request #13 from cognitivegears/bugfix/mcp_hanging
cognitivegears Nov 6, 2025
21ec675
Bug fix for warnings
cognitivegears Nov 6, 2025
822f110
Merge pull request #14 from cognitivegears/bugfix/version_warning
cognitivegears Nov 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "depgate"
version = "0.6.0"
version = "0.6.1"
description = "DepGate detects and prevents dependency confusion and supply-chain risks. (Hard fork of Apiiro's Dependency Combobulator)"
readme = "README.md"
requires-python = ">=3.10"
Expand Down
220 changes: 204 additions & 16 deletions src/cli_mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from typing_extensions import TypedDict

import urllib.parse as _u
from constants import Constants
from constants import Constants, ExitCodes
from common.logging_utils import configure_logging as _configure_logging
from common.http_client import get_json as _get_json

Expand Down Expand Up @@ -73,8 +73,9 @@ class PackageOut(TypedDict, total=False):
policyDecision: Any


class SummaryOut(TypedDict):
class SummaryOut(TypedDict, total=False):
count: int
findingsCount: int


class ScanResultOut(TypedDict, total=False):
Expand Down Expand Up @@ -350,12 +351,72 @@ def _handle_lookup_latest_version(


def _run_scan_pipeline(scan_args: Any) -> Dict[str, Any]:
pkglist = build_pkglist(scan_args)
create_metapackages(scan_args, pkglist)
apply_version_resolution(scan_args, pkglist)
check_against(scan_args.package_type, scan_args.LEVEL, metapkg.instances)
run_analysis(scan_args.LEVEL, scan_args, metapkg.instances)
return _gather_results()
"""Run the scan pipeline, catching SystemExit and converting to RuntimeError for MCP context.

This function handles various FILE_ERROR scenarios by providing specific error messages
based on where in the pipeline the error occurred.
"""
try:
# Step 1: Build package list (may fail if no dependency files found, file I/O errors, or parse errors)
try:
pkglist = build_pkglist(scan_args)
except SystemExit as se:
exit_code = se.code if hasattr(se, 'code') and se.code is not None else 1
if exit_code == ExitCodes.FILE_ERROR.value:
# Check if this is a project scan (has FROM_SRC) vs single dependency scan
from_src = getattr(scan_args, "FROM_SRC", None)
if from_src:
project_dir = from_src[0] if from_src else None
if project_dir:
# Match the specific error message format from _build_cli_args_for_project_scan
raise RuntimeError(
f"No supported dependency files found in '{project_dir}'. "
"Expected one of: package.json (npm), requirements.txt/pyproject.toml (pypi), or pom.xml (maven)"
) from se
raise RuntimeError(
"No supported dependency files found in project directory. "
"Expected one of: package.json (npm), requirements.txt/pyproject.toml (pypi), or pom.xml (maven)"
) from se
# For single dependency scans, FILE_ERROR might indicate file I/O errors or parse errors
raise RuntimeError("Failed to build package list: file error or parse error") from se
raise

# Step 2: Create metapackages (may fail on invalid Maven coordinates)
try:
create_metapackages(scan_args, pkglist)
except SystemExit as se:
exit_code = se.code if hasattr(se, 'code') and se.code is not None else 1
if exit_code == ExitCodes.FILE_ERROR.value:
# Invalid Maven coordinates or other package creation errors
raise RuntimeError("Invalid package format or coordinates") from se
raise

# Step 3: Apply version resolution
apply_version_resolution(scan_args, pkglist)

# Step 4: Check against registry (may fail on invalid package type)
try:
check_against(scan_args.package_type, scan_args.LEVEL, metapkg.instances)
except SystemExit as se:
exit_code = se.code if hasattr(se, 'code') and se.code is not None else 1
if exit_code == ExitCodes.FILE_ERROR.value:
raise RuntimeError(f"Package type '{scan_args.package_type}' does not support registry check") from se
raise

# Step 5: Run analysis
run_analysis(scan_args.LEVEL, scan_args, metapkg.instances)

return _gather_results()
except RuntimeError:
# Re-raise RuntimeErrors as-is (they already have specific messages)
raise
except SystemExit as se:
# Catch any other SystemExit that wasn't handled above
exit_code = se.code if hasattr(se, 'code') and se.code is not None else 1
if exit_code == ExitCodes.FILE_ERROR.value:
# Generic fallback for FILE_ERROR we couldn't categorize
raise RuntimeError("Scan failed: file or package error") from se
raise RuntimeError(f"Scan failed with exit code {exit_code}") from se


def _build_args_for_single_dependency(eco: Ecosystem, name: str, version: Optional[str] = None) -> Any:
Expand Down Expand Up @@ -407,8 +468,11 @@ def _build_cli_args_for_project_scan(
elif os.path.isfile(os.path.join(root, Constants.POM_XML_FILE)):
pkg_type = "maven"
else:
# Default to npm to preserve common behavior
pkg_type = "npm"
# No supported dependency files found - raise error early for MCP context
raise RuntimeError(
f"No supported dependency files found in '{project_dir}'. "
"Expected one of: package.json (npm), requirements.txt/pyproject.toml (pypi), or pom.xml (maven)"
)
args.package_type = pkg_type
args.LIST_FROM_FILE = []
args.FROM_SRC = [project_dir]
Expand All @@ -432,29 +496,153 @@ def _build_cli_args_for_project_scan(


def _gather_results() -> Dict[str, Any]:
"""Gather scan results and detect supply-chain issues.

Collects package information and generates findings for various supply-chain
risks including missing packages, invalid repository URLs, version mismatches,
and missing repository URLs.

Returns:
Dict with keys:
- packages: List of package information dictionaries
- findings: List of supply-chain issue findings
- summary: Summary statistics including count and findingsCount

Findings Types:
- missing_package: Package doesn't exist in registry (severity: error)
- invalid_repository_url: Repository URL exists but repo doesn't (severity: warning)
- version_mismatch: Repo exists but version doesn't match (severity: warning)
- missing_repository_url: Package exists but no repo URL (severity: info)
"""
out: Dict[str, Any] = {
"packages": [],
"findings": [],
"summary": {},
}
pkgs = []
findings = []

# Helper function to format package name with optional version
def _format_pkg_version(name: str, version: Optional[str]) -> str:
"""Format package name with optional version."""
if version:
return f"{name}@{version}"
return name

for mp in metapkg.instances:
pkg_name = getattr(mp, "pkg_name", None)
pkg_type = getattr(mp, "pkg_type", None)
resolved_version = getattr(mp, "resolved_version", None)
repo_url = getattr(mp, "repo_url_normalized", None)
repo_exists = getattr(mp, "repo_exists", None)
repo_resolved = bool(getattr(mp, "repo_resolved", False))
repo_version_match = getattr(mp, "repo_version_match", None)

# Skip packages with missing essential data (name and ecosystem are required by schema)
if not pkg_name or not pkg_type:
continue

pkgs.append(
{
"name": getattr(mp, "pkg_name", None),
"ecosystem": getattr(mp, "pkg_type", None),
"version": getattr(mp, "resolved_version", None),
"repositoryUrl": getattr(mp, "repo_url_normalized", None),
"name": pkg_name,
"ecosystem": pkg_type,
"version": resolved_version,
"repositoryUrl": repo_url,
"license": getattr(mp, "license_id", None),
"linked": getattr(mp, "linked", None),
"repoVersionMatch": getattr(mp, "repo_version_match", None),
"repoVersionMatch": repo_version_match,
"policyDecision": getattr(mp, "policy_decision", None),
}
)

pkg_display = _format_pkg_version(pkg_name, resolved_version)

# Check for various supply-chain issues and add findings

# 1. Missing package (package doesn't exist in registry)
pkg_exists = getattr(mp, "exists", None)
if pkg_exists is False:
findings.append({
"type": "missing_package",
"severity": "error",
"package": pkg_name,
"ecosystem": pkg_type,
"version": resolved_version,
"message": (
f"Package {pkg_name} does not exist in the {pkg_type} registry. "
"This may indicate a dependency confusion attack or a typo in the package name."
),
})

# 2. Invalid repository URL (repository URL exists but repository doesn't exist)
if repo_url and repo_resolved and repo_exists is False:
findings.append({
"type": "invalid_repository_url",
"severity": "warning",
"package": pkg_name,
"ecosystem": pkg_type,
"version": resolved_version,
"repositoryUrl": repo_url,
"message": (
f"Package {pkg_display} references a repository URL "
f"({repo_url}) that does not exist or is not accessible. "
"This may indicate a broken link or a supply-chain risk."
),
})

# 3. Version mismatch (repository exists but version doesn't match)
# This mirrors the logic from linked.py: repo_ok = (repo_url is not None) and repo_resolved and repo_exists
repo_ok = (repo_url is not None) and repo_resolved and (repo_exists is True)
if repo_ok:
match_ok = False
try:
if repo_version_match and isinstance(repo_version_match, dict):
match_ok = bool(repo_version_match.get("matched", False))
except Exception: # pylint: disable=broad-exception-caught
match_ok = False

if not match_ok and resolved_version:
# Repository exists but version doesn't match - this is a problem
# Only flag if we have a resolved version (to avoid false positives when version matching is disabled)
findings.append({
"type": "version_mismatch",
"severity": "warning",
"package": pkg_name,
"ecosystem": pkg_type,
"version": resolved_version,
"repositoryUrl": repo_url,
"message": (
f"Package {pkg_display} has a repository URL "
f"({repo_url}) but no matching tag or release was found in the repository. "
"This may indicate a supply-chain risk where the package version "
"does not correspond to a repository release."
),
})

# 4. Missing repository URL (package exists but has no repository URL)
# This is less critical but could be informative for supply-chain transparency
if pkg_exists is True and not repo_url:
repo_present_in_registry = getattr(mp, "repo_present_in_registry", None)
# Only flag if we know the package should have a repo URL (it was checked but not found)
if repo_present_in_registry is False:
findings.append({
"type": "missing_repository_url",
"severity": "info",
"package": pkg_name,
"ecosystem": pkg_type,
"version": resolved_version,
"message": (
f"Package {pkg_display} exists in the registry "
"but does not have a repository URL in its metadata. "
"This may reduce supply-chain transparency."
),
})

out["packages"] = pkgs
# findings and summary are inferred by callers today; we include minimal fields
out["findings"] = findings
out["summary"] = {
"count": len(pkgs),
"findingsCount": len(findings),
}
return out

Expand Down
2 changes: 1 addition & 1 deletion src/depgate.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: depgate
Version: 0.6.0
Version: 0.6.1
Summary: DepGate detects and prevents dependency confusion and supply-chain risks. (Hard fork of Apiiro's Dependency Combobulator)
Author: cognitivegears
License: Apache-2.0
Expand Down
2 changes: 2 additions & 0 deletions src/depgate.egg-info/SOURCES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,11 @@ tests/test_logging_integration_e2e.py
tests/test_logging_utils_formatters.py
tests/test_logging_utils_redaction.py
tests/test_maven_repo_discovery.py
tests/test_mcp_findings_comprehensive.py
tests/test_mcp_scan_project_integration.py
tests/test_mcp_server_basic.py
tests/test_mcp_stdio_integration.py
tests/test_mcp_version_mismatch.py
tests/test_npm_exists_preservation.py
tests/test_npm_repo_discovery.py
tests/test_parse_tokens.py
Expand Down
5 changes: 4 additions & 1 deletion src/depgate_mcp/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@
"summary": {
"type": "object",
"required": ["count"],
"properties": {"count": {"type": "integer", "minimum": 0}},
"properties": {
"count": {"type": "integer", "minimum": 0},
"findingsCount": {"type": "integer", "minimum": 0},
},
"additionalProperties": True,
},
},
Expand Down
6 changes: 4 additions & 2 deletions src/registry/maven/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,10 @@ def _url_fallback_from_pom(pom_xml: str) -> Optional[str]:
url_elem = root.find(f"{ns}url")
if url_elem is not None and url_elem.text:
url = url_elem.text.strip()
# Check if it looks like a GitHub/GitLab URL
if "github.com" in url or "gitlab.com" in url:
# Check if it looks like a GitHub/GitLab URL by parsing it
# (avoid substring matching in sanitized URLs)
repo_ref = normalize_repo_url(url)
if repo_ref is not None and repo_ref.host in ("github", "gitlab"):
return url
except (ET.ParseError, AttributeError):
pass
Expand Down
Loading