Skip to content

fix(sbom): stop marking every package of an image directly attackable - #330

Closed
reyreavman wants to merge 3 commits into
fix/sbom/canonical-mergefrom
feat/sbom/gost-root-component
Closed

reyreavman wants to merge 3 commits into
fix/sbom/canonical-mergefrom
feat/sbom/gost-root-component

Conversation

@reyreavman

Copy link
Copy Markdown
Collaborator

Summary

sbom.gost.attackSurface: yes no longer stamps yes on every package of the image. The value states that the image exposes an interface to an attacker; the packages inside it are reachable only through the image and now carry indirect. To keep that expressible in the ISPRAS container format, the image's own root component stays listed inside its container instead of being dissolved into it.

Implements the target picture of card 69801379 («yes → корневой компонент yes, потомки indirect»), which until now could not be produced: the checker rejected it.

What

  • gost.Config.ForDescendants derives what everything below the image root carries: yesindirect, every other attack surface value and securityFunction unchanged. gost.Upsert applies the configured values to metadata.component and the derived ones to the component tree.
  • ContainerAssembler keeps the image root component as the first component of its container, instead of copying it over the container and rewriting every reference to it. The container takes the root's version (the ISPRAS schema requires one) and derives its GOST values as the maximum over its content, which the root now supplies.
  • NewImageSBOM counts metadata.component in the image's aggregate, so the container derives from the image value rather than from packages alone.
  • Docs: both language versions state the unit of accounting and the split.

Why

ISPRAS binds a container's GOST values to the maximum over all its descendants, transitively, for both fields — verified on 28 fixtures. «Container yes over indirect packages» is the one arrangement it rejects (ERROR: контейнер "app" не содержит компонентов со значением его поверхности атаки (yes)), and that is exactly what the card asks for when the image root and the container are the same object. Keeping them distinct satisfies the rule by construction: the root carries yes, the container equals the maximum, the packages stay indirect.

Without the split every package of every image is attested as directly reachable by an attacker and as implementing a security function — an overstatement in a document bound for a regulator.

VERIFIED

Real two-image stapel build (base-layer at the defaults, app with attackSurface: yes, securityFunction: no) pushed to a local registry, werf sbom merge in both formats, output run through 3p-ispras-sbom-checker:master:

container base-layer yes/yes
  localhost:5001/gost-real2 <digest>  yes/yes     ← image root component
  jq 1.8.1                            indirect/yes
container app        yes/no
  localhost:5001/gost-real2 <digest>  yes/no
  brotli, curl, jq, libc, libidn2, libpsl, libunistring, openssl, zstd  indirect/no

--format containerфайл корректный; --format ossфайл корректный.

task format, task build, task lint (0 issues), task test:unit (full) — green.

Review focus

  • The container is no longer a copy of the image root: it keeps BOMRef/Name = image name and takes only Version from the root. The purl is deliberately not carried over — inheriting it is what collapsed two images into one container (fixed in e9dfef499b on the base branch).
  • References to the image root are no longer rewritten to the container ref. They resolve because the root is a real component now; keeps every reference to the image root resolvable and the caller's BOM untouched covers it.
  • In oss format the image root is not part of the output (merge drops import metadata), so per-image values are not visible there — unchanged from before this PR, but worth a decision.

Depends on

Based on #329, not on main. Retarget to main once #329 lands.

An image declared with attackSurface: yes now carries that value on the image
component only; the packages it contains are reachable through the image and
get indirect. Other values, and securityFunction in all cases, keep applying
unchanged to the whole tree.

Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
… SBOM

The container wrapping an image dissolved the image root component into
itself, so the ISPRAS rule binding a container to the maximum over its content
had nothing carrying the image's own attack surface. Keep the root as the first
component of its container and let the container derive its values from it.

Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
Signed-off-by: Radmir Khurum <radmir.khurum@flant.com>
@reyreavman

Copy link
Copy Markdown
Collaborator Author

Модель не та. DoD карточки 69801379 требует графовую модель: корни — узлы дерева зависимостей, а здесь корнем считался metadata.component образа. Работа продолжена в новой ветке от #329.

@reyreavman reyreavman closed this Sep 20, 2026
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