fix(P99): P0 heredoc command injection + ROADMAP/REQUIREMENTS reconciliation
P0 fix (final review T1): internal/sshpush/idempotency.go heredoc command injection via fixed EOF delimiter. Replaced with per-write random delimiter verified absent from content (strings.Contains check). Fake SSH server updated to parse the delimiter dynamically from the command. This prevents command injection via crafted file content in multi-tenant namespaces. ROADMAP reconciliation (final review T2.1): updated v0.9 phase list to reflect actual execution — 14 tagged phases (P03/P04/P08 combined, P07a/b/c combined), tags v0.8.1..v0.8.14. Milestone marked COMPLETE. Phase checkboxes marked [x] with actual REQs covered. REQUIREMENTS reconciliation: 21 v0.9-scoped REQs marked Complete (062,063,064,067,068,069,070,071,072,073,074,076,077,078,081,082, 083,085,088,089,090). 9 v0.10-deferred REQs (061,065,066,075,079, 080,084,086,087) Phase columns fixed to reference only v0.10 (not v0.9/v0.8) so verify-reqs doesn't flag them as belonging to completed milestones. Final review: P0 fixed. P1 warnings logged for post-hoc v0.10: fuzz in CI, podman command quoting, scheduler O(n^2), ProcessRuntime stdout leak, host-key verification path gap. 12/19 grill gates cleared; 7 deferred to v0.10 (C-08,C-09,C-11,C-12,C-13,C-19). 26 packages pass, 20 bats pass, gofmt clean, verify-reqs 90 consistent. ---ci--- project: orca phase: 99 milestone: v0.9 status: execute ---/ci---
This commit is contained in:
@@ -48,10 +48,12 @@ func (t *Transport) writeFile(ctx context.Context, peer string, path string, con
|
||||
}
|
||||
// Build the remote command: mkdir -p <dir> && cat > <tmp> <<'EOF'
|
||||
// ... EOF && chmod <mode> <tmp> && mv <tmp> <path>. The heredoc
|
||||
// delimiter is chosen to not appear in the content (we use a fixed
|
||||
// marker; content with the marker would break, but the marker is
|
||||
// sufficiently unusual).
|
||||
const eof = "ORCA_PUSH_EOF_a1b2c3"
|
||||
// delimiter is per-write random and verified absent from content
|
||||
// to prevent command injection via crafted file content.
|
||||
eof := "ORCA_PUSH_EOF_" + randomToken(16)
|
||||
for strings.Contains(string(content), eof) {
|
||||
eof = "ORCA_PUSH_EOF_" + randomToken(16)
|
||||
}
|
||||
modeStr := fmt.Sprintf("%04o", uint32(mode.Perm()))
|
||||
cmd := fmt.Sprintf(
|
||||
"mkdir -p %s && cat > %s <<'%s'\n%s\n%s\nchmod %s %s && mv -f %s %s",
|
||||
|
||||
@@ -180,19 +180,25 @@ func (s *fakeSSHServer) runCommand(cmd string) ([]byte, int) {
|
||||
// handleWrite parses the heredoc write command produced by writeFile.
|
||||
// Command format:
|
||||
//
|
||||
// mkdir -p '<dir>' && cat > '<tmp>' <<'ORCA_PUSH_EOF_a1b2c3'
|
||||
// mkdir -p '<dir>' && cat > '<tmp>' <<'ORCA_PUSH_EOF_<random>'
|
||||
// <content>
|
||||
// ORCA_PUSH_EOF_a1b2c3
|
||||
// ORCA_PUSH_EOF_<random>
|
||||
// chmod <mode> '<tmp>' && mv -f '<tmp>' '<path>'
|
||||
func (s *fakeSSHServer) handleWrite(cmd string) ([]byte, int) {
|
||||
const eof = "ORCA_PUSH_EOF_a1b2c3"
|
||||
// Find the opening heredoc line: ... <<'EOF'\n
|
||||
openerIdx := strings.Index(cmd, "<<'"+eof+"'")
|
||||
// Find the opening heredoc line: ... <<'ORCA_PUSH_EOF_<random>'\n
|
||||
// The delimiter is per-write random (P0 fix); extract it from the command.
|
||||
openerIdx := strings.Index(cmd, "<<'")
|
||||
if openerIdx < 0 {
|
||||
return []byte("sh: no heredoc opener\n"), 1
|
||||
}
|
||||
eofStart := openerIdx + len("<<'")
|
||||
eofEnd := strings.Index(cmd[eofStart:], "'")
|
||||
if eofEnd < 0 {
|
||||
return []byte("sh: no heredoc closer quote\n"), 1
|
||||
}
|
||||
eof := cmd[eofStart : eofStart+eofEnd]
|
||||
// Body starts after the opener line's newline.
|
||||
rest := cmd[openerIdx+len("<<'"+eof+"'"):]
|
||||
rest := cmd[eofStart+eofEnd+1:]
|
||||
nl := strings.Index(rest, "\n")
|
||||
if nl < 0 {
|
||||
return []byte("sh: no body start\n"), 1
|
||||
|
||||
Reference in New Issue
Block a user