Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: external tools producing duplicate keys #440

Merged
merged 1 commit into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
- Docker pushing non-chalked images did not report metsys
plugin keys such as `_EXIT_CODE`, `_CHALK_RUN_TIME`.
([#438](https://github.com/crashappsec/chalk/pull/438))
- External tools for non-file artifacts (e.g. docker image)
sent duplicate keys in both report-level as well as
chalk-mark level. For example `SBOM` key with equivalent
content was duplicated twice.
([#440](https://github.com/crashappsec/chalk/pull/440))

### New Features

Expand Down
18 changes: 11 additions & 7 deletions src/plugins/externalTool.nim
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,25 @@
## This plugin uses information from the config file to set metadata
## keys.

import std/[algorithm, sequtils]
import std/[algorithm, sequtils, sets]
import ".."/[config, plugin_api, util]

type PIInfo = ref object
name: string
type
AlreadyRanError = object of CatchableError
PIInfo = ref object
name: string

proc ensureRunCallback[T](cb: CallbackObj, args: seq[Box]): T =
let value = runCallback(cb, args)
if value.isNone():
raise newException(ValueError, "missing implemenetation of " & $(cb))
return unpack[T](value.get())

var toolCache = initTable[string, ChalkDict]()
var alreadyRan = initHashSet[string]()
proc runOneTool(info: PIInfo, path: string): ChalkDict =
let key = info.name & ":" & path
if key in toolCache:
return toolCache[key]
if key in alreadyRan:
raise newException(AlreadyRanError, "")

trace("Running tool: " & info.name)
result = ChalkDict()
Expand Down Expand Up @@ -65,7 +67,7 @@ proc runOneTool(info: PIInfo, path: string): ChalkDict =
d.del("info")

trace(info.name & ": produced keys " & $(d.keys().toSeq()))
toolCache[key] = d
alreadyRan.incl(key)
return d

template toolBase(path: string) {.dirty.} =
Expand Down Expand Up @@ -109,6 +111,8 @@ template toolBase(path: string) {.dirty.} =
result.merge(data.nestWith(info.name))
if len(data) >= 0 and attrGet[bool]("tool." & info.name & ".stop_on_success"):
break
except AlreadyRanError:
trace(info.name & ": already ran for " & path & ". skipping")
except:
error(info.name & ": " & getCurrentExceptionMsg())

Expand Down
8 changes: 4 additions & 4 deletions tests/functional/chalk/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,16 @@ def validate_chalk_report(
for artifact in artifact_map.values():
assert chalk_report.contains(artifact.host_info)

for chalk in chalk_report.marks:
path = chalk["PATH_WHEN_CHALKED"]
for mark in chalk_report.marks:
path = mark.lifted["PATH_WHEN_CHALKED"]
assert path in artifact_map, "chalked artifact incorrect"
artifact = artifact_map[path]

assert chalk.has(
assert mark.lifted.has(
ARTIFACT_TYPE=artifact.type,
**artifact.chalk_info,
)
assert chalk.has_if(
assert mark.lifted.has_if(
chalk_action == "insert",
_VIRTUAL=virtual,
)
Expand Down
5 changes: 4 additions & 1 deletion tests/functional/test_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
validate_virtual_chalk,
)
from .conf import CODEOWNERS, CONFIGS, DATA, DOCKERFILES, LS_PATH, PYS
from .utils.dict import ANY
from .utils.dict import ANY, MISSING
from .utils.docker import Docker
from .utils.git import Git
from .utils.log import get_logger
Expand Down Expand Up @@ -869,6 +869,9 @@ def test_syft_docker(chalk_copy: Chalk, test_file: str, random_hex: str):
tag=tag,
)

assert build.report.contains(sbom_data)
assert build.mark.has(SBOM=MISSING)

# artifact is the docker image
artifact_info = ArtifactInfo(
type="Docker Image",
Expand Down