diff --git a/server/cmd/api/api/chromium.go b/server/cmd/api/api/chromium.go index b65b01a8..905b8721 100644 --- a/server/cmd/api/api/chromium.go +++ b/server/cmd/api/api/chromium.go @@ -154,6 +154,15 @@ func (s *ApiService) UploadExtensionsAndRestart(ctx context.Context, request oap log.Error("failed to unzip zip file", "error", err) return oapi.UploadExtensionsAndRestart400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "invalid zip file"}}, nil } + + // Rewrite update.xml URLs to match the extension name (directory name) + // This ensures URLs like /extensions/web-bot-auth/ become /extensions// + updateXMLPath := filepath.Join(dest, "update.xml") + if err := policy.RewriteUpdateXMLUrls(updateXMLPath, p.name); err != nil { + log.Warn("failed to rewrite update.xml URLs", "error", err, "extension", p.name) + // continue since not all extensions require update.xml + } + if err := exec.Command("chown", "-R", "kernel:kernel", dest).Run(); err != nil { log.Error("failed to chown extension dir", "error", err) return oapi.UploadExtensionsAndRestart500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to chown extension dir"}}, nil diff --git a/server/lib/policy/policy.go b/server/lib/policy/policy.go index aaa3d623..16f43a62 100644 --- a/server/lib/policy/policy.go +++ b/server/lib/policy/policy.go @@ -19,6 +19,7 @@ const NewTabPageLocation = "https://start.duckduckgo.com/" // Chrome extension IDs are 32 lowercase a-p characters var extensionIDRegex = regexp.MustCompile(`^[a-p]{32}$`) +var extensionPathRegex = regexp.MustCompile(`/extensions/[^/]+/`) // Policy represents the Chrome enterprise policy structure type Policy struct { @@ -263,3 +264,29 @@ func ExtractExtensionIDFromUpdateXML(updateXMLPath string) (string, error) { return appID, nil } + +// RewriteUpdateXMLUrls rewrites the codebase URLs in update.xml to use the specified extension name. +// This ensures that regardless of what name was originally in the update.xml, the URLs will match +// the actual directory name where the extension is installed. +func RewriteUpdateXMLUrls(updateXMLPath, extensionName string) error { + data, err := os.ReadFile(updateXMLPath) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return fmt.Errorf("failed to read update.xml: %w", err) + } + + content := string(data) + newPath := fmt.Sprintf("/extensions/%s/", extensionName) + + newContent := extensionPathRegex.ReplaceAllString(content, newPath) + + if newContent != content { + if err := os.WriteFile(updateXMLPath, []byte(newContent), 0644); err != nil { + return fmt.Errorf("failed to write update.xml: %w", err) + } + } + + return nil +}