Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
Stop doubling the namespace in a removed package's purl
update_package_values already prefixes a namespaced package's purl with its
namespace, so prefixing it again while collecting removed artifacts produced
`com.example/widget@1.0.0com.example/com.example/widget@1.0.0`.

The purl reaches the dependency overview comment verbatim, so every removed or
replaced row for a namespaced package rendered with an unreadable name. The
loop collecting added artifacts calls the same function and never did this.
  • Loading branch information
lelia committed Sep 24, 2026
commit 7912791f6f83729332347c4b24f202b6020733b6
2 changes: 0 additions & 2 deletions socketsecurity/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2290,8 +2290,6 @@ def get_added_and_removed_packages(
try:
pkg = Package.from_diff_artifact(asdict(artifact))
pkg = Core.update_package_values(pkg)
if pkg.namespace:
pkg.purl += f"{pkg.namespace}/{pkg.purl}"
removed_packages[artifact.id] = pkg
except KeyError:
log.error(f"KeyError: Could not create package from removed artifact {artifact.id}")
Expand Down
47 changes: 46 additions & 1 deletion tests/core/test_diff_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from pathlib import Path

import pytest
from socketdev.fullscans import DiffArtifact
from socketdev.fullscans import DiffArtifact, StreamDiffResponse

from socketsecurity.core import Core
from socketsecurity.core.classes import Package
Expand Down Expand Up @@ -312,3 +312,48 @@ def print_added_and_removed(added, removed):
# pkg1_purl = next(p for p in diff.new_packages if p.id == "pkg1")
# assert hasattr(pkg1_purl, "capabilities")
# assert set(pkg1_purl.capabilities) == {"File System Access", "Network Access"}


def _namespaced_diff_response(namespace: str = "com.example"):
"""One namespaced artifact, delivered as both an addition and a removal."""
raw = json.loads(
(Path(__file__).parent.parent / "data/fullscans/diff/stream_diff.json").read_text()
)
template = raw["data"]["artifacts"]["added"][0]
artifacts = {bucket: [] for bucket in ("added", "removed", "unchanged", "replaced", "updated")}
for bucket in ("added", "removed"):
artifacts[bucket].append(
dict(
template,
diffType=bucket,
head=None,
base=None,
id=f"namespaced-{bucket}",
namespace=namespace,
name="widget",
version="1.0.0",
type="maven",
)
)
return StreamDiffResponse.from_dict({
"success": raw["success"],
"status": raw["status"],
"data": {**raw["data"], "artifacts": artifacts},
})


def test_removed_package_purl_matches_the_added_form(core):
"""A namespace belongs in the purl once, whichever bucket the artifact arrives in.

The purl reaches the dependency overview comment verbatim, so a second copy of
the namespace renders as an unreadable package name on every removed or
replaced row.
"""
core.sdk.fullscans.stream_diff.side_effect = None
core.sdk.fullscans.stream_diff.return_value = _namespaced_diff_response()
core.sdk.diffscans.create_from_ids.side_effect = Exception("diff-scans unavailable")

added, removed, _ = core.get_added_and_removed_packages("head", "new")

assert added["namespaced-added"].purl == "com.example/widget@1.0.0"
assert removed["namespaced-removed"].purl == added["namespaced-added"].purl