From 9fc2930cfddc72c1045dc462a24c1b3fa2e28922 Mon Sep 17 00:00:00 2001 From: scanash00 Date: Wed, 31 Dec 2025 14:12:28 -0900 Subject: [PATCH] fix git parsing in frontend and add validation to custom build commands --- backend-go/deploy/build.go | 46 ++++++++++++++++++++++++++++ client/src/pages/DashboardLayout.jsx | 5 ++- client/src/pages/DashboardSite.jsx | 6 ++-- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/backend-go/deploy/build.go b/backend-go/deploy/build.go index 8b83056..45c78bd 100644 --- a/backend-go/deploy/build.go +++ b/backend-go/deploy/build.go @@ -6,12 +6,55 @@ package deploy import ( "context" + "errors" "fmt" "os" "os/exec" "path/filepath" + "strings" ) +func validateBuildCommand(cmd string) error { + cmd = strings.TrimSpace(cmd) + if cmd == "" { + return nil + } + + dangerousMap := []string{ + "&", "|", ";", ">", "<", "`", "$(", + } + for _, char := range dangerousMap { + if strings.Contains(cmd, char) { + return fmt.Errorf("command contains forbidden character: %s", char) + } + } + + allowedPrefixes := []string{ + "npm ", "yarn ", "pnpm ", "bun ", "npx ", "node ", + } + isAllowed := false + for _, p := range allowedPrefixes { + if strings.HasPrefix(cmd, p) { + isAllowed = true + break + } + } + if !isAllowed { + return errors.New("command must start with npm, yarn, pnpm, bun, npx, or node") + } + + forbiddenKeywords := []string{ + " start", " dev", " serve", " preview", " watch", + } + for _, kw := range forbiddenKeywords { + if strings.Contains(cmd, kw) { + return fmt.Errorf("command looks like a runtime server (contains '%s'), only build commands are allowed", strings.TrimSpace(kw)) + } + } + + return nil +} + func fileExists(path string) bool { _, err := os.Stat(path) return err == nil @@ -130,6 +173,9 @@ func (b *BuildSystem) Build(ctx context.Context, customCommand string) (string, } if customCommand != "" { + if err := validateBuildCommand(customCommand); err != nil { + return "", fmt.Errorf("invalid build command: %w", err) + } if b.Logger != nil { b.Logger(fmt.Sprintf("Running custom build command: %s\n", customCommand)) } diff --git a/client/src/pages/DashboardLayout.jsx b/client/src/pages/DashboardLayout.jsx index a30fdf4..477a743 100644 --- a/client/src/pages/DashboardLayout.jsx +++ b/client/src/pages/DashboardLayout.jsx @@ -152,7 +152,10 @@ function Sidebar({ sites, selectedId, collapsed, onToggle, user, onLogout, mobil
{s.name} - {s.domain || s.git?.url?.replace('https://github.com/', '') || ''} + {s.domain || + s.gitUrl?.replace('https://github.com/', '') || + s.git?.url?.replace('https://github.com/', '') || + ''}
diff --git a/client/src/pages/DashboardSite.jsx b/client/src/pages/DashboardSite.jsx index eefd44a..778dfad 100644 --- a/client/src/pages/DashboardSite.jsx +++ b/client/src/pages/DashboardSite.jsx @@ -438,9 +438,9 @@ export default function DashboardSite() { setEnvDraft(site.envText || ''); setSettingsDraft({ name: site.name || '', - gitUrl: site.git?.url || '', - branch: site.git?.branch || 'main', - subdir: site.git?.subdir || '', + gitUrl: site.gitUrl || site.git?.url || '', + branch: site.gitBranch || site.git?.branch || 'main', + subdir: site.gitSubdir || site.git?.subdir || '', domain: edgeOnly ? toEdgeLabel(site.domain || '') : site.domain || '', buildCommand: site.buildCommand || '', outputDir: site.outputDir || '' -- 2.51.2