package main
import (
"bytes"
"crypto/sha256"
"encoding/base64"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"regexp"
)
var hashFile = flag .String ("hf" , "internal/middleware/secureheaders.go" , "file with hashes for CSP header" )
func main () {
flag .Usage = func () {
fmt .Fprintf (flag .CommandLine .Output (), "usage: %s [flags] FILES\n" , os .Args [0 ])
fmt .Fprintf (flag .CommandLine .Output (), "suggestion for FILES: content/static/html/**/*.tmpl\n" )
flag .PrintDefaults ()
}
flag .Parse ()
if flag .NArg () == 0 {
flag .Usage ()
os .Exit (1 )
}
cspHashes , err := extractHashes (*hashFile )
if err != nil {
log .Fatal (err )
}
cspHashMap := map [string ]bool {}
for _ , h := range cspHashes {
cspHashMap [h ] = true
}
ok := true
for _ , file := range flag .Args () {
scripts , err := scripts (file )
if err != nil {
log .Fatal (err )
}
for _ , s := range scripts {
if bytes .Contains (s .tag , []byte ("src=" )) {
fmt .Printf ("%s: has script with src attribute: %s\n" , file , s .tag )
ok = false
}
if bytes .Contains (s .body , []byte ("{{" )) {
fmt .Printf ("%s: has script with template expansion:\n%s\n" , file , s .body )
fmt .Printf ("Scripts must be static so they have a constant hash.\n" )
ok = false
continue
}
hash := cspHash (s .body )
if !cspHashMap [hash ] {
fmt .Printf ("missing hash: add the lines below to %s:\n" , *hashFile )
fmt .Printf (" // From %s\n" , file )
fmt .Printf (` "'sha256-%s'",` , hash )
fmt .Println ()
ok = false
} else {
delete (cspHashMap , hash )
}
}
}
for h := range cspHashMap {
fmt .Printf ("unused hash %s\n" , h )
ok = false
}
if !ok {
fmt .Printf ("Add missing hashes to %s and remove unused ones.\n" , *hashFile )
os .Exit (1 )
}
}
var hashRegexp = regexp .MustCompile (`'sha256-([^']+)'` )