fix(profile): validate remotely downloaded profiles to prevent command injection (CWE-78)#236
Merged
Merged
Conversation
…d injection Profile (.env) values fetched via 'harbor profile set <url>' were only checked for the presence of '=' or '#' before being installed as the active config. Several Harbor code paths later expand those values through shell evaluation (e.g. the cli.path / *.cache lookups), so a malicious remote profile containing $(...) or backticks in a value could achieve arbitrary command execution on the next harbor invocation that consumed that key. Add validate_profile_content() which rejects downloaded profiles whose values contain command substitution or whose keys are not plain identifiers, and warn the user that a remote profile is being installed.
av
approved these changes
May 9, 2026
Contributor
Author
|
Thanks for reviewing. @lewiswigmore |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Profiles downloaded by
harbor profile set <url>are installed as the active.envafter only minimal validation (presence of=or#). Several Harbor code paths later expand profile values through shell evaluation (e.g.eval echo "$(env_manager get cli.path)"and similar lookups inharbor.sharound lines 247, 1003, 1088, and 2659–2662). A profile value containing$(...)or backticks is therefore interpreted as a shell command on the next Harbor invocation that consumes the key — yielding arbitrary command execution in the user's shell context.harbor.sh→download_profile()(no value-content validation prior to install)download_profile→ written toprofiles/<name>.env→ activated →env_manager get <key>→ re-parsed byeval/command substitution at consumer site → shell executes attacker payloadReproducer (conceptual)
A remote profile containing:
is accepted by the current
download_profile(the line has=, no#), saved as the active config, and executed the next time a Harbor command expands that key througheval.Fix
Add
validate_profile_content()and call it fromdownload_profile()after a successful download. The validator:export.KEY=VALUEshape and a plain-identifier key (^[A-Za-z_][A-Za-z0-9_]*$).$(or a backtick — the two constructs that cause shell command execution when the value is later interpolated throughevalor unquoted command substitution.If validation fails, the downloaded file is removed and the install is aborted with a clear error. On success, two warnings are printed so the user is reminded that a remote profile was just installed and how to inspect it.
Locally authored profiles (
harbor profile add) are unaffected — only the remote-download path is gated.Diff is +67 / -0 in
harbor.sh, no other files touched.Testing
FOO=$(id), backtick-wrappedid, andFOO="prefix $(id) suffix"are now rejected and the temporary file is removed.1FOO=bar,FOO BAR=baz) are rejected.export KEY=valuecontinue to parse.default.env-style profile passes validation unchanged.bash -n harbor.shparses cleanly; existingharbor profile setsemantics (filename derivation, success message, return codes) are preserved when content is safe.Security analysis
The vulnerability is exploitable end-to-end: the
eval/command-substitution sinks that consumeenv_manager getoutput already exist inharbor.sh, the download path stores attacker-controlled bytes verbatim, and there is no host/scheme allowlist on the URL. The only precondition is that the user runsharbor profile set <attacker_url>— a documented, user-facing operation that users reasonably treat as importing configuration, not code. The fix neutralises the dominant payload shape ($(...)and backticks) before content ever lands on disk, and the added warning helps users notice unexpected remote installs.Known limits worth flagging for reviewers:
${VAR}parameter expansion is intentionally still allowed; it can read process env but cannot execute commands.$'\x24('could in theory survive the substring check and re-introduce$(after a laterevalre-parse. This is a narrower, value-shape-dependent edge case; a follow-up could additionally reject$'in values, but that risks rejecting legitimate strings. We chose the conservative fix that addresses the realistic attack and leaves room for a stricter pass later.Adversarial review
Before submitting we tried to disprove this. We checked whether the precondition (running
harbor profile set <url>) already implies code execution by the attacker — it doesn't: the documented behaviour of that command is "download a config file", not "run a script", and Harbor advertises profiles as switchable env files. We also checked whether any existing sanitiser, sandbox, or quoting in the consumer sites would neutralise$(...)— they don't, because the values are passed througheval/unquoted command substitution by design. So the gap is real and the patch closes the realistic exploitation path.cc @lewiswigmore