Skip to content

RTECO-945: Add Alpine APK command support (apk add/upload/config) - #496

Open
naveenku-jfrog wants to merge 3 commits into
mainfrom
RTECO-945-alpine
Open

RTECO-945: Add Alpine APK command support (apk add/upload/config)#496
naveenku-jfrog wants to merge 3 commits into
mainfrom
RTECO-945-alpine

Conversation

@naveenku-jfrog

@naveenku-jfrog naveenku-jfrog commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements the JFrog CLI Artifactory layer for Alpine APK integration: jf apk add, jf apk upload, and jf apk config. This layer sits between the JFrog CLI frontend (jfrog-cli) and the build-info library (build-info-go), handling credential injection, build-info orchestration, AQL checksum enrichment, and repository configuration.

Depends on: build-info-go#393


What Changed

New: artifactory/commands/alpine/apkcommand.go

The main jf apk <subcommand> wrapper:

  • ApkCommand — wraps the native apk binary with credential injection and Build Info collection
  • Pre/post snapshot diffing — takes a package snapshot before invoking native apk, diffs after to identify newly installed packages
  • buildEnvWithHTTPAuth() — injects HTTP_AUTH=basic:<host>:<user>:<pass> into the subprocess environment so apk-tools ≥ 2.12 can authenticate against Artifactory virtual repositories without touching /etc/apk/repositories
  • collectBuildInfo() — two-phase collection: local checksums first (from APK DB C: field), then AQL enrichment for SHA-256/MD5 of packages that went through Artifactory
  • enrichChecksumsFromAQL() — batched AQL query against the specified virtual repo to resolve SHA-256, SHA-1, and MD5 for any deps with missing checksums
  • stripJFFlags() / extractPackageNames() — strips JFrog-specific flags (--repo, --build-name, etc.) before forwarding args to native apk
  • filterSecretEnvVars() — respects JFROG_CLI_ENV_EXCLUDE to prevent secrets leaking into the subprocess environment or build-info records

New: artifactory/commands/alpine/apkupload.go

Publishes a local .apk file to an Artifactory Alpine repository:

  • Constructs the correct Artifactory path (<repo>/<branch>/<arch>/<name>-<version>.apk)
  • Stamps CI/VCS properties on the uploaded artifact when running in a CI environment (GitHub Actions, Jenkins, GitLab CI, etc.)
  • Supports --branch, --arch overrides

New: artifactory/commands/alpine/apkconfig.go

Configures the local Alpine client to use Artifactory as the package source:

  • Downloads the Artifactory RSA public key and writes it to /etc/apk/keys/
  • Rewrites /etc/apk/repositories to replace CDN URLs with the Artifactory virtual repository URL
  • --apply flag controls whether changes are written to disk or previewed
  • Restores original /etc/apk/repositories on write failure to avoid leaving the system in a broken state

New: artifactory/commands/alpine/credentials.go

Credential resolution helpers:

  • resolveHTTPAuthCredentials() — merges server config, explicit --user/--password flags, and token-based auth
  • buildHTTPAuth() — formats the HTTP_AUTH=basic:<host>:<user>:<pass> string for apk-tools

Design Notes

No routing change without jf apk config: jf apk add --repo=X alone does not change /etc/apk/repositories. Packages are fetched from wherever apk is currently configured (CDN by default). The --repo flag affects HTTP_AUTH injection and AQL enrichment only. This is intentional — routing all traffic through Artifactory requires an explicit jf apk config step, matching the security model of the apt and yum integrations.

--repo is optional for build-info: Consistent with jf npm install and jf pip install, omitting --repo does not block build-info collection. Dependencies are still recorded with checksums from the APK DB. AQL enrichment (for SHA-256/MD5) is simply skipped.

Unknown --server-id is a hard error: If --server-id is explicitly provided and the server is not in the config, the command fails immediately — consistent with every other JFrog CLI package manager command.

Dependency ID format: name:version (e.g. curl:8.14.1-r2), consistent with npm, pip, and Maven modules.

Summary by CodeRabbit

  • New Features

    • Added Alpine APK package management through Artifactory, including authenticated installs and package handling.
    • Added APK uploads with metadata, dependency information, and optional build details.
    • Added Alpine repository configuration for local, remote, and virtual repositories.
    • Added repository validation, credential resolution, signing-key setup, and secure configuration updates.
  • Tests

    • Added comprehensive coverage for APK commands, uploads, dependencies, credentials, repository setup, and security-related scenarios.

@naveenku-jfrog naveenku-jfrog changed the title Rteco 945 alpine RTECO-945: Add Alpine APK command support (apk add/upload/config) Jul 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

👍 Frogbot scanned this pull request and did not find any new security issues.


Comment thread artifactory/commands/setup/setup.go
Comment thread artifactory/commands/alpine/apkcommand.go
Comment thread artifactory/commands/alpine/apkcommand.go Outdated
Comment thread artifactory/commands/alpine/apkupload.go Outdated
Comment thread artifactory/commands/alpine/apkupload.go
Comment thread artifactory/commands/alpine/apkcommand.go Outdated
Comment thread artifactory/commands/alpine/apkcommand.go Outdated
Comment thread artifactory/commands/setup/setup.go Outdated
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

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

Adds Alpine APK setup, command execution, repository isolation, credential handling, package upload, dependency resolution, and Build Info collection with unit coverage and updated JFrog modules.

Changes

Alpine APK support

Layer / File(s) Summary
Alpine repository setup
artifactory/commands/setup/setup.go, artifactory/commands/setup/setup_test.go
Adds Alpine repository selection, validation, credential embedding, signing-key installation, repository-file updates, and setup tests.
APK command execution and Build Info
artifactory/commands/alpine/apkcommand.go, artifactory/commands/alpine/credentials.go, artifactory/commands/alpine/apkcommand_test.go, go.mod
Adds the ApkCommand wrapper, argument filtering, repository and cache isolation, HTTP authentication, exit handling, Build Info collection, checksum enrichment, credential resolution, helper tests, and JFrog module updates.
APK upload and metadata recording
artifactory/commands/alpine/apkupload.go, artifactory/commands/alpine/apkupload_test.go
Adds APK metadata detection, repository resolution, artifact properties, dependency collection, checksum enrichment, upload validation, and Build Info recording.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • jfrog/jfrog-cli-artifactory#499: Adds a native package-manager command with Artifactory authentication, repository handling, and Build Info collection for another ecosystem.

Suggested reviewers: bhanurp, itsmeleela

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SetupCommand
  participant ApkCommand
  participant ApkUploadCommand
  participant apk
  participant Artifactory
  User->>SetupCommand: configure Alpine repository
  SetupCommand->>Artifactory: validate repository and retrieve signing key
  SetupCommand->>RepositoriesFile: update Alpine repository configuration
  User->>ApkCommand: invoke APK operation
  ApkCommand->>apk: run filtered command with repository and HTTP_AUTH
  apk-->>ApkCommand: return package state and exit status
  User->>ApkUploadCommand: upload local APK
  ApkUploadCommand->>apk: inspect package metadata and dependencies
  ApkUploadCommand->>Artifactory: upload APK and save Build Info
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.60% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the addition of Alpine APK command support for add, upload, and config operations.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch RTECO-945-alpine

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.

@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: 7

🧹 Nitpick comments (3)
artifactory/commands/alpine/apkcommand.go (1)

459-483: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Re-resolve apk via apkPath and drop the redundant lookup.

warnIfApkTooOld shells out to apk --version by name, re-searching PATH even though Run already resolved apkPath (Line 142); passing the resolved path avoids picking up a different binary. Also, parts[0] is printed as a string while minor is printed as an int — using fields[1] directly would be clearer.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/alpine/apkcommand.go` around lines 459 - 483, Update
warnIfApkTooOld to accept the resolved apkPath from Run and pass it to
exec.Command instead of re-resolving “apk” through PATH. Remove the redundant
lookup, and use the parsed version field directly in the warning message rather
than mixing parts[0] with the integer minor value.
artifactory/commands/setup/setup.go (1)

779-795: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate Alpine-version detection.

detectAlpineVersion here and detectSystemAlpineVersion in artifactory/commands/alpine/apkupload.go (Lines 648-660) parse the same file with the same logic, differing only in the v prefix. Consider exporting one helper and normalizing at the call site.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/setup/setup.go` around lines 779 - 795, Consolidate the
duplicated Alpine release parsing by reusing a single exported helper between
detectAlpineVersion and detectSystemAlpineVersion. Move the shared file-reading
and version-normalization logic into that helper, then apply the required
v-prefix difference at each call site while preserving both existing callers’
behavior.
artifactory/commands/alpine/apkcommand_test.go (1)

101-123: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

These two tests depend on the ambient JFROG_CLI_ENV_EXCLUDE.

filterSecretEnvVars reads coreutils.EnvExclude first; if a developer or CI runner has it set, both tests fail or pass vacuously. Pin it with t.Setenv(coreutils.EnvExclude, "*password*;*secret*;*token*;*key*").

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/alpine/apkcommand_test.go` around lines 101 - 123, Make
both TestFilterSecretEnvVars_RemovesSecrets and
TestFilterSecretEnvVars_PreservesNonSecrets deterministic by setting
coreutils.EnvExclude to "*password*;*secret*;*token*;*key*" with t.Setenv at the
start of each test, before calling filterSecretEnvVars.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@artifactory/commands/alpine/apkcommand.go`:
- Around line 283-297: Update userExplicitFlags in the environment-credential
handling flow to represent only credentials explicitly supplied via the --user,
--password, or --server-id flags; do not include apkCmd.serverDetails, which is
populated by default server configuration. Preserve the existing behavior of
honoring pre-set HTTP_AUTH when none of those explicit flags were provided.

In `@artifactory/commands/alpine/apkupload.go`:
- Line 206: Update the uploadURL construction in the APK upload flow to
normalize the Artifactory base URL before joining it with target. Trim trailing
slashes from rtURL, then insert exactly one separator before target.
- Around line 277-289: Update the artifact upload flow around getBuildProps,
SearchFiles, and SetProps: propagate the getBuildProps error instead of ignoring
it, and ensure the ContentReader returned by SearchFiles is closed after
SetProps completes, including when SetProps returns an error.
- Around line 436-460: Update resolveRepoFromRepositoriesFile to parse each
repository line with net/url instead of matching the raw string prefix, so
credentials in the URL userinfo do not prevent resolution. Compare the parsed
URL’s host and normalized path against rtURL’s host and Artifactory path, then
extract the repository key from the remaining path while preserving skipping
invalid, empty, and commented lines.

In `@artifactory/commands/setup/setup.go`:
- Around line 942-959: Update the documentation comment for
apkUpdateRepositories to accurately state that it replaces the existing
repositories file with the Artifactory repository entry, rather than claiming
unrelated third-party entries are preserved. Do not change the write behavior or
add an opt-in gate.
- Around line 700-722: The raw HTTP calls must use the configured Artifactory
client instead of http.DefaultClient. In artifactory/commands/setup/setup.go
lines 700-722, update apkValidateRepositoryExists and the sibling keypair calls
to use the service-manager equivalent such as IsRepoExists, removing apkSetAuth;
in artifactory/commands/alpine/apkupload.go lines 239-260, route the upload PUT
through the Artifactory upload service or configured client, preserving
configured timeouts, transport, authentication, and retry behavior.
- Around line 913-936: Update the non-root branch of apkWriteFile to create and
write the credential-bearing file with restrictive permissions from the start,
replacing the sudo tee-then-chmod sequence with a safe sudo mechanism such as
install -m 600 /dev/stdin or an equivalent umask-controlled command. Preserve
the existing content, path handling, error propagation, and root-path behavior;
do not alter the safe exec.Command usage for perm.

---

Nitpick comments:
In `@artifactory/commands/alpine/apkcommand_test.go`:
- Around line 101-123: Make both TestFilterSecretEnvVars_RemovesSecrets and
TestFilterSecretEnvVars_PreservesNonSecrets deterministic by setting
coreutils.EnvExclude to "*password*;*secret*;*token*;*key*" with t.Setenv at the
start of each test, before calling filterSecretEnvVars.

In `@artifactory/commands/alpine/apkcommand.go`:
- Around line 459-483: Update warnIfApkTooOld to accept the resolved apkPath
from Run and pass it to exec.Command instead of re-resolving “apk” through PATH.
Remove the redundant lookup, and use the parsed version field directly in the
warning message rather than mixing parts[0] with the integer minor value.

In `@artifactory/commands/setup/setup.go`:
- Around line 779-795: Consolidate the duplicated Alpine release parsing by
reusing a single exported helper between detectAlpineVersion and
detectSystemAlpineVersion. Move the shared file-reading and
version-normalization logic into that helper, then apply the required v-prefix
difference at each call site while preserving both existing callers’ behavior.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b632789-efca-4012-a13f-b9f20e8cabff

📥 Commits

Reviewing files that changed from the base of the PR and between 2227ac7 and acf804d.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • artifactory/commands/alpine/apkcommand.go
  • artifactory/commands/alpine/apkcommand_test.go
  • artifactory/commands/alpine/apkupload.go
  • artifactory/commands/alpine/credentials.go
  • artifactory/commands/setup/setup.go
  • artifactory/commands/setup/setup_test.go
  • go.mod

Comment thread artifactory/commands/alpine/apkcommand.go Outdated
Comment thread artifactory/commands/alpine/apkupload.go Outdated
Comment thread artifactory/commands/alpine/apkupload.go Outdated
Comment thread artifactory/commands/alpine/apkupload.go
Comment thread artifactory/commands/setup/setup.go
Comment thread artifactory/commands/setup/setup.go Outdated
Comment thread artifactory/commands/setup/setup.go Outdated

@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

🧹 Nitpick comments (8)
artifactory/commands/alpine/apkcommand_test.go (1)

208-211: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the signature-hint result.

This test has no observable assertion. It passes if emitSignatureHint does nothing or emits a hint for every input. Capture the log output, or expose a testable result, and assert that only the signature input produces the hint.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/alpine/apkcommand_test.go` around lines 208 - 211,
Update TestEmitSignatureHint_DetectsPatterns to observe emitSignatureHint
output, capturing its log or using a testable return value, and assert that the
UNTRUSTED signature input produces a hint while the ordinary input produces
none.
artifactory/commands/alpine/apkcommand.go (2)

565-599: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Handle file-name collisions in fileToDep.

fileToDep[fileName] = i keeps only the last index for a given file name. If two dependencies map to the same .apk name, only one of them receives checksums, and the other stays unenriched without any log. Store a slice of indices instead.

The aqlJSONString escaping resolves the earlier raw-interpolation concern in this query.

♻️ Proposed refactor
-	fileToDep := make(map[string]int, len(missing))
+	fileToDeps := make(map[string][]int, len(missing))
 	names := make([]string, 0, len(missing))
 	for _, i := range missing {
 		fileName := apkFileNameFromID(deps[i].Id)
-		fileToDep[fileName] = i
-		names = append(names, fmt.Sprintf(`{"name":%s}`, aqlJSONString(fileName)))
+		if _, seen := fileToDeps[fileName]; !seen {
+			names = append(names, fmt.Sprintf(`{"name":%s}`, aqlJSONString(fileName)))
+		}
+		fileToDeps[fileName] = append(fileToDeps[fileName], i)
 	}
@@
 	for item := new(specutils.ResultItem); reader.NextRecord(item) == nil; item = new(specutils.ResultItem) {
-		i, ok := fileToDep[item.Name]
+		indices, ok := fileToDeps[item.Name]
 		if !ok {
 			continue
 		}
-		deps[i].Sha1 = item.Actual_Sha1
-		deps[i].Sha256 = item.Sha256
-		deps[i].Md5 = item.Actual_Md5
-		resolved++
+		for _, i := range indices {
+			deps[i].Sha1 = item.Actual_Sha1
+			deps[i].Sha256 = item.Sha256
+			deps[i].Md5 = item.Actual_Md5
+			resolved++
+		}
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/alpine/apkcommand.go` around lines 565 - 599, Update the
checksum enrichment flow around fileToDep and the reader.NextRecord loop to
retain all dependency indices for each file name rather than overwriting
duplicates. Map each item.Name to its slice of indices, then apply Actual_Sha1,
Sha256, and Actual_Md5 to every matching dependency and increment resolved
appropriately.

587-603: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Check reader.GetError() after the AQL record loop.

NextRecord returns nil on completion or an actual read error; callers should call reader.GetError() after the loop. If GetError() is non-nil, log the enrichment failure instead of presenting partial enrichment as success.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/alpine/apkcommand.go` around lines 587 - 603, After the
record loop in the AQL enrichment flow, call reader.GetError() and handle any
non-nil error by logging the enrichment failure instead of reporting partial
results as successful. Keep the existing resolved-count success log only for
error-free completion, using the surrounding enrichment function and reader loop
as the change location.
artifactory/commands/alpine/apkupload_test.go (2)

145-162: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add unit coverage for enrichDepsFromLocalCache.

This test exercises collectApkDependencies end-to-end but doesn't populate /var/cache/apk or APKCACHE, so enrichDepsFromLocalCache — the local-cache checksum lookup that replaced the previously-flagged per-dependency apk fetch approach — has no assertion coverage on the success path (checksum actually populated from a cached file). Consider a focused test that sets APKCACHE to a temp dir containing a fixture .apk and asserts Sha1/Sha256 get populated on the matching dependency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/alpine/apkupload_test.go` around lines 145 - 162, Add
focused unit coverage for enrichDepsFromLocalCache rather than relying only on
TestCollectApkDependencies_ScopesMatchAddFlow. Configure APKCACHE to a temporary
directory containing a matching fixture .apk, invoke the cache-enrichment path
with the corresponding dependency, and assert that both Sha1 and Sha256 are
populated from the cached file.

18-43: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a test case for the standalone "." value.

validateArtifactoryPathSegment explicitly rejects value == "." (a separate branch from the ".."/slash checks). None of the current test cases exercise this specific value. Add a {name: "single dot", value: ".", wantErr: true} case to cover it directly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/alpine/apkupload_test.go` around lines 18 - 43, Add a
`"single dot"` table entry with value `"."` and wantErr set to true in
TestValidateArtifactoryPathSegment, covering the validator’s explicit
standalone-dot rejection.
artifactory/commands/alpine/apkupload.go (3)

29-33: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add a non-zero retry backoff for uploads.

uploadHTTPRetryWaitMilliSecs is 0. CreateUploadServiceManager (Line 245-246) uses this for uploadHTTPRetries = 3 retries with no wait. On a transient failure or rate-limit response, this retries immediately three times with no backoff. In CI pipelines that invoke jf apk upload many times in sequence or parallel, this increases load on Artifactory during an outage instead of giving it time to recover.

Set a non-zero wait between retries.

♻️ Proposed fix
 const (
 	uploadThreads                = 1
 	uploadHTTPRetries            = 3
-	uploadHTTPRetryWaitMilliSecs = 0
+	uploadHTTPRetryWaitMilliSecs = 1000
 )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/alpine/apkupload.go` around lines 29 - 33, Update the
uploadHTTPRetryWaitMilliSecs constant used by CreateUploadServiceManager to a
non-zero delay, preserving the existing three-retry behavior while adding
backoff between upload retries.

310-343: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse artifactoryPath instead of rebuilding the same path string.

artifactoryPath (Line 321) and Artifacts[0].Path (Line 332) are built from the same format string and the same fields; a third build of the identical string exists at Line 220 for the upload target. If the path layout ever changes, one of these Sprintf calls can drift from the others and silently desync the recorded Build Info artifact path, the dependency requestedBy path, and the actual upload target.

Reuse the already-computed artifactoryPath for Path.

♻️ Proposed fix
 		Artifacts: []entities.Artifact{{
 			Name: fmt.Sprintf("%s:%s:%s", pkgName, pkgVersion, arch),
-			Path: fmt.Sprintf("%s/%s/%s/%s/%s", apkCmd.repoKey, apkCmd.alpineVersion, apkCmd.branch, arch, filename),
+			Path: artifactoryPath,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/alpine/apkupload.go` around lines 310 - 343, In
recordBuildInfoArtifact, reuse the existing artifactoryPath value for the
artifact’s entities.Artifact.Path instead of rebuilding the identical path with
fmt.Sprintf. Keep artifactoryPath as the single source for the dependency
requestedBy path and recorded artifact path.

439-475: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Batch the apk info fallback instead of resolving one dependency per process.

depsFromApkInfoCommand already supports multiple package names, but every unresolved provider still calls resolveDepID, which spawns one apk info subprocess. Collect unresolved dependency IDs and pass them together instead of making N subprocess calls for one package.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/alpine/apkupload.go` around lines 439 - 475, The
collectApkDependencies flow currently resolves each dependency individually,
causing one apk info subprocess per unresolved provider. Update
resolveDepIDWithProviders usage to collect unresolved dependency IDs during the
loop, then batch-resolve them through the existing multi-package
depsFromApkInfoCommand capability and apply the results back to the
corresponding dependencies, preserving deduplication and existing provider
resolution behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@artifactory/commands/alpine/apkcommand.go`:
- Around line 438-445: Update the apk command execution error handling around
cmd.Run() and exitErr.ExitCode() to normalize signal termination: when
ExitCode() returns -1, return a conventional nonzero exit status instead of
propagating -1, while preserving existing exit codes and stderr signature-hint
emission for regular process failures.

In `@artifactory/commands/setup/setup.go`:
- Around line 930-952: Update apkUpdateRepositories to track whether
apkRepositoriesFile existed separately from originalContent, using the
os.ReadFile result before replacing the not-exist error. In the write-failure
recovery branch, use that existence flag to attempt restoration even when the
prior file content was empty, while preserving the current behavior for files
that did not exist.

---

Nitpick comments:
In `@artifactory/commands/alpine/apkcommand_test.go`:
- Around line 208-211: Update TestEmitSignatureHint_DetectsPatterns to observe
emitSignatureHint output, capturing its log or using a testable return value,
and assert that the UNTRUSTED signature input produces a hint while the ordinary
input produces none.

In `@artifactory/commands/alpine/apkcommand.go`:
- Around line 565-599: Update the checksum enrichment flow around fileToDep and
the reader.NextRecord loop to retain all dependency indices for each file name
rather than overwriting duplicates. Map each item.Name to its slice of indices,
then apply Actual_Sha1, Sha256, and Actual_Md5 to every matching dependency and
increment resolved appropriately.
- Around line 587-603: After the record loop in the AQL enrichment flow, call
reader.GetError() and handle any non-nil error by logging the enrichment failure
instead of reporting partial results as successful. Keep the existing
resolved-count success log only for error-free completion, using the surrounding
enrichment function and reader loop as the change location.

In `@artifactory/commands/alpine/apkupload_test.go`:
- Around line 145-162: Add focused unit coverage for enrichDepsFromLocalCache
rather than relying only on TestCollectApkDependencies_ScopesMatchAddFlow.
Configure APKCACHE to a temporary directory containing a matching fixture .apk,
invoke the cache-enrichment path with the corresponding dependency, and assert
that both Sha1 and Sha256 are populated from the cached file.
- Around line 18-43: Add a `"single dot"` table entry with value `"."` and
wantErr set to true in TestValidateArtifactoryPathSegment, covering the
validator’s explicit standalone-dot rejection.

In `@artifactory/commands/alpine/apkupload.go`:
- Around line 29-33: Update the uploadHTTPRetryWaitMilliSecs constant used by
CreateUploadServiceManager to a non-zero delay, preserving the existing
three-retry behavior while adding backoff between upload retries.
- Around line 310-343: In recordBuildInfoArtifact, reuse the existing
artifactoryPath value for the artifact’s entities.Artifact.Path instead of
rebuilding the identical path with fmt.Sprintf. Keep artifactoryPath as the
single source for the dependency requestedBy path and recorded artifact path.
- Around line 439-475: The collectApkDependencies flow currently resolves each
dependency individually, causing one apk info subprocess per unresolved
provider. Update resolveDepIDWithProviders usage to collect unresolved
dependency IDs during the loop, then batch-resolve them through the existing
multi-package depsFromApkInfoCommand capability and apply the results back to
the corresponding dependencies, preserving deduplication and existing provider
resolution behavior.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a4f9d3cb-66fe-46ee-ba68-ec304b6447f1

📥 Commits

Reviewing files that changed from the base of the PR and between acf804d and 895ccaa.

📒 Files selected for processing (6)
  • artifactory/commands/alpine/apkcommand.go
  • artifactory/commands/alpine/apkcommand_test.go
  • artifactory/commands/alpine/apkupload.go
  • artifactory/commands/alpine/apkupload_test.go
  • artifactory/commands/setup/setup.go
  • artifactory/commands/setup/setup_test.go

Comment thread artifactory/commands/alpine/apkcommand.go
Comment thread artifactory/commands/setup/setup.go
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@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

♻️ Duplicate comments (1)
artifactory/commands/setup/setup.go (1)

683-705: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

The raw http.DefaultClient calls still bypass the configured Artifactory client.

apkValidateRepositoryExists, apkFetchKeyPairRef, and apkDownloadRSAKey use http.DefaultClient, which has no timeout and ignores configured proxy, TLS, retry, and auth settings. A stalled endpoint hangs jf setup apk indefinitely. This was reported before and marked as addressed, but the code still uses http.DefaultClient.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/setup/setup.go` around lines 683 - 705, The APK setup
HTTP helpers apkValidateRepositoryExists, apkFetchKeyPairRef, and
apkDownloadRSAKey must stop using http.DefaultClient. Route their requests
through the configured Artifactory client used by the surrounding setup flow so
configured timeout, proxy, TLS, retry, and authentication behavior is preserved.
🧹 Nitpick comments (1)
artifactory/commands/alpine/apkcommand.go (1)

569-583: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

The AQL query is not chunked.

All missing dependencies are placed in a single $or list. A large apk add with many uncached packages produces one very large query string. Artifactory can reject or slow down such requests. Split missing into fixed-size chunks, for example 100 items, and merge the results.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/alpine/apkcommand.go` around lines 569 - 583, Update
enrichDepsChecksumsFromAQL to process missing dependencies in fixed-size chunks
of about 100 items, building and executing a separate AQL query for each chunk.
Merge each chunk’s checksum results into the returned dependencies while
preserving the existing repository filtering and behavior for all missing
entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@artifactory/commands/alpine/apkcommand.go`:
- Around line 174-178: Update extractPackageNames and its callers in the apk
command flow so values belonging to native value-taking flags such as
--cache-dir, --repository, and -t are not classified as requested packages.
Ensure the filtered package list remains correct for excludeRequestedPackages,
alpineModule.SetRequestedPackages, and the completeness warning while preserving
genuine package arguments.

---

Duplicate comments:
In `@artifactory/commands/setup/setup.go`:
- Around line 683-705: The APK setup HTTP helpers apkValidateRepositoryExists,
apkFetchKeyPairRef, and apkDownloadRSAKey must stop using http.DefaultClient.
Route their requests through the configured Artifactory client used by the
surrounding setup flow so configured timeout, proxy, TLS, retry, and
authentication behavior is preserved.

---

Nitpick comments:
In `@artifactory/commands/alpine/apkcommand.go`:
- Around line 569-583: Update enrichDepsChecksumsFromAQL to process missing
dependencies in fixed-size chunks of about 100 items, building and executing a
separate AQL query for each chunk. Merge each chunk’s checksum results into the
returned dependencies while preserving the existing repository filtering and
behavior for all missing entries.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cbe31892-145e-43d2-8f02-09d5e9459d24

📥 Commits

Reviewing files that changed from the base of the PR and between 1a5e6a2 and 2d29dc7.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (8)
  • artifactory/commands/alpine/apkcommand.go
  • artifactory/commands/alpine/apkcommand_test.go
  • artifactory/commands/alpine/apkupload.go
  • artifactory/commands/alpine/apkupload_test.go
  • artifactory/commands/alpine/credentials.go
  • artifactory/commands/setup/setup.go
  • artifactory/commands/setup/setup_test.go
  • go.mod
🚧 Files skipped from review as they are similar to previous changes (5)
  • artifactory/commands/alpine/credentials.go
  • artifactory/commands/alpine/apkupload_test.go
  • artifactory/commands/setup/setup_test.go
  • artifactory/commands/alpine/apkcommand_test.go
  • artifactory/commands/alpine/apkupload.go

Comment thread artifactory/commands/alpine/apkcommand.go
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@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 (2)
artifactory/commands/alpine/apkcommand.go (2)

469-473: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reuse the resolved apkPath for the version probe.

Run resolves the binary with exec.LookPath("apk") at Line 162, then warnIfApkTooOld performs a second PATH lookup. Pass apkPath into warnIfApkTooOld so the version check inspects the same binary that will be executed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/alpine/apkcommand.go` around lines 469 - 473, Update
warnIfApkTooOld to accept the resolved apkPath and use it when invoking the
version command instead of looking up "apk" again. Pass the apkPath resolved by
Run into warnIfApkTooOld so both checks inspect the same binary.

296-303: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Apply the secret env filter on the early-return paths too.

filterSecretEnvVars runs only after the rtURL == "" early returns at Lines 292 and 298. When no server is configured, the apk subprocess receives the unfiltered environment. If the intent is "never pass secret-like variables to apk", move the filter above the early returns so all paths return the same filtered environment.

♻️ Proposed change
 func (apkCmd *ApkCommand) buildEnvWithHTTPAuth() ([]string, error) {
-	env := os.Environ()
+	// Filter out env vars matching the JFROG_CLI_ENV_EXCLUDE pattern to avoid secret leaks
+	// in the subprocess environment.
+	env := filterSecretEnvVars(os.Environ())
@@
 	if rtURL == "" {
 		log.Warn("No JFrog server configured — skipping HTTP_AUTH injection. Run: jf c add")
 		return env, nil
 	}
-
-	// Filter out env vars matching the JFROG_CLI_ENV_EXCLUDE pattern to avoid secret leaks
-	// in the subprocess environment.
-	env = filterSecretEnvVars(env)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/alpine/apkcommand.go` around lines 296 - 303, Move the
filterSecretEnvVars call above the rtURL == "" early-return branches in the APK
environment setup so every return path, including no configured server, returns
a sanitized environment. Preserve the existing warning and return behavior while
ensuring the apk subprocess never receives unfiltered secret-like variables.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@artifactory/commands/setup/setup.go`:
- Around line 1096-1102: Validate keyPairRef in apkWriteSigningKey before
constructing keyFilePath with filepath.Join, rejecting values containing '/',
'\', or '..'; alternatively use only the validated base name. Ensure invalid
references return an error and cannot cause apkWriteFile to target outside
apkKeysDir.

---

Nitpick comments:
In `@artifactory/commands/alpine/apkcommand.go`:
- Around line 469-473: Update warnIfApkTooOld to accept the resolved apkPath and
use it when invoking the version command instead of looking up "apk" again. Pass
the apkPath resolved by Run into warnIfApkTooOld so both checks inspect the same
binary.
- Around line 296-303: Move the filterSecretEnvVars call above the rtURL == ""
early-return branches in the APK environment setup so every return path,
including no configured server, returns a sanitized environment. Preserve the
existing warning and return behavior while ensuring the apk subprocess never
receives unfiltered secret-like variables.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 413395c4-20be-4b8b-825a-b6c9cc9c4fe4

📥 Commits

Reviewing files that changed from the base of the PR and between 1a5e6a2 and 512493a.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (8)
  • artifactory/commands/alpine/apkcommand.go
  • artifactory/commands/alpine/apkcommand_test.go
  • artifactory/commands/alpine/apkupload.go
  • artifactory/commands/alpine/apkupload_test.go
  • artifactory/commands/alpine/credentials.go
  • artifactory/commands/setup/setup.go
  • artifactory/commands/setup/setup_test.go
  • go.mod
🚧 Files skipped from review as they are similar to previous changes (4)
  • artifactory/commands/alpine/credentials.go
  • artifactory/commands/alpine/apkupload_test.go
  • artifactory/commands/alpine/apkupload.go
  • artifactory/commands/alpine/apkcommand_test.go

Comment on lines +1096 to +1102
if err = apkMkdirAll(apkKeysDir); err != nil {
return fmt.Errorf("failed to create %s: %w", apkKeysDir, err)
}
keyFilePath := filepath.Join(apkKeysDir, keyPairRef+".rsa.pub")
if err = apkWriteFile(keyFilePath, pemKey, 0644); err != nil {
return fmt.Errorf("failed to write RSA key to %s: %w", keyFilePath, err)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm keyPairRef flows unvalidated from the API response into the write path.
rg -n -C4 'primaryKeyPairRef|keyPairRef' artifactory/commands/setup/setup.go
rg -n -C4 'func apkWriteFile' artifactory/commands/setup/setup.go

Repository: jfrog/jfrog-cli-artifactory

Length of output: 2689


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Relevant setup.go sections =="
sed -n '1081,1160p' artifactory/commands/setup/setup.go
sed -n '1197,1218p' artifactory/commands/setup/setup.go

echo
echo "== Existing sanitization/helper references nearby =="
rg -n 'validateArtifactoryPathSegment|filepath\.Base|strings\.ContainsAny|apkKeysDir|apkMkdirAll|apkWriteFile' artifactory/commands/setup/setup.go

echo
echo "== Static verifier: path traversal invariant from API response to APK key directory =="
python3 - <<'PY'
import subprocess
p = subprocess.run(["go", "tool", "asm", "-c", "filepath.Join", "/tmp/x.go"], cwd="/tmp", stdout=subprocess.PIPE, stderr=subprocess.PIPE, env={**subprocess.os.environ})
print("go asm result:", p.returncode)
PY

echo
echo "== Run-only behavioral probe for Go filepath.Join in this tree =="
go env GOROOT 2>/dev/null | sed -n '1p' || true
tmpdir="$(mktemp -d)"
cat > "$tmpdir/main.go" <<'GO'
package main

import (
	"path/filepath"
	"fmt"
)

func main() {
	for _, keyPairRef := range []string{"../../../etc/cron.d/x", "../evil", "safe", "foo/bar"} {
		keyFilePath := filepath.Join("/etc/apk/keys", keyPairRef+".rsa.pub")
		fmt.Printf("%q -> %q\n", keyPairRef, keyFilePath)
	}
}
GO

go run "$tmpdir/main.go"
rm -rf "$tmpdir"

Repository: jfrog/jfrog-cli-artifactory

Length of output: 5670


Path Traversal (CWE-22): Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

Reachability: Internal

Reachability path
● Entry
  artifactory/commands/setup/setup_test.go:1281
  TestApkValidateRepositoryExists
│
▼
● Sink
  artifactory/commands/setup/setup.go

Validate keyPairRef before it becomes a filesystem path.

apkFetchKeyPairRef returns primaryKeyPairRef directly, and apkWriteSigningKey appends it to /etc/apk/keys with filepath.Join. Joined components are cleaned, so ../../../etc/cron.d/x resolves outside the intended directory; apkWriteFile then writes that path through sudo. Reject any keyPairRef containing /, \, or .., or insert only its base name.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/setup/setup.go` around lines 1096 - 1102, Validate
keyPairRef in apkWriteSigningKey before constructing keyFilePath with
filepath.Join, rejecting values containing '/', '\', or '..'; alternatively use
only the validated base name. Ensure invalid references return an error and
cannot cause apkWriteFile to target outside apkKeysDir.

Source: Linters/SAST tools

naveenku-jfrog and others added 2 commits August 5, 2026 11:45
Adds native Alpine Linux APK support to jfrog-cli-artifactory:

- artifactory/commands/alpine: the jf apk wrapper that runs the native apk
  binary against an Artifactory-backed repository, collecting Build Info via
  build-info-go AlpineModule (install-side dependency graph with checksums,
  scopes and requestedBy chains) and uploading downloaded archives.
- artifactory/commands/setup: jf setup apk configuration - writes the repo
  definition and signing key to the apk config, creating credential-bearing
  files owner-only (umask 077) and restoring prior content on failure.
- go.mod: bump build-info-go and jfrog-cli-core to the commits carrying the
  Alpine build-info APIs (SetDownloadsDir, apk module) and the apk ProjectType.

Co-authored-by: Cursor <cursoragent@cursor.com>
Run() called ensureRepoExists unconditionally whenever --repo was set,
even with no JFrog server configured at all. With no server there is
nothing to validate against, and apk still works via the system's
default repositories in that case (matching upload's existing guard).
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@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: 4

♻️ Duplicate comments (2)
artifactory/commands/setup/setup.go (2)

1096-1102: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Validate keyPairRef before it becomes a filesystem path.

apkFetchKeyPairRef returns primaryKeyPairRef from the API response without validation. filepath.Join cleans the joined path, so a value such as ../../../etc/cron.d/x resolves outside /etc/apk/keys, and apkWriteFile then writes that path through sudo. Reject any value that contains /, \, or .., or use only its base name.

🛡️ Proposed fix
+	if strings.ContainsAny(keyPairRef, `/\`) || strings.Contains(keyPairRef, "..") {
+		return fmt.Errorf("invalid primaryKeyPairRef %q returned for repo %q", keyPairRef, repoKey)
+	}
 	if err = apkMkdirAll(apkKeysDir); err != nil {
 		return fmt.Errorf("failed to create %s: %w", apkKeysDir, err)
 	}
 	keyFilePath := filepath.Join(apkKeysDir, keyPairRef+".rsa.pub")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/setup/setup.go` around lines 1096 - 1102, Validate
keyPairRef immediately after apkFetchKeyPairRef returns and before filepath.Join
in the setup flow, rejecting values containing "/", "\", or ".." (or normalizing
to a safe base name). Ensure invalid references return an error and only
validated values reach apkWriteFile for paths under apkKeysDir.

975-997: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

http.DefaultClient still bypasses the configured Artifactory client.

apkValidateRepositoryExists (Line 983), apkFetchKeyPairRef (Line 1129), and apkDownloadRSAKey (Line 1166) all use http.DefaultClient with a hand-built Authorization header. That client has no timeout, so a stalled Artifactory endpoint hangs jf setup apk indefinitely. It also ignores the configured proxy, TLS/insecure settings, retries, and any non-basic/non-bearer auth scheme. Route these calls through artutils.CreateServiceManager (for example IsRepoExists) or, at minimum, through a client with an explicit timeout and the configured transport.

This was raised on the previous commit and marked as addressed, but the code still calls http.DefaultClient.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/setup/setup.go` around lines 975 - 997, Replace the
direct http.DefaultClient usage in apkValidateRepositoryExists,
apkFetchKeyPairRef, and apkDownloadRSAKey with the configured Artifactory client
via artutils.CreateServiceManager, reusing its repository-existence and request
capabilities where applicable. Preserve the existing validation and error
behavior while ensuring configured authentication, proxy, TLS, retries, and
timeout settings are honored.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@artifactory/commands/alpine/apkcommand.go`:
- Around line 709-729: Update extractPackageNames to normalize each non-flag
package token by removing its version constraint, including =, >, and ~
specifiers, before appending it to pkgs. Preserve existing flag skipping and
empty-token handling so returned values contain only package names for
excludeRequestedPackages and SetRequestedPackages.
- Around line 303-320: Update the existing HTTP_AUTH-preservation branch in the
environment-building function to restore the pre-existing HTTP_AUTH value to the
filtered env before returning. Keep the current behavior of honoring the
original value when no explicit username/password flags are provided, while
retaining override behavior for explicit flags.

In `@artifactory/commands/setup/setup.go`:
- Around line 1272-1278: Update apkMergeRepositoriesContent to match existing
repository lines by scheme, host, and repository path prefix rather than
hostname alone, so distinct branches such as main and community from the same
Artifactory host are preserved. Keep replacement limited to the intended
repository entry while retaining insertion behavior for the configured main
branch.
- Around line 951-958: Update the Alpine repository URL setup around
detectAlpineVersion so that when it returns an empty string, the existing
fallback URL path is retained but a warning is logged explaining that the Alpine
release could not be detected and the repository URL may be invalid. Use the
surrounding setup logger and keep the detected-version URL behavior unchanged.

---

Duplicate comments:
In `@artifactory/commands/setup/setup.go`:
- Around line 1096-1102: Validate keyPairRef immediately after
apkFetchKeyPairRef returns and before filepath.Join in the setup flow, rejecting
values containing "/", "\", or ".." (or normalizing to a safe base name). Ensure
invalid references return an error and only validated values reach apkWriteFile
for paths under apkKeysDir.
- Around line 975-997: Replace the direct http.DefaultClient usage in
apkValidateRepositoryExists, apkFetchKeyPairRef, and apkDownloadRSAKey with the
configured Artifactory client via artutils.CreateServiceManager, reusing its
repository-existence and request capabilities where applicable. Preserve the
existing validation and error behavior while ensuring configured authentication,
proxy, TLS, retries, and timeout settings are honored.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 901f1b99-f3db-4f28-9071-06321cfceca1

📥 Commits

Reviewing files that changed from the base of the PR and between 1a5e6a2 and 6196485.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (8)
  • artifactory/commands/alpine/apkcommand.go
  • artifactory/commands/alpine/apkcommand_test.go
  • artifactory/commands/alpine/apkupload.go
  • artifactory/commands/alpine/apkupload_test.go
  • artifactory/commands/alpine/credentials.go
  • artifactory/commands/setup/setup.go
  • artifactory/commands/setup/setup_test.go
  • go.mod
🚧 Files skipped from review as they are similar to previous changes (6)
  • go.mod
  • artifactory/commands/alpine/credentials.go
  • artifactory/commands/alpine/apkupload_test.go
  • artifactory/commands/alpine/apkupload.go
  • artifactory/commands/setup/setup_test.go
  • artifactory/commands/alpine/apkcommand_test.go

Comment on lines +303 to +320
// Filter out env vars matching the JFROG_CLI_ENV_EXCLUDE pattern to avoid secret leaks
// in the subprocess environment.
env = filterSecretEnvVars(env)

// Explicit --user/--password flags override any pre-set HTTP_AUTH; a stored/default
// server config does not, so a user-provided HTTP_AUTH is otherwise honoured as-is.
userExplicitFlags := apkCmd.username != "" || apkCmd.password != ""

existingHTTPAuth := os.Getenv("HTTP_AUTH")
if existingHTTPAuth != "" {
if userExplicitFlags {
log.Warn("HTTP_AUTH is already set in your environment. Overriding it with the credentials from the provided flags/server config.")
} else {
// User did not pass explicit flags — honour their pre-set HTTP_AUTH.
log.Debug("HTTP_AUTH already set in environment and no explicit flags provided — keeping existing value.")
return env, nil
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

The "keep existing HTTP_AUTH" branch returns an environment with HTTP_AUTH removed.

filterSecretEnvVars at Line 305 drops every variable whose lowercased name matches *auth*, and http_auth matches that pattern. On the branch at Lines 316-318 the function returns the filtered env without re-adding the value. The user's pre-set HTTP_AUTH is therefore silently lost, and apk runs unauthenticated instead of honouring it.

🐛 Proposed fix
 	env = filterSecretEnvVars(env)
 
 	// Explicit --user/--password flags override any pre-set HTTP_AUTH; a stored/default
 	// server config does not, so a user-provided HTTP_AUTH is otherwise honoured as-is.
 	userExplicitFlags := apkCmd.username != "" || apkCmd.password != ""
 
 	existingHTTPAuth := os.Getenv("HTTP_AUTH")
 	if existingHTTPAuth != "" {
 		if userExplicitFlags {
 			log.Warn("HTTP_AUTH is already set in your environment. Overriding it with the credentials from the provided flags/server config.")
 		} else {
 			// User did not pass explicit flags — honour their pre-set HTTP_AUTH.
 			log.Debug("HTTP_AUTH already set in environment and no explicit flags provided — keeping existing value.")
-			return env, nil
+			// filterSecretEnvVars removed HTTP_AUTH (it matches *auth*), so re-add it.
+			return append(env, "HTTP_AUTH="+existingHTTPAuth), nil
 		}
 	}
📝 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
// Filter out env vars matching the JFROG_CLI_ENV_EXCLUDE pattern to avoid secret leaks
// in the subprocess environment.
env = filterSecretEnvVars(env)
// Explicit --user/--password flags override any pre-set HTTP_AUTH; a stored/default
// server config does not, so a user-provided HTTP_AUTH is otherwise honoured as-is.
userExplicitFlags := apkCmd.username != "" || apkCmd.password != ""
existingHTTPAuth := os.Getenv("HTTP_AUTH")
if existingHTTPAuth != "" {
if userExplicitFlags {
log.Warn("HTTP_AUTH is already set in your environment. Overriding it with the credentials from the provided flags/server config.")
} else {
// User did not pass explicit flags — honour their pre-set HTTP_AUTH.
log.Debug("HTTP_AUTH already set in environment and no explicit flags provided — keeping existing value.")
return env, nil
}
}
// Filter out env vars matching the JFROG_CLI_ENV_EXCLUDE pattern to avoid secret leaks
// in the subprocess environment.
env = filterSecretEnvVars(env)
// Explicit --user/--password flags override any pre-set HTTP_AUTH; a stored/default
// server config does not, so a user-provided HTTP_AUTH is otherwise honoured as-is.
userExplicitFlags := apkCmd.username != "" || apkCmd.password != ""
existingHTTPAuth := os.Getenv("HTTP_AUTH")
if existingHTTPAuth != "" {
if userExplicitFlags {
log.Warn("HTTP_AUTH is already set in your environment. Overriding it with the credentials from the provided flags/server config.")
} else {
// User did not pass explicit flags — honour their pre-set HTTP_AUTH.
log.Debug("HTTP_AUTH already set in environment and no explicit flags provided — keeping existing value.")
// filterSecretEnvVars removed HTTP_AUTH (it matches *auth*), so re-add it.
return append(env, "HTTP_AUTH="+existingHTTPAuth), nil
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/alpine/apkcommand.go` around lines 303 - 320, Update the
existing HTTP_AUTH-preservation branch in the environment-building function to
restore the pre-existing HTTP_AUTH value to the filtered env before returning.
Keep the current behavior of honoring the original value when no explicit
username/password flags are provided, while retaining override behavior for
explicit flags.

Comment on lines +709 to +729
func extractPackageNames(args []string) []string {
var pkgs []string
skipNext := false
for _, arg := range args {
if skipNext {
skipNext = false
continue
}
if strings.HasPrefix(arg, "-") {
// `--flag=value` carries its own value; `--flag value` consumes the next token.
if !strings.Contains(arg, "=") && apkValueFlags[arg] {
skipNext = true
}
continue
}
if arg != "" {
pkgs = append(pkgs, arg)
}
}
return pkgs
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Strip version constraints from requested package tokens.

apk add accepts constrained specifiers such as curl=8.5.0-r0, curl>1.0, and curl~3.2. extractPackageNames returns the whole token, so excludeRequestedPackages never matches pkg.Name, and SetRequestedPackages records a value that is not a package name. Normalize each token to the name part.

🩹 Proposed fix
 		if arg != "" {
-			pkgs = append(pkgs, arg)
+			pkgs = append(pkgs, trimPkgConstraint(arg))
 		}
 	}
 	return pkgs
 }
+
+// trimPkgConstraint reduces "curl=8.5.0-r0", "curl>1.0", or "curl~3.2" to "curl".
+func trimPkgConstraint(arg string) string {
+	if idx := strings.IndexAny(arg, "=<>~"); idx > 0 {
+		return arg[:idx]
+	}
+	return arg
+}
📝 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
func extractPackageNames(args []string) []string {
var pkgs []string
skipNext := false
for _, arg := range args {
if skipNext {
skipNext = false
continue
}
if strings.HasPrefix(arg, "-") {
// `--flag=value` carries its own value; `--flag value` consumes the next token.
if !strings.Contains(arg, "=") && apkValueFlags[arg] {
skipNext = true
}
continue
}
if arg != "" {
pkgs = append(pkgs, arg)
}
}
return pkgs
}
func extractPackageNames(args []string) []string {
var pkgs []string
skipNext := false
for _, arg := range args {
if skipNext {
skipNext = false
continue
}
if strings.HasPrefix(arg, "-") {
// `--flag=value` carries its own value; `--flag value` consumes the next token.
if !strings.Contains(arg, "=") && apkValueFlags[arg] {
skipNext = true
}
continue
}
if arg != "" {
pkgs = append(pkgs, trimPkgConstraint(arg))
}
}
return pkgs
}
// trimPkgConstraint reduces "curl=8.5.0-r0", "curl>1.0", or "curl~3.2" to "curl".
func trimPkgConstraint(arg string) string {
if idx := strings.IndexAny(arg, "=<>~"); idx > 0 {
return arg[:idx]
}
return arg
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/alpine/apkcommand.go` around lines 709 - 729, Update
extractPackageNames to normalize each non-flag package token by removing its
version constraint, including =, >, and ~ specifiers, before appending it to
pkgs. Preserve existing flag skipping and empty-token handling so returned
values contain only package names for excludeRequestedPackages and
SetRequestedPackages.

Comment on lines +951 to +958
alpineVersion := detectAlpineVersion()

var repoURL string
if alpineVersion != "" {
repoURL = fmt.Sprintf("%s/%s/%s/%s/", rtURL, sc.repoName, alpineVersion, apkDefaultBranch)
} else {
repoURL = fmt.Sprintf("%s/%s/", rtURL, sc.repoName)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Warn when the Alpine release cannot be detected.

If /etc/alpine-release is missing or unparseable, detectAlpineVersion returns "" and the code writes <rtURL>/<repo>/ with no version or branch segment. apk cannot fetch APKINDEX.tar.gz from that URL, so the setup reports success and the next apk command fails with an unclear error. Log a warning on this path.

🩹 Proposed fix
 	var repoURL string
 	if alpineVersion != "" {
 		repoURL = fmt.Sprintf("%s/%s/%s/%s/", rtURL, sc.repoName, alpineVersion, apkDefaultBranch)
 	} else {
+		log.Warn(fmt.Sprintf("Could not determine the Alpine release from %s. The repository URL is written without a "+
+			"version and branch segment, so apk may not find an index. Run this command on an Alpine host, or edit %s manually.",
+			alpineReleaseFile, apkRepositoriesFile))
 		repoURL = fmt.Sprintf("%s/%s/", rtURL, sc.repoName)
 	}
📝 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
alpineVersion := detectAlpineVersion()
var repoURL string
if alpineVersion != "" {
repoURL = fmt.Sprintf("%s/%s/%s/%s/", rtURL, sc.repoName, alpineVersion, apkDefaultBranch)
} else {
repoURL = fmt.Sprintf("%s/%s/", rtURL, sc.repoName)
}
alpineVersion := detectAlpineVersion()
var repoURL string
if alpineVersion != "" {
repoURL = fmt.Sprintf("%s/%s/%s/%s/", rtURL, sc.repoName, alpineVersion, apkDefaultBranch)
} else {
log.Warn(fmt.Sprintf("Could not determine the Alpine release from %s. The repository URL is written without a "+
"version and branch segment, so apk may not find an index. Run this command on an Alpine host, or edit %s manually.",
alpineReleaseFile, apkRepositoriesFile))
repoURL = fmt.Sprintf("%s/%s/", rtURL, sc.repoName)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/setup/setup.go` around lines 951 - 958, Update the
Alpine repository URL setup around detectAlpineVersion so that when it returns
an empty string, the existing fallback URL path is retained but a warning is
logged explaining that the Alpine release could not be detected and the
repository URL may be invalid. Use the surrounding setup logger and keep the
detected-version URL behavior unchanged.

Comment on lines +1272 to +1278
if artHost != "" && apkRepoHostname(trimmed) == artHost {
if !inserted {
out = append(out, repoURL)
inserted = true
}
continue
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Host-level matching removes every other repository line from the same Artifactory host.

apkMergeRepositoriesContent compares only the hostname. A user who has both .../v3.20/main/ and .../v3.20/community/ from the same Artifactory host keeps only the new line, so apk loses access to the community branch. configureApk always writes the main branch, so there is no way to keep a second branch after setup.

Consider matching on scheme+host+path prefix instead of hostname alone, or logging the lines that are replaced so the change is visible to the user.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@artifactory/commands/setup/setup.go` around lines 1272 - 1278, Update
apkMergeRepositoriesContent to match existing repository lines by scheme, host,
and repository path prefix rather than hostname alone, so distinct branches such
as main and community from the same Artifactory host are preserved. Keep
replacement limited to the intended repository entry while retaining insertion
behavior for the configured main branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new feature Automatically generated release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants