Add "Task" build and development process with Taskfile integration
- Added Taskfile.yml to streamline build, development, and testing tasks. - Updated README to reflect new build instructions and development environment setup using `go-task`. - Included `.taskfile.env` and `.task` in .dockerignore and .gitignore for better environment management. - Modified asset loading in verifier.ts to include integrity and cross-origin attributes for security. - Updated SRI generation script to handle both directory and single file inputs for improved flexibility.
This commit is contained in:
@@ -12,23 +12,34 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
buildDir := "frontend/build"
|
||||
// Default behavior: process HTML in frontend/build
|
||||
target := "frontend/build"
|
||||
if len(os.Args) > 1 {
|
||||
buildDir = os.Args[1]
|
||||
target = os.Args[1]
|
||||
}
|
||||
|
||||
fmt.Printf("Generating SRI hashes for assets in %s...\n", buildDir)
|
||||
info, err := os.Stat(target)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err := filepath.Walk(buildDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() && strings.HasSuffix(path, ".html") {
|
||||
return processHTML(path, buildDir)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if info.IsDir() {
|
||||
fmt.Printf("Generating SRI hashes for assets in %s...\n", target)
|
||||
err = filepath.Walk(target, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() && strings.HasSuffix(path, ".html") {
|
||||
return processHTML(path, target)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
} else {
|
||||
// If a single file is provided (like verifier.ts), process it specially
|
||||
fmt.Printf("Updating SRI hashes in %s...\n", target)
|
||||
err = processSourceFile(target)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
@@ -37,16 +48,12 @@ func main() {
|
||||
}
|
||||
|
||||
func processHTML(htmlPath, buildDir string) error {
|
||||
content, err := os.ReadFile(filepath.Clean(htmlPath)) // #nosec G304
|
||||
content, err := os.ReadFile(filepath.Clean(htmlPath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updated := string(content)
|
||||
|
||||
// Regex to find script and link tags that don't have integrity yet
|
||||
// Matches: <script src="/_app/..."
|
||||
// Matches: <link rel="stylesheet" href="/_app/..."
|
||||
scriptRegex := regexp.MustCompile(`<(script|link)\s+([^>]*)(src|href)=["'](/[^"']+)["']([^>]*)>`)
|
||||
|
||||
matches := scriptRegex.FindAllStringSubmatch(updated, -1)
|
||||
@@ -55,12 +62,10 @@ func processHTML(htmlPath, buildDir string) error {
|
||||
attr := match[3]
|
||||
url := match[4]
|
||||
|
||||
// Only process local assets starting with /
|
||||
if !strings.HasPrefix(url, "/") || strings.HasPrefix(url, "//") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip if integrity already exists
|
||||
if strings.Contains(tag, "integrity=") {
|
||||
continue
|
||||
}
|
||||
@@ -68,7 +73,6 @@ func processHTML(htmlPath, buildDir string) error {
|
||||
filePath := filepath.Join(buildDir, url)
|
||||
hash, err := calculateSHA384(filePath)
|
||||
if err != nil {
|
||||
// Asset might not exist (e.g. dynamic URL or external-ish local path)
|
||||
fmt.Printf(" Skipping %s: %v\n", url, err)
|
||||
continue
|
||||
}
|
||||
@@ -80,14 +84,58 @@ func processHTML(htmlPath, buildDir string) error {
|
||||
}
|
||||
|
||||
if updated != string(content) {
|
||||
return os.WriteFile(filepath.Clean(htmlPath), []byte(updated), 0644) // #nosec G306
|
||||
return os.WriteFile(filepath.Clean(htmlPath), []byte(updated), 0644)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func processSourceFile(sourcePath string) error {
|
||||
content, err := os.ReadFile(filepath.Clean(sourcePath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updated := string(content)
|
||||
|
||||
// We need a way to map the asset to the SRI. For verifier.ts, we know the assets.
|
||||
assets := map[string]string{
|
||||
"wasm_exec.js": "frontend/static/verifier/wasm_exec.js",
|
||||
"verifier.wasm": "frontend/static/verifier/verifier.wasm",
|
||||
}
|
||||
|
||||
for assetName, assetPath := range assets {
|
||||
hash, err := calculateSHA384(assetPath)
|
||||
if err != nil {
|
||||
fmt.Printf(" Warning: could not calculate hash for %s: %v\n", assetName, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Find the line that mentions the asset and update the NEXT integrity string
|
||||
assetEscaped := strings.ReplaceAll(assetName, ".", "\\.")
|
||||
assetPattern := regexp.MustCompile(`['"](/verifier/)?` + assetEscaped + `['"][\s\S]*?sha384-([^'"]+)`)
|
||||
|
||||
matches := assetPattern.FindAllStringSubmatchIndex(updated, -1)
|
||||
// Process from end to start to not mess up indices
|
||||
for i := len(matches) - 1; i >= 0; i-- {
|
||||
match := matches[i]
|
||||
oldHashStart, oldHashEnd := match[4], match[5]
|
||||
oldHash := updated[oldHashStart:oldHashEnd]
|
||||
|
||||
if oldHash != hash {
|
||||
updated = updated[:oldHashStart] + hash + updated[oldHashEnd:]
|
||||
fmt.Printf(" Updated SRI for %s in %s\n", assetName, sourcePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if updated != string(content) {
|
||||
return os.WriteFile(filepath.Clean(sourcePath), []byte(updated), 0644)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func calculateSHA384(path string) (string, error) {
|
||||
f, err := os.Open(filepath.Clean(path)) // #nosec G304
|
||||
f, err := os.Open(filepath.Clean(path))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user