Skip to content

feat: add variants override mechanism with libc detection#4733

Merged
suzuki-shunsuke merged 6 commits into
mainfrom
feat/variants-libc
Apr 29, 2026
Merged

feat: add variants override mechanism with libc detection#4733
suzuki-shunsuke merged 6 commits into
mainfrom
feat/variants-libc

Conversation

@suzuki-shunsuke

@suzuki-shunsuke suzuki-shunsuke commented Apr 29, 2026

Copy link
Copy Markdown
Member

Context

Refs #4514. Resolves the request to detect musl Linux at runtime and select different release assets accordingly.

The original issue proposed adding a dedicated musl: true field to registry overrides. @jdx commented that the underlying need is broader: same goos/goarch artifacts diverge along multiple axes (libc, CPU baseline, static vs dynamic linkage, ...), and a generic mechanism would handle them all without one-off schema fields. This PR implements that generic mechanism, with libc as the first concrete axis.

Design

Schema

A new variants field on Override, modeled as a list of structs to mirror existing aqua conventions (format_overrides, version_overrides) and to leave room for future extension fields (values, operator, not, ...) without reshaping the schema.

overrides:
  - goos: linux
    goarch: amd64
    variants:
      - key: libc
        value: musl
    asset: foo-{{.OS}}-{{.Arch}}-musl.{{.Format}}

  - goos: linux
    goarch: amd64
    variants:
      - key: libc
        value: gnu
    asset: foo-{{.OS}}-{{.Arch}}-gnu.{{.Format}}

Matching rules

In addition to existing goos/goarch/envs matching, every variant must match for the override to apply:

  1. If the variant's key is not in aqua's allowlist of supported keys, the override is treated as non-matching (skipped).
  2. Otherwise the value reported by the runtime for that key must equal value exactly.
  3. Overrides without variants keep their existing behavior — fully backwards compatible.

Forward compatibility (skip on unknown key)

Skipping on unknown keys lets registries stack a more specific variant alongside a fallback override that older aqua versions can still match:

overrides:
  # newer aqua that knows the `cpu` key
  - goos: linux
    variants:
      - key: libc
        value: musl
      - key: cpu
        value: avx2
    asset: foo-musl-avx2.tar.gz
  # fallback for older aqua (matched when `cpu` is unknown)
  - goos: linux
    variants:
      - key: libc
        value: musl
    asset: foo-musl.tar.gz

This was preferred over silently ignoring unknown keys because silent-ignore can install the wrong artifact (e.g. a glibc binary on a musl host). Failing to match is the safer default. A registry-level min_aqua_version knob was also considered, but does not currently exist in aqua and was deemed heavier than needed for this change.

Changes

pkg/config/registry/package_info.go

  • Adds Variant struct (Key, Value) and Variants slice type. Key carries jsonschema:"enum=libc" so the generated schema reflects the current allowlist.
  • Adds Variants Variants field to Override. (Not added to VersionOverride: that type is matched by version constraints only — the platform-level variant matching belongs on the nested Override entries it already contains.)

pkg/config/registry/override.go

  • Adds supportedVariantKeys allowlist (currently just libc).
  • Extends Override.Match with a final loop over Variants that returns false on any unknown key or any value mismatch.
  • Adds runtimeVariantValue helper that maps a variant key to the corresponding Runtime field (currently libcrt.LibC).

pkg/runtime/runtime.go

  • Adds LibC string to the Runtime struct.
  • New() populates it via a new libc() helper that honors AQUA_LIBC env var (mirrors the existing AQUA_GOOS / AQUA_GOARCH override pattern, useful for tests and manual overrides) and falls back to detectLibC().
  • NewR() populates it via detectLibC() directly (no env override, matching its existing semantics).

pkg/runtime/libc_linux.go / pkg/runtime/libc_other.go (new)

  • Linux build: runs ldd --version and inspects the combined stdout/stderr for the substring musl. Returns "musl", "gnu", or "" (when ldd is missing and the buffer is empty). Run errors are intentionally ignored: musl's ldd --version exits non-zero but still prints its libc name to stderr, and a missing ldd legitimately means we can't detect — we fall through to "" and let the override loop skip libc-constrained entries in favor of fallback overrides.
  • Non-Linux build: stub returning "". Selected by build tag, so non-Linux binaries never invoke os/exec.

pkg/config/registry/override_test.go

  • Adds five TestOverride_Match cases:
    • libc: musl matches a musl runtime.
    • libc: musl does not match a gnu runtime.
    • libc: musl does not match an empty LibC (non-Linux / detection failed).
    • Unknown variant key (cpu) skips the override.
    • Conflicting variants (libc: musl AND libc: gnu) cannot all match.

pkg/runtime/libc_linux_test.go (new)

  • Smoke test asserting detectLibC() returns one of "musl", "gnu", "" on Linux. The CI runner is glibc-based so a real musl test would need an Alpine container; skipped here to keep this PR scoped.

json-schema/registry.json

  • Regenerated via go run ./cmd/gen-jsonschema. Adds Variant and Variants definitions and a variants property on Override.

Test plan

  • cmdx v (go vet)
  • cmdx l (golangci-lint, 0 issues)
  • cmdx t (full test suite, all packages pass)
  • Manual verification on a real musl host (Alpine) with a registry that uses variants: [{key: libc, value: musl}] — recommended before merging if reviewers want end-to-end confidence; the unit tests cover the matching logic with a mocked LibC field.
  • Existing registries (no variants) regress nothing — covered by the existing TestOverride_Match and TestPackageInfo_Override cases continuing to pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Package overrides now support variant-based conditions for finer runtime matching.
    • Runtime detects and exposes the system libc variant on Linux (enabling libc-based matching); non-Linux platforms do not report a libc variant.
  • Tests

    • Added tests covering variant-based override matching and libc detection behavior.

Add a generic `variants` field to registry overrides for selecting
artifacts that share the same goos/goarch but differ along other
axes. The first supported axis is `libc` (musl / gnu) detected via
`ldd --version` on Linux.

Refs #4514

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 29, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Add variant-based override matching: new Variant/Variants types and schema entries, extend Override to evaluate variants against runtime values, add libc detection (musl/gnu) and expose Runtime.LibC, update constructors to accept context.Context, and add tests for matching and detection.

Changes

Cohort / File(s) Summary
Schema & Types
json-schema/registry.json, pkg/config/registry/package_info.go
Add $defs/Variant and $defs/Variants to JSON schema; add Go types Variant and Variants; extend Override struct with variants field.
Override Matching Logic
pkg/config/registry/override.go, pkg/config/registry/override_test.go
Extend Override.Match to validate supported variant keys and require each variant value to equal the runtime-derived value; add tests for matching, non-matching, unknown keys, and multi-variant enforcement.
Runtime: Libc Detection & API changes
pkg/runtime/libc_linux.go, pkg/runtime/libc_other.go, pkg/runtime/runtime.go, pkg/runtime/libc_linux_internal_test.go, pkg/runtime/runtime_test.go
Implement detectLibC (Linux: check known musl files, fallback to ldd --version); no-op on non-Linux. Add Runtime.LibC and change New/NewR to accept context.Context; add unit test validating possible outputs.
Callsites & Initializers
cmd/aqua/main.go, pkg/cli/runner.go, pkg/controller/exec/exec.go, pkg/cosign/verify.go, pkg/ghattestation/exec.go, pkg/installpackage/installer.go, pkg/minisign/exec.go, pkg/slsa/exec.go, pkg/config/package_internal_test.go, pkg/config/package_test.go
Update runtime construction sites to pass a context.Context (often context.Background() or t.Context() in tests) and propagate context where needed (e.g., timestamp update).

Sequence Diagram

sequenceDiagram
    participant Caller as Caller (cmd/runner/exec)
    participant Init as Runtime.New(ctx)
    participant Detect as detectLibC(ctx)
    participant Runtime as Runtime{GOOS, GOARCH, LibC}
    participant Override as Override.Match
    participant VariantEval as Variant evaluator

    Caller->>Init: New(ctx) / NewR(ctx)
    Init->>Detect: call detectLibC(ctx) or read AQUA_LIBC
    Detect-->>Init: return "musl"/"gnu"/""
    Init-->>Runtime: populate LibC field

    Override->>VariantEval: for each variant key/value
    VariantEval->>Runtime: read key (e.g., libc)
    VariantEval-->>Override: equal? (yes/no)
    Override->>Override: include variant results in final match decision
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through code with whiskers bright,

Listening for libc in day and night,
Variants matched with careful care,
Overrides only when keys pair,
A crunchy carrot for tests that light.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main feature: adding a variants override mechanism with libc detection, which is the core focus of all changes across the codebase.
Description check ✅ Passed The description is comprehensive, well-structured, and covers context, design rationale, detailed change breakdown, test plan, and rationale for key decisions. All required template items are addressed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/variants-libc

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@suzuki-shunsuke suzuki-shunsuke added the enhancement New feature or request label Apr 29, 2026

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/config/registry/override.go`:
- Around line 48-55: The loop over ov.Variants in override matching must guard
against nil entries to avoid panics; update the loop that iterates ov.Variants
to check if v == nil and return false (or otherwise treat the override as
non-matching) before accessing v.Key or v.Value so you don't dereference a nil
pointer when evaluating supportedVariantKeys[v.Key] or calling
runtimeVariantValue(rt, v.Key).

In `@pkg/config/registry/package_info.go`:
- Around line 188-189: The struct fields Variant.Key and Variant.Value are
marked with `omitempty`, allowing missing keys/values to silently pass
deserialization; remove `omitempty` from the YAML/JSON struct tags on `Key` and
`Value` in the Variant struct so those fields are required at parse time (e.g.,
change `yaml:",omitempty" json:"key,omitempty"` to `yaml:"" json:"key"` and
similarly for `Value`), ensuring parsing fails for malformed entries and
preventing silent misses in the override matching logic used by the matching
code (see the override matching that checks Variant.Key).

In `@pkg/runtime/libc_linux.go`:
- Around line 21-25: The ldd invocation in detectLibC() currently runs via
exec.Command("ldd","--version") without a timeout and can hang; change it to use
a context with timeout (e.g., context.WithTimeout) and exec.CommandContext so
the process is killed if it exceeds the timeout, preserve capturing
stdout/stderr (out buffer) and handle ctx.Err() / process exit to return the
same safe default value on timeout or error; update detectLibC() to cancel the
context and treat a timeout as a non-detection rather than blocking startup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6a6eae9b-2589-4cc9-b02a-3772a7d11c64

📥 Commits

Reviewing files that changed from the base of the PR and between 71e823e and 0052f1e.

📒 Files selected for processing (8)
  • json-schema/registry.json
  • pkg/config/registry/override.go
  • pkg/config/registry/override_test.go
  • pkg/config/registry/package_info.go
  • pkg/runtime/libc_linux.go
  • pkg/runtime/libc_linux_test.go
  • pkg/runtime/libc_other.go
  • pkg/runtime/runtime.go

Comment on lines +48 to +55
for _, v := range ov.Variants {
if _, ok := supportedVariantKeys[v.Key]; !ok {
return false
}
if runtimeVariantValue(rt, v.Key) != v.Value {
return false
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard nil Variants entries to prevent panic in matcher.

At Line 49, v.Key is dereferenced without checking v != nil. A malformed config containing a null variant element can crash override matching.

Suggested fix
 	for _, v := range ov.Variants {
+		if v == nil {
+			return false
+		}
 		if _, ok := supportedVariantKeys[v.Key]; !ok {
 			return false
 		}
 		if runtimeVariantValue(rt, v.Key) != v.Value {
 			return false
 		}
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for _, v := range ov.Variants {
if _, ok := supportedVariantKeys[v.Key]; !ok {
return false
}
if runtimeVariantValue(rt, v.Key) != v.Value {
return false
}
}
for _, v := range ov.Variants {
if v == nil {
return false
}
if _, ok := supportedVariantKeys[v.Key]; !ok {
return false
}
if runtimeVariantValue(rt, v.Key) != v.Value {
return false
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/config/registry/override.go` around lines 48 - 55, The loop over
ov.Variants in override matching must guard against nil entries to avoid panics;
update the loop that iterates ov.Variants to check if v == nil and return false
(or otherwise treat the override as non-matching) before accessing v.Key or
v.Value so you don't dereference a nil pointer when evaluating
supportedVariantKeys[v.Key] or calling runtimeVariantValue(rt, v.Key).

Comment on lines +188 to +189
Key string `yaml:",omitempty" json:"key,omitempty" jsonschema:"enum=libc"`
Value string `yaml:",omitempty" json:"value,omitempty"`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python - <<'PY'
import json
with open("json-schema/registry.json", "r", encoding="utf-8") as f:
    schema = json.load(f)
variant = schema["$defs"]["Variant"]
print("Variant.required =", variant.get("required"))
print("Variant.properties keys =", sorted(variant.get("properties", {}).keys()))
PY

Repository: aquaproj/aqua

Length of output: 124


🏁 Script executed:

# First, let's examine the Variant struct definition
sed -n '180,200p' pkg/config/registry/package_info.go | cat -n

Repository: aquaproj/aqua

Length of output: 1418


🏁 Script executed:

# Check how Variant is used in the codebase
rg -n "Variant" pkg/config/registry/package_info.go | head -20

Repository: aquaproj/aqua

Length of output: 376


🏁 Script executed:

# Check for any validation logic around Variant fields
rg -n "\.Key|\.Value" pkg/config/ -A 2 -B 2 | head -40

Repository: aquaproj/aqua

Length of output: 1049


🏁 Script executed:

# Check the schema generation to understand how omitempty affects it
find . -name "*.go" -type f | xargs rg -l "jsonschema" | grep -E "(cmd|pkg)" | head -5

Repository: aquaproj/aqua

Length of output: 229


🏁 Script executed:

# Check the Override matching logic more carefully
cat -n pkg/config/registry/override.go | sed -n '45,60p'

Repository: aquaproj/aqua

Length of output: 469


🏁 Script executed:

# Check if there are any tests that might rely on empty Variant fields
rg -n "Variant" pkg/config/registry/ -A 2 | grep -E "test|Test"

Repository: aquaproj/aqua

Length of output: 1073


🏁 Script executed:

# Look for the schema generation code to understand jsonschema tag handling
rg -l "jsonschema" pkg/ | head -10

Repository: aquaproj/aqua

Length of output: 365


🏁 Script executed:

# Check the package_info.go for complete context around Override definition
sed -n '210,230p' pkg/config/registry/package_info.go | cat -n

Repository: aquaproj/aqua

Length of output: 1799


🏁 Script executed:

# Check if there are any cases where Variant is created with empty fields
rg -n "Variant\s*{" pkg/ -A 3 | grep -v "Key:" | grep -v "test"

Repository: aquaproj/aqua

Length of output: 143


🏁 Script executed:

# Check the actual schema generation logic to understand how omitempty affects required fields
rg -n "required" json-schema/registry.json | head -20

Repository: aquaproj/aqua

Length of output: 250


🏁 Script executed:

# Verify the full Override schema definition
python3 - <<'PY'
import json
with open("json-schema/registry.json", "r", encoding="utf-8") as f:
    schema = json.load(f)
override = schema["$defs"]["Override"]
print("Override.required =", override.get("required", []))
print("\nVariant definition:")
variant = schema["$defs"]["Variant"]
print("  properties:", list(variant.get("properties", {}).keys()))
print("  required:", variant.get("required", []))
print("\nOverride.properties.variants:")
if "variants" in override.get("properties", {}):
    variants_prop = override["properties"]["variants"]
    print("  ", variants_prop)
PY

Repository: aquaproj/aqua

Length of output: 210


🏁 Script executed:

# Let's check how the runtime matching works with empty values
sed -n '45,75p' pkg/config/registry/override.go | cat -n

Repository: aquaproj/aqua

Length of output: 530


🏁 Script executed:

# Verify if there's any defensive code that handles empty Key or Value
rg -n "Key == |Value == " pkg/config/registry/

Repository: aquaproj/aqua

Length of output: 39


🏁 Script executed:

# Check Variants definition to understand the list behavior
sed -n '192,196p' pkg/config/registry/package_info.go | cat -n

Repository: aquaproj/aqua

Length of output: 299


🏁 Script executed:

# Search for any YAML unmarshaling or config loading that might handle empty variants
rg -n "Variants" pkg/config/ -B 2 -A 2 | head -30

Repository: aquaproj/aqua

Length of output: 1679


🏁 Script executed:

# Check if there's any code that might intentionally create Variant with empty fields
rg -n "Key.*:|Value.*:" pkg/config/registry/ | grep -v "test\|json\|yaml\|jsonschema"

Repository: aquaproj/aqua

Length of output: 343


🏁 Script executed:

# Verify the supportedVariantKeys implementation to understand empty key behavior
rg -n "supportedVariantKeys" pkg/config/registry/ -B 2 -A 5

Repository: aquaproj/aqua

Length of output: 1536


🏁 Script executed:

# Double-check if there are any integration tests or config files that might have empty variants
find . -name "*.yaml" -o -name "*.yml" -o -name "*.json" | xargs rg -l "variants" 2>/dev/null | head -5

Repository: aquaproj/aqua

Length of output: 113


🏁 Script executed:

# Verify the specific lines mentioned in the review
sed -n '186,191p' pkg/config/registry/package_info.go | cat -n

Repository: aquaproj/aqua

Length of output: 329


🏁 Script executed:

# Check if there's any validation or config loading that checks for empty Variant fields
rg -n "Variant" pkg/ --type go -B 3 -A 3 | grep -E "empty|nil|validate" | head -20

Repository: aquaproj/aqua

Length of output: 1158


🏁 Script executed:

# Let's understand the exact behavior when Key is empty
python3 - <<'PY'
# Simulate the matching logic
supportedVariantKeys = {"libc": None}

# Test case 1: empty Key
v_key = ""
v_value = "musl"

print("Test 1: Key='', Value='musl'")
if v_key in supportedVariantKeys:
    print(f"  v.Key '{v_key}' is supported")
else:
    print(f"  v.Key '{v_key}' is NOT supported - override would NOT match")

# Test case 2: valid Key
v_key = "libc"
v_value = "musl"
print("\nTest 2: Key='libc', Value='musl'")
if v_key in supportedVariantKeys:
    print(f"  v.Key '{v_key}' is supported - would check value")
else:
    print(f"  v.Key '{v_key}' is NOT supported")
PY

Repository: aquaproj/aqua

Length of output: 223


🏁 Script executed:

# Check if jsonschema library handles omitempty tag to generate required fields
find . -name "*.go" -type f | xargs rg -l "jsonschema\|json.Marshal" | xargs rg -l "omitempty" | head -3

Repository: aquaproj/aqua

Length of output: 149


🏁 Script executed:

# Check if there are any tests that verify behavior with empty Key or Value
rg -n "Key.*\"\"" pkg/config/registry/override_test.go

Repository: aquaproj/aqua

Length of output: 39


🏁 Script executed:

# Verify the Variant matching logic doesn't have defensive code for empty fields
cat -n pkg/config/registry/override.go | sed -n '40,60p'

Repository: aquaproj/aqua

Length of output: 675


🏁 Script executed:

# Check if removing omitempty could break backward compatibility by searching for examples
find . -type f \( -name "*.yaml" -o -name "*.yml" -o -name "*.json" \) -exec rg -l "variants" {} \; 2>/dev/null

Repository: aquaproj/aqua

Length of output: 113


🏁 Script executed:

# Verify the full context of how omitempty affects schema generation in this project
rg -n "omitempty" json-schema/registry.json | head -10

Repository: aquaproj/aqua

Length of output: 39


🏁 Script executed:

# Let's verify if removing omitempty from the struct tags would actually affect the generated schema
# by checking how schema generation works in this project
rg -n "schema\|jsonschema" pkg/config/ --type go -A 2 | head -20

Repository: aquaproj/aqua

Length of output: 39


🏁 Script executed:

# Check the test case mentioned in the first search result
sed -n '195,210p' pkg/config/registry/override_test.go | cat -n

Repository: aquaproj/aqua

Length of output: 520


🏁 Script executed:

# Verify the overall pattern: does the project use omitempty consistently with optional fields?
python3 - <<'PY'
import json
with open("json-schema/registry.json", "r", encoding="utf-8") as f:
    schema = json.load(f)

# Check a few other types to see if they have required fields
for type_name in ["Override", "PackageInfo", "Checksum"]:
    if type_name in schema["$defs"]:
        obj = schema["$defs"][type_name]
        required = obj.get("required", [])
        print(f"{type_name}.required = {required}")
PY

Repository: aquaproj/aqua

Length of output: 135


🏁 Script executed:

# Check what happens with actual config parsing - does omitempty prevent validation errors?
# Look for any validation code that checks for required Variant fields
rg -n "validate|Validate" pkg/config/registry/ --type go | grep -i variant

Repository: aquaproj/aqua

Length of output: 39


🏁 Script executed:

# Final verification: check if there's any code that deserializes Variant and expects optional fields
rg -n "unmarshal\|Unmarshal\|decode\|Decode" pkg/config/registry/ --type go | head -10

Repository: aquaproj/aqua

Length of output: 39


🏁 Script executed:

# Verify line numbers are correct
wc -l pkg/config/registry/package_info.go

Repository: aquaproj/aqua

Length of output: 97


Make Variant.Key and Variant.Value required in config shape.

At lines 188–189, omitempty allows malformed entries (e.g., missing key) to pass deserialization and silently fail during override matching at runtime. The matching logic at override.go:49 checks if Key is in the supported keys map; an empty Key returns false without validation feedback. Removing omitempty enforces these fields at parse time, catching config errors early rather than during runtime matching.

Suggested fix
 type Variant struct {
-	Key   string `yaml:",omitempty" json:"key,omitempty" jsonschema:"enum=libc"`
-	Value string `yaml:",omitempty" json:"value,omitempty"`
+	Key   string `yaml:"key" json:"key" jsonschema:"enum=libc"`
+	Value string `yaml:"value" json:"value"`
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Key string `yaml:",omitempty" json:"key,omitempty" jsonschema:"enum=libc"`
Value string `yaml:",omitempty" json:"value,omitempty"`
Key string `yaml:"key" json:"key" jsonschema:"enum=libc"`
Value string `yaml:"value" json:"value"`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/config/registry/package_info.go` around lines 188 - 189, The struct
fields Variant.Key and Variant.Value are marked with `omitempty`, allowing
missing keys/values to silently pass deserialization; remove `omitempty` from
the YAML/JSON struct tags on `Key` and `Value` in the Variant struct so those
fields are required at parse time (e.g., change `yaml:",omitempty"
json:"key,omitempty"` to `yaml:"" json:"key"` and similarly for `Value`),
ensuring parsing fails for malformed entries and preventing silent misses in the
override matching logic used by the matching code (see the override matching
that checks Variant.Key).

Comment thread pkg/runtime/libc_linux.go Outdated
Make libc detection more robust on minimal images. Stat the well-known
musl dynamic linker / libc alias paths first so distroless or scratch
musl containers (where ldd may not be on PATH) are still detected as
musl. Fall back to `ldd --version` when none of the file checks hit.

Mirrors the multi-stage approach in the official Claude Code install
script (libc.musl-*.so.1 file checks combined with an ldd grep).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

♻️ Duplicate comments (1)
pkg/runtime/libc_linux.go (1)

46-50: ⚠️ Potential issue | 🟠 Major

Bound ldd execution with context timeout (CommandContext).

At Line 46, exec.Command("ldd", "--version") is unbounded and can hang runtime initialization. Use context.WithTimeout + exec.CommandContext and preserve current safe fallback behavior.

Suggested patch
 import (
 	"bytes"
+	"context"
 	"os"
 	"os/exec"
+	"time"
 )
@@
-	cmd := exec.Command("ldd", "--version")
+	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
+	defer cancel()
+	cmd := exec.CommandContext(ctx, "ldd", "--version")
 	var out bytes.Buffer
 	cmd.Stdout = &out
 	cmd.Stderr = &out
 	_ = cmd.Run()
+	if ctx.Err() != nil {
+		return ""
+	}
#!/bin/bash
set -euo pipefail

echo "Inspect libc detection implementation:"
cat -n pkg/runtime/libc_linux.go | sed -n '1,120p'

echo
echo "Check for unbounded exec.Command usage in Go files:"
rg -n --type=go 'exec\.Command\(' -C2

echo
echo "Check timeout/context usage for command execution:"
rg -n --type=go 'context\.WithTimeout\(' -C2
rg -n --type=go 'exec\.CommandContext\(' -C2

As per coding guidelines: "Use context for cancellation and timeouts in Go code".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/runtime/libc_linux.go` around lines 46 - 50, The unbounded
exec.Command("ldd", "--version") can hang; replace it with a context-backed
call: create a context.WithTimeout (e.g., 2s), use exec.CommandContext with that
context to build the command (keeping the same bytes.Buffer for Stdout/Stderr),
defer the cancel(), then call Run() and preserve the existing fallback behavior
by ignoring or handling the error as before; update the code around the cmd
variable creation and execution to use CommandContext and ensure the
timeout/cancel prevents indefinite blocking.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@pkg/runtime/libc_linux.go`:
- Around line 46-50: The unbounded exec.Command("ldd", "--version") can hang;
replace it with a context-backed call: create a context.WithTimeout (e.g., 2s),
use exec.CommandContext with that context to build the command (keeping the same
bytes.Buffer for Stdout/Stderr), defer the cancel(), then call Run() and
preserve the existing fallback behavior by ignoring or handling the error as
before; update the code around the cmd variable creation and execution to use
CommandContext and ensure the timeout/cancel prevents indefinite blocking.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 853a63f6-8818-43c7-8c14-7e794a2439da

📥 Commits

Reviewing files that changed from the base of the PR and between 0052f1e and c98a833.

📒 Files selected for processing (1)
  • pkg/runtime/libc_linux.go

Replace exec.Command with exec.CommandContext (with a defensive 5s
timeout so a hung ldd cannot stall aqua's startup) to satisfy noctx.
Rename the test file to *_internal_test.go to follow the existing
parse_internal_test.go convention so testpackage permits the same-package
test of the unexported detectLibC.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
pkg/runtime/libc_linux_internal_test.go (1)

9-17: Consider table-driven tests for extensibility.

This assertion works, but if supported libc values grow, a table-driven pattern with subtests will scale better and keep expectations explicit.

As per coding guidelines, **/*_test.go: For test files, automatically suggest table-driven test patterns.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/runtime/libc_linux_internal_test.go` around lines 9 - 17, Convert
TestDetectLibC to a table-driven subtest pattern: define a slice of test cases
listing expected valid libc values (e.g., "musl", "gnu", "") and iterate with
t.Run for each case, asserting detectLibC() equals one of the allowed values per
case (or that the returned value is contained in the allowed set); update the
test function name TestDetectLibC and use t.Parallel inside each subtest as
appropriate; reference the detectLibC function and the original TestDetectLibC
test harness when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/runtime/libc_linux.go`:
- Around line 57-63: The current detection treats any non-empty, non-musl ldd
output as libcGNU; change the logic in the libc detection (the block using
out.Bytes(), libcMusl and libcGNU in pkg/runtime/libc_linux.go) to explicitly
detect GNU/glibc tokens (e.g., "GNU libc", "GLIBC", or "gnu") using
bytes.Contains, return libcMusl if musl is found, return libcGNU only when one
of those GNU/glibc tokens is present, and otherwise return "" for
unknown/unexpected non-empty output (preserving the empty-output check).

---

Nitpick comments:
In `@pkg/runtime/libc_linux_internal_test.go`:
- Around line 9-17: Convert TestDetectLibC to a table-driven subtest pattern:
define a slice of test cases listing expected valid libc values (e.g., "musl",
"gnu", "") and iterate with t.Run for each case, asserting detectLibC() equals
one of the allowed values per case (or that the returned value is contained in
the allowed set); update the test function name TestDetectLibC and use
t.Parallel inside each subtest as appropriate; reference the detectLibC function
and the original TestDetectLibC test harness when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4d5a19dd-749d-4639-83e7-ade4a5383378

📥 Commits

Reviewing files that changed from the base of the PR and between c98a833 and 55eba3e.

📒 Files selected for processing (2)
  • pkg/runtime/libc_linux.go
  • pkg/runtime/libc_linux_internal_test.go

Comment thread pkg/runtime/libc_linux.go
Comment on lines +57 to +63
if bytes.Contains(out.Bytes(), []byte(libcMusl)) {
return libcMusl
}
if out.Len() == 0 {
return ""
}
return libcGNU

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

What stable tokens appear in ldd --version output for glibc across distros/locales, and how do BusyBox/Toybox/uClibc outputs differ?

💡 Result:

Stable tokens in ldd --version output for glibc across distros/locales are: - First line: "ldd (VARIANT GLIBC MAJOR.MINOR[-PATCH]) MAJOR.MINOR" where VARIANT is "Ubuntu GLIBC", "Debian GLIBC", "Debian EGLIBC", "GNU libc", etc., MAJOR.MINOR is the glibc version (e.g. 2.17, 2.21, 2.23, 2.31, 2.36, 2.40). - Second line: "Copyright (C) YEAR Free Software Foundation, Inc." - Third line: "This is free software; see the source for copying conditions. There is NO" - Fourth line: "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - Fifth line: "Written by Roland McGrath and Ulrich Drepper." The VARIANT and exact version/patch vary by distro (Ubuntu adds "Ubuntu GLIBC X.Y-Z", Fedora/Alma "GNU libc", Debian "Debian GLIBC/EGLIBC"), year, and glibc version, but the structure, copyright notice, warranty disclaimer, and authors are stable. Locale does not affect this output as it's not translated. BusyBox: Does not provide ldd (BusyBox implements many commands but ldd is not standard in it; systems using BusyBox often use uClibc/musl static binaries or minimal dynamic setups without ldd). Running ldd if symlinked would show BusyBox version via --help: "BusyBox vX.Y.Z (DATE) multi-call binary.", but no ldd-specific --version output. Toybox: Does not implement ldd (listed as "not started" in status; toybox provides ~200 commands but ldd not included). Commands support --version showing toybox version (e.g. "toybox version 0.8.13"), but no ldd. uClibc: Provides a standalone ldd utility (in utils/ldd.c), but no specific --version output documented; shows library dependencies like glibc ldd when run (e.g. "libc.so.6 => /lib/tls/libc.so.6"), without standard version banner. Can be built/run on glibc hosts.

Citations:


🏁 Script executed:

# First, let's look at the actual file and understand the constants
cat -n pkg/runtime/libc_linux.go | head -70

Repository: aquaproj/aqua

Length of output: 2462


🏁 Script executed:

# Also check for the constants definitions
rg -n "libcMusl|libcGNU" pkg/runtime/

Repository: aquaproj/aqua

Length of output: 373


Avoid treating any non-musl output as GNU libc.

At line 63, non-empty output that does not contain "musl" is always classified as gnu. This violates the function's documented contract to return "" when detection is not possible, and can misclassify systems with missing or unexpected ldd --version output (e.g., systems with alternative libc implementations or no ldd available). Glibc's ldd --version reliably outputs "GNU libc" or "GLIBC" version strings; explicitly match these tokens rather than assuming any unrecognized output is GNU.

Proposed fix
 	_ = cmd.Run()
-	if bytes.Contains(out.Bytes(), []byte(libcMusl)) {
+	if ctx.Err() != nil {
+		return ""
+	}
+	b := bytes.ToLower(out.Bytes())
+	if bytes.Contains(b, []byte(libcMusl)) {
 		return libcMusl
 	}
-	if out.Len() == 0 {
-		return ""
-	}
-	return libcGNU
+	if bytes.Contains(b, []byte("gnu libc")) || bytes.Contains(b, []byte("glibc")) {
+		return libcGNU
+	}
+	return ""
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/runtime/libc_linux.go` around lines 57 - 63, The current detection treats
any non-empty, non-musl ldd output as libcGNU; change the logic in the libc
detection (the block using out.Bytes(), libcMusl and libcGNU in
pkg/runtime/libc_linux.go) to explicitly detect GNU/glibc tokens (e.g., "GNU
libc", "GLIBC", or "gnu") using bytes.Contains, return libcMusl if musl is
found, return libcGNU only when one of those GNU/glibc tokens is present, and
otherwise return "" for unknown/unexpected non-empty output (preserving the
empty-output check).

Extract the ldd timeout into a named const (lddTimeout) so mnd no longer
flags the literal. detectLibC runs once at startup and is not part of any
cancellable user operation, so it intentionally creates a fresh context
rather than threading one through runtime.New's many callers; document
this and silence contextcheck at the source.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

♻️ Duplicate comments (1)
pkg/runtime/libc_linux.go (1)

62-68: ⚠️ Potential issue | 🟠 Major

Do not default unknown ldd output to GNU libc.

At Line 68, the current fallback classifies any non-empty output as gnu. This can mis-detect cases like missing ldd or unexpected stderr and cause incorrect variant matches downstream.

Suggested fix
 	_ = cmd.Run()
-	if bytes.Contains(out.Bytes(), []byte(libcMusl)) {
+	if ctx.Err() != nil {
+		return ""
+	}
+	b := bytes.ToLower(out.Bytes())
+	if bytes.Contains(b, []byte(libcMusl)) {
 		return libcMusl
 	}
-	if out.Len() == 0 {
-		return ""
-	}
-	return libcGNU
+	if bytes.Contains(b, []byte("gnu libc")) || bytes.Contains(b, []byte("glibc")) {
+		return libcGNU
+	}
+	return ""
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/runtime/libc_linux.go` around lines 62 - 68, The code currently defaults
any non-empty ldd output to libcGNU (the final "return libcGNU"), which
misclassifies unknown or error outputs; update the fallback so that only an
explicit match of libcMusl (bytes.Contains(out.Bytes(), []byte(libcMusl)))
returns libcMusl and all other cases return the unknown/empty value (keep the
existing empty-string behavior used when out.Len() == 0) instead of returning
libcGNU; change the final return from libcGNU to "" (or the function's explicit
unknown constant) so unexpected ldd output doesn't get mis-detected as GNU libc.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@pkg/runtime/libc_linux.go`:
- Around line 62-68: The code currently defaults any non-empty ldd output to
libcGNU (the final "return libcGNU"), which misclassifies unknown or error
outputs; update the fallback so that only an explicit match of libcMusl
(bytes.Contains(out.Bytes(), []byte(libcMusl))) returns libcMusl and all other
cases return the unknown/empty value (keep the existing empty-string behavior
used when out.Len() == 0) instead of returning libcGNU; change the final return
from libcGNU to "" (or the function's explicit unknown constant) so unexpected
ldd output doesn't get mis-detected as GNU libc.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 67b685bd-9bc0-422a-bf01-677b10e6f329

📥 Commits

Reviewing files that changed from the base of the PR and between 55eba3e and 04c45d6.

📒 Files selected for processing (1)
  • pkg/runtime/libc_linux.go

suzuki-shunsuke and others added 2 commits April 29, 2026 18:42
…ction

Make runtime.New and runtime.NewR take a context so detectLibC can use
exec.CommandContext with the caller's context. This satisfies both the
noctx (use CommandContext) and contextcheck (propagate the parent
context) lint rules without per-call //nolint directives.

Update all callers:
- pkg/cli/runner.go and pkg/controller/exec/exec.go (updateTimestamp now
  takes a ctx) thread the request-scoped context through.
- Constructors and factories that have no ctx in scope
  (cmd/aqua/main.go, installpackage.New, minisign/ghattestation/slsa/
  cosign NewExecutor/NewVerifier) use context.Background().
- Tests use t.Context().

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lers

The New constructor previously called runtime.NewR() five times — once per
dedicated installer (cosign, slsa, minisign, gh) plus once inside
newInstaller for the realRuntime field. With libc detection now part of
NewR, each call performed file stats and could invoke ldd --version,
which was wasteful for what is the same host runtime value.

Compute the real runtime once at the top of New and thread it through
both newInstaller (as a new realRT parameter) and the four dedicated
installer wirings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

♻️ Duplicate comments (1)
pkg/runtime/libc_linux.go (1)

52-58: ⚠️ Potential issue | 🟠 Major

Avoid defaulting unknown ldd output to GNU libc.

At Line 58, any non-empty output that doesn’t contain "musl" is treated as GNU. That can misclassify unexpected ldd output and incorrectly match libc=gnu overrides.

Proposed fix
 	_ = cmd.Run()
-	if bytes.Contains(out.Bytes(), []byte(libcMusl)) {
+	b := bytes.ToLower(out.Bytes())
+	if bytes.Contains(b, []byte(libcMusl)) {
 		return libcMusl
 	}
-	if out.Len() == 0 {
+	if out.Len() == 0 {
 		return ""
 	}
-	return libcGNU
+	if bytes.Contains(b, []byte("gnu libc")) || bytes.Contains(b, []byte("glibc")) {
+		return libcGNU
+	}
+	return ""
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/runtime/libc_linux.go` around lines 52 - 58, The current detection code
checks bytes.Contains(out.Bytes(), []byte(libcMusl)) and otherwise returns
libcGNU for any non-empty ldd output, which wrongly defaults unknown output to
GNU; change the logic so you only return libcGNU when the output explicitly
indicates GNU (e.g., match a known GNU marker or explicit "glibc"/"gnu" token),
otherwise return the empty/unknown value (keep returning "" when out.Len() == 0
or when no known marker is found). Update the branch that now returns libcGNU to
instead perform an explicit check for a GNU indicator before returning libcGNU,
and return "" for all other unexpected ldd outputs to avoid misclassification
(referencing the symbols libcMusl, libcGNU, out and the bytes.Contains call to
locate the code).
🧹 Nitpick comments (1)
pkg/runtime/runtime.go (1)

24-33: Add doc comments for exported constructors New and NewR.

These signatures changed and now carry distinct libc behavior (New honors AQUA_LIBC, NewR uses detection only), so comments are important for callers.

As per coding guidelines "Add comments for all exported functions and types".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/runtime/runtime.go` around lines 24 - 33, Add Go doc comments for the
exported constructors New and NewR on type Runtime that explain their differing
libc semantics: document that New(ctx context.Context) constructs a Runtime
using libc behavior that honors the AQUA_LIBC environment/configuration, while
NewR(ctx context.Context) constructs a Runtime that uses automatic libc
detection only; place the comments immediately above the respective New and NewR
function declarations and follow the standard Go comment style starting with the
function name.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/minisign/exec.go`:
- Around line 32-33: NewExecutor currently constructs the minisign runtime with
a background context (runtime.NewR(context.Background())), which ignores caller
cancellation; change the NewExecutor signature to accept a context.Context
parameter (e.g., func NewExecutor(ctx context.Context, logger *slog.Logger,
executor CommandExecutor, param *config.Param) (*ExecutorImpl, error)) and pass
that ctx into runtime.NewR(ctx), and propagate this signature change to the
wire/init call sites that create ExecutorImpl so callers can supply their
context; update any tests or callers of NewExecutor accordingly and ensure
ExecutorImpl construction still returns the same types.

In `@pkg/slsa/exec.go`:
- Around line 31-33: Change NewExecutor to accept a caller context and propagate
it into runtime.NewR instead of using context.Background(): update the
NewExecutor signature to NewExecutor(ctx context.Context, executor
CommandExecutor, param *config.Param) and call runtime.NewR(ctx) when
initializing the runtime; update all callers (the DI entry in
pkg/controller/wire.go to pass the injected ctx into slsa.NewExecutor) so the
injected context flows through (wire_gen.go will be regenerated/updated for
other call sites).

---

Duplicate comments:
In `@pkg/runtime/libc_linux.go`:
- Around line 52-58: The current detection code checks
bytes.Contains(out.Bytes(), []byte(libcMusl)) and otherwise returns libcGNU for
any non-empty ldd output, which wrongly defaults unknown output to GNU; change
the logic so you only return libcGNU when the output explicitly indicates GNU
(e.g., match a known GNU marker or explicit "glibc"/"gnu" token), otherwise
return the empty/unknown value (keep returning "" when out.Len() == 0 or when no
known marker is found). Update the branch that now returns libcGNU to instead
perform an explicit check for a GNU indicator before returning libcGNU, and
return "" for all other unexpected ldd outputs to avoid misclassification
(referencing the symbols libcMusl, libcGNU, out and the bytes.Contains call to
locate the code).

---

Nitpick comments:
In `@pkg/runtime/runtime.go`:
- Around line 24-33: Add Go doc comments for the exported constructors New and
NewR on type Runtime that explain their differing libc semantics: document that
New(ctx context.Context) constructs a Runtime using libc behavior that honors
the AQUA_LIBC environment/configuration, while NewR(ctx context.Context)
constructs a Runtime that uses automatic libc detection only; place the comments
immediately above the respective New and NewR function declarations and follow
the standard Go comment style starting with the function name.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5417ee01-3c6d-461f-bbae-a70d519a1925

📥 Commits

Reviewing files that changed from the base of the PR and between 04c45d6 and 7118a1f.

📒 Files selected for processing (15)
  • cmd/aqua/main.go
  • pkg/cli/runner.go
  • pkg/config/package_internal_test.go
  • pkg/config/package_test.go
  • pkg/controller/exec/exec.go
  • pkg/cosign/verify.go
  • pkg/ghattestation/exec.go
  • pkg/installpackage/installer.go
  • pkg/minisign/exec.go
  • pkg/runtime/libc_linux.go
  • pkg/runtime/libc_linux_internal_test.go
  • pkg/runtime/libc_other.go
  • pkg/runtime/runtime.go
  • pkg/runtime/runtime_test.go
  • pkg/slsa/exec.go
✅ Files skipped from review due to trivial changes (3)
  • pkg/ghattestation/exec.go
  • cmd/aqua/main.go
  • pkg/cosign/verify.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/runtime/libc_linux_internal_test.go
  • pkg/runtime/libc_other.go

Comment thread pkg/minisign/exec.go
Comment on lines 32 to +33
func NewExecutor(logger *slog.Logger, executor CommandExecutor, param *config.Param) (*ExecutorImpl, error) {
rt := runtime.NewR()
rt := runtime.NewR(context.Background())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Identify minisign.NewExecutor call sites for context propagation.
rg -nP --type=go '\bminisign\.NewExecutor\s*\(' -C 2

Repository: aquaproj/aqua

Length of output: 1773


🏁 Script executed:

# Try listing the repository structure and accessing the file directly
git ls-files | head -20

Repository: aquaproj/aqua

Length of output: 866


🏁 Script executed:

# Search for minisign.NewExecutor in Go files
rg 'minisign\.NewExecutor' --type=go -C 2

Repository: aquaproj/aqua

Length of output: 2961


🏁 Script executed:

# Look for the NewExecutor function definition in the minisign package
rg 'func NewExecutor' --type=go -C 5

Repository: aquaproj/aqua

Length of output: 1797


🏁 Script executed:

# Check the wire_gen.go functions that call minisign.NewExecutor
rg -B10 'minisign\.NewExecutor\(' pkg/controller/wire_gen.go | head -50

Repository: aquaproj/aqua

Length of output: 2589


🏁 Script executed:

# Check if there's a context parameter in wire_gen.go functions
rg 'func.*wire_gen\.go' --type=go -A5 pkg/controller/wire_gen.go | head -40

Repository: aquaproj/aqua

Length of output: 39


🏁 Script executed:

# Search for context usage in the wire setup or controller initialization
rg 'context\.' pkg/controller/wire.go -C 2

Repository: aquaproj/aqua

Length of output: 3500


Thread caller context into minisign runtime initialization.

At line 33, runtime.NewR(context.Background()) ignores caller cancellation and timeouts. The NewExecutor function should accept context.Context as a parameter and pass it to runtime.NewR. Context is already available at all call sites through the wire initialization functions.

This violates the coding guideline: "Use context for cancellation and timeouts in Go code."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/minisign/exec.go` around lines 32 - 33, NewExecutor currently constructs
the minisign runtime with a background context
(runtime.NewR(context.Background())), which ignores caller cancellation; change
the NewExecutor signature to accept a context.Context parameter (e.g., func
NewExecutor(ctx context.Context, logger *slog.Logger, executor CommandExecutor,
param *config.Param) (*ExecutorImpl, error)) and pass that ctx into
runtime.NewR(ctx), and propagate this signature change to the wire/init call
sites that create ExecutorImpl so callers can supply their context; update any
tests or callers of NewExecutor accordingly and ensure ExecutorImpl construction
still returns the same types.

Comment thread pkg/slsa/exec.go
Comment on lines 31 to 33
func NewExecutor(executor CommandExecutor, param *config.Param) *ExecutorImpl {
rt := runtime.NewR()
rt := runtime.NewR(context.Background())
return &ExecutorImpl{

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify call sites that would need context threading if NewExecutor signature changes.
rg -nP --type=go '\bslsa\.NewExecutor\s*\(' -C 2

Repository: aquaproj/aqua

Length of output: 1769


🏁 Script executed:

#!/bin/bash
# Verify call sites that would need context threading if NewExecutor signature changes.
rg -nP --type=go '\bslsa\.NewExecutor\s*\(' -C 2

Repository: aquaproj/aqua

Length of output: 5396


🏁 Script executed:

#!/bin/bash
# Check the full NewExecutor implementation and surrounding context
cat -n pkg/slsa/exec.go | head -50

Repository: aquaproj/aqua

Length of output: 1705


🏁 Script executed:

#!/bin/bash
# Check if wire_gen.go is auto-generated and look for wire build file
head -20 pkg/controller/wire_gen.go

Repository: aquaproj/aqua

Length of output: 560


🏁 Script executed:

#!/bin/bash
# Search for wire.go or go:generate directives
find . -name "wire.go" -o -name "*wire*.go" | grep -v wire_gen | head -20

Repository: aquaproj/aqua

Length of output: 82


🏁 Script executed:

#!/bin/bash
# Check wire.go to see how providers are defined
cat pkg/controller/wire.go

Repository: aquaproj/aqua

Length of output: 39130


🏁 Script executed:

#!/bin/bash
# Search for runtime.NewR implementation
rg -nA 10 "func NewR" pkg/runtime/ --type=go

Repository: aquaproj/aqua

Length of output: 553


🏁 Script executed:

#!/bin/bash
# Search for other usages of runtime.NewR to ensure consistency
rg -n "runtime\.NewR\(" --type=go

Repository: aquaproj/aqua

Length of output: 403


Propagate caller context into runtime.NewR in NewExecutor.

At line 32, context.Background() drops caller cancellation/deadline for runtime libc detection. Pass a caller context into NewExecutor and use it here.

💡 Suggested fix
-func NewExecutor(executor CommandExecutor, param *config.Param) *ExecutorImpl {
-	rt := runtime.NewR(context.Background())
+func NewExecutor(ctx context.Context, executor CommandExecutor, param *config.Param) *ExecutorImpl {
+	rt := runtime.NewR(ctx)
 	return &ExecutorImpl{

Update pkg/controller/wire.go to pass the injected ctx to slsa.NewExecutor. The auto-generated wire_gen.go will update all 12 call sites.

As per coding guidelines: "Use context for cancellation and timeouts in Go code."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/slsa/exec.go` around lines 31 - 33, Change NewExecutor to accept a caller
context and propagate it into runtime.NewR instead of using
context.Background(): update the NewExecutor signature to NewExecutor(ctx
context.Context, executor CommandExecutor, param *config.Param) and call
runtime.NewR(ctx) when initializing the runtime; update all callers (the DI
entry in pkg/controller/wire.go to pass the injected ctx into slsa.NewExecutor)
so the injected context flows through (wire_gen.go will be regenerated/updated
for other call sites).

@suzuki-shunsuke suzuki-shunsuke added this to the v2.58.0 milestone Apr 29, 2026
@suzuki-shunsuke suzuki-shunsuke merged commit eb8b477 into main Apr 29, 2026
30 checks passed
@suzuki-shunsuke suzuki-shunsuke deleted the feat/variants-libc branch April 29, 2026 12:50
@github-project-automation github-project-automation Bot moved this to Done in main Apr 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant