Skip to content

fix(alluxio): build the metadata query command as an argv vector - #6146

Merged
RongGu merged 1 commit into
fluid-cloudnative:masterfrom
cheyang:fix/alluxio-metadata-query-cmdguard
Aug 4, 2026
Merged

fix(alluxio): build the metadata query command as an argv vector#6146
RongGu merged 1 commit into
fluid-cloudnative:masterfrom
cheyang:fix/alluxio-metadata-query-cmdguard

Conversation

@cheyang

@cheyang cheyang commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

AlluxioFileUtils.QueryMetaDataInfoIntoFile assembled its command as a shell script:

str     = "sed -n '" + line + "' " + filename
command = []string{"bash", "-c", str}

Every container exec passes through cmdguard.ValidateCommandSlice (pkg/utils/kubeclient/exec.go:125). A bash -c command takes the shell-script validation path, whose illegalSequences list contains ', so the command was rejected before reaching the master pod:

unsafe shell script sed -n '3p' /host/metadata.yaml, illegal sequence detected: '

The single quotes belong to the sed script itself, so this failed on every call, not just on unusual input. RestoreMetadataInternal could never read the backup file, so a Dataset with DataRestoreLocation set never had Status.UfsTotal or Status.FileNum restored.

The change

Build the command as an argv vector:

return []string{"sed", "-n", line, filename}, nil

exec() hands the slice straight to ExecCommandInContainerWithTimeoutContext, so no shell is involved. The command now takes cmdguard's plain-argv path, the quotes are gone, and the file name stays in its own element where it reaches sed verbatim.

Also included:

  • Command construction extracted into metaDataQueryCommand, so the resulting argv is directly assertable in a unit test.
  • An unrecognized key now returns an error. Previously it logged one and then ran sed -n '' <file>, which would have returned the whole file as the value.
  • New cmdguard.ValidateArg, for validating a single argument such as a path, called from ParseBackupRestorePath. The character lists stay in cmdguard next to the existing ones rather than being restated by each caller: it reuses illegalChars, adds the glob / brace / home-expansion / comment / quoting characters that carry no meaning in a path, and rejects control characters. Validating at the point where the path is parsed also covers the consumers that never reach ValidateCommandSlicetransform.go renders this path into Helm values for a command that runs inside the pod.
  • checkCommandArgs is deliberately left unchanged, so the behaviour of every existing exec, helm and CSI call site is identical to before.
  • The JuiceFS copy of QueryMetaDataInfoIntoFile has no production caller, but its argv was malformed: sed -n "'3p' /file" makes sed treat the whole argument as its script. Corrected the same way to keep the two engines consistent.

Testing

New unit tests are pure functions over the command builder, the new validator and the path parser — no gomonkey patching, so they are deterministic:

  • TestMetaDataQueryCommand (alluxio + juicefs): exact argv per key, unknown key errors, and a file name containing shell-significant characters stays in a single argv element with no shell wrapper.
  • TestValidateArg (cmdguard): ordinary paths accepted; one case per rejected character class, including the control characters.
  • TestParseBackupRestorePathRejectsShellMetacharacters: rejection through the parser plus a set of ordinary paths that must keep working.
ok  github.com/fluid-cloudnative/fluid/pkg/utils/cmdguard
ok  github.com/fluid-cloudnative/fluid/pkg/utils
ok  github.com/fluid-cloudnative/fluid/pkg/ddc/alluxio/operations
ok  github.com/fluid-cloudnative/fluid/pkg/ddc/juicefs/operations

go build ./... and go vet are clean.

One note for reviewers: pkg/ddc/alluxio/operations has a number of pre-existing test failures on darwin/arm64 caused by gomonkey patch re-application, unrelated to this change. I compared the set of failing test names before and after the change and it is unchanged, so there is no regression from this PR — but CI on Linux is the authoritative check.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.13%. Comparing base (45af080) to head (921abbd).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6146      +/-   ##
==========================================
+ Coverage   65.10%   65.13%   +0.02%     
==========================================
  Files         485      485              
  Lines       34015    34039      +24     
==========================================
+ Hits        22147    22171      +24     
  Misses      10127    10127              
  Partials     1741     1741              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cheyang
cheyang force-pushed the fix/alluxio-metadata-query-cmdguard branch from c8ee6d9 to 1bb0fb9 Compare August 3, 2026 04:46
@cheyang
cheyang requested a review from Copilot August 3, 2026 06:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes metadata-restore command execution for Alluxio (and aligns JuiceFS) by eliminating shell-wrapped sed invocations and instead constructing sed commands as plain argv vectors, allowing them to pass cmdguard validation and execute reliably in engine pods.

Changes:

  • Refactor metadata query command building to return []string{"sed","-n",<line>,<file>} (no bash -c) and add unit tests asserting the argv shape.
  • Tighten behavior for unsupported metadata keys to return an error instead of attempting a fallback sed invocation.
  • Add cmdguard.ValidateArg and apply it in ParseBackupRestorePath to reject shell-metacharacter/control-character input early, with accompanying tests.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pkg/utils/databackup.go Validates backup/restore path strings early via cmdguard.ValidateArg before parsing.
pkg/utils/databackup_test.go Adds tests ensuring shell metacharacters/control chars are rejected and ordinary paths still parse.
pkg/utils/cmdguard/exec.go Introduces ValidateArg with a stricter character/control-character policy for single arguments.
pkg/utils/cmdguard/arg_test.go Adds unit coverage for accepted/rejected cases in ValidateArg.
pkg/ddc/alluxio/operations/base.go Builds metadata query sed command as argv vector via metaDataQueryCommand; errors on unknown keys.
pkg/ddc/alluxio/operations/base_test.go Updates existing tests and adds coverage for metaDataQueryCommand argv construction.
pkg/ddc/juicefs/operations/base.go Mirrors Alluxio change: correct sed argv and error on unknown keys for consistency.
pkg/ddc/juicefs/operations/base_test.go Updates existing tests and adds coverage for metaDataQueryCommand argv construction.
Suppressed comments (1)

pkg/utils/cmdguard/exec.go:143

  • The control-character error currently reports an "index" from a range loop, which is a byte index in Go strings. Calling this out (and quoting the arg) avoids confusion when debugging.
		if r < 0x20 || r == 0x7f {
			return fmt.Errorf("arg %s has illegal control character %q at index %d", arg, r, i)
		}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/utils/databackup.go Outdated
Comment on lines +76 to +78
// The parsed path is used to build commands run inside engine pods and is rendered into
// Helm values, so reject anything a shell would act on here rather than relying on every
// consumer to quote it correctly.
Comment on lines +135 to +137
if strings.ContainsRune(arg, illegalChar) {
return fmt.Errorf("arg %s has illegal access with illegalChar %c", arg, illegalChar)
}
Comment thread pkg/ddc/alluxio/operations/base_test.go Outdated

// A file name carrying characters that are significant to a shell must stay in a single
// argv element, and the command must never be wrapped in a shell.
pathWithSpecialChars := "/pvc/tmp/a b'c;d/metadata.yaml"
Comment thread pkg/ddc/juicefs/operations/base_test.go Outdated

// A file name carrying characters that are significant to a shell must stay in a single
// argv element.
pathWithSpecialChars := "/pvc/tmp/a b'c;d/metadata.yaml"
QueryMetaDataInfoIntoFile assembled its command as a shell script:

    str     = "sed -n '" + line + "' " + filename
    command = []string{"bash", "-c", str}

Every container exec passes through cmdguard.ValidateCommandSlice
(pkg/utils/kubeclient/exec.go:125). A `bash -c` command takes the shell-script
validation path, whose illegalSequences list contains `'`, so the command was
rejected before reaching the master pod:

    unsafe shell script sed -n '3p' /host/metadata.yaml,
    illegal sequence detected: '

The single quotes belong to the sed script itself, so this failed on every
call. RestoreMetadataInternal could not read the backup, and a dataset with
DataRestoreLocation set never had Status.UfsTotal or Status.FileNum restored.

Build the command as an argv vector instead. exec() passes the slice straight
to ExecCommandInContainerWithTimeoutContext, so no shell is involved: the
command now takes cmdguard's plain-argv path, the quotes are gone, and the
file name stays in its own element where it is handed to sed verbatim. A path
containing spaces now works too, where joining it into a shell string would
have word-split it into several sed operands.

The construction moves into metaDataQueryCommand so the resulting argv can be
asserted in a unit test. An unrecognized key now returns an error instead of
logging one and then running sed with an empty script, which would have
returned the whole file as the value.

Add cmdguard.ValidateArg for a single argument such as a path, and call it from
ParseBackupRestorePath. The characters it rejects are kept in cmdguard next to
the existing lists instead of being restated by each caller: argIllegalChars
extends illegalChars with the glob, brace, home-expansion, comment and quoting
characters that carry no meaning in a path, and control characters are rejected
as well. Whitespace stays allowed, since spaces occur in real paths and are
harmless once the value is its own argv element. Validating where the path is
parsed also covers the consumers that never reach ValidateCommandSlice, such as
transform.go rendering the path into Helm values for a command that runs inside
the pod. checkCommandArgs is left untouched, so the behaviour of every existing
exec, helm and CSI call site is unchanged.

The JuiceFS copy of QueryMetaDataInfoIntoFile has no production caller, but its
argv was malformed - `sed -n "'3p' /file"` makes sed treat the whole argument
as its script. It is corrected the same way to keep the two engines consistent.

Signed-off-by: cheyang <cheyang.cy@alibaba-inc.com>
@cheyang
cheyang force-pushed the fix/alluxio-metadata-query-cmdguard branch from 1bb0fb9 to 921abbd Compare August 3, 2026 10:08
@cheyang

cheyang commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for the review — all four inline comments were valid and are addressed in the force-pushed update.

1. databackup.go — comment did not match behaviour. Correct: the comment claimed everything a shell acts on is rejected, but spaces are allowed. I fixed the comment rather than the behaviour: a path containing spaces is a legitimate value, and it is harmless once the command is an argv vector. The same note is now on ValidateArg itself so the intent is visible at the definition.

2. cmdguard/exec.go — unreadable error message. Fixed, and this turned out to matter more than it looks:

before: args /pvc/tmp/a b'c;d/x.yaml has illegal access with illegalChar ;
after:  arg "/pvc/tmp/a b'c;d/x.yaml" contains illegal character ';'
after:  arg "/tmp/a\nb" contains illegal control character '\n'

Without %q a rejected control character was printed literally and broke the log line. While fixing this I stopped routing ValidateArg through checkCommandArgs, because that helper returns the old message format and the two would have been inconsistent. ValidateArg now iterates argIllegalChars, which is derived from illegalChars, so the character list still has a single source and checkCommandArgs keeps its existing behaviour and message untouched.

3 & 4. Test path was not reachable. Correct, and I verified it:

"/pvc/tmp/a b'c;d/metadata.yaml"        -> rejected: contains illegal character ';'
"/pvc/tmp/dir with space/metadata.yaml" -> accepted

Both tests now use the spaces-only path. This makes the assertion stronger rather than weaker: a space is the shell-significant character a restore path can actually carry, and it is exactly what the previous bash -c form got wrong — joining it into a shell string word-splits it into two sed operands.

Also addressed the Codecov gap. The uncovered lines were the unknown-key branch of QueryMetaDataInfoIntoFile. That branch returns before exec, so it can be tested without stubbing anything; added Test*_QueryMetaDataInfoIntoFileUnknownKey for both engines. Coverage of that function goes from 63.6% to 81.8% locally, and metaDataQueryCommand was already at 100%.

On the SonarCloud duplication (32.2% on new code): that is the two near-identical metaDataQueryCommand helpers in the alluxio and juicefs packages. I deliberately did not factor them out. KeyOfMetaDataFile is declared independently in each package, so sharing the helper means introducing a cross-engine type for the sake of one copy that has no production caller. Happy to do it if maintainers prefer.

@sonarqubecloud

sonarqubecloud Bot commented Aug 3, 2026

Copy link
Copy Markdown

@RongGu RongGu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

/lgtm
/approve

@RongGu
RongGu merged commit 7c00ed2 into fluid-cloudnative:master Aug 4, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants