Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion syncmedia.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ const (

// mediaExtensions is the set of file extensions treated as "media" (i.e.
// non-code) that will be synced to blob storage. Keep in lowercase.
//
// This is an allowlist: files whose extensions are not listed here are never
// uploaded. See codeExtensions for an explicit denylist of source-code
// extensions that must never leak into storage even if this allowlist grows.
var mediaExtensions = map[string]bool{
".png": true,
".jpg": true,
Expand All @@ -71,6 +75,27 @@ var mediaExtensions = map[string]bool{
".zip": true,
}

// codeExtensions is an explicit denylist of source-code file extensions that
// must NEVER be uploaded to blob storage. Code lives in git; only rendered
// media belongs in the container. This check runs before the mediaExtensions
// allowlist so accidental additions to that allowlist can't slip code through.
// Keep in lowercase.
var codeExtensions = map[string]bool{
".md": true,
".css": true,
".html": true,
".go": true,
".js": true,
".ts": true,
".json": true,
".yaml": true,
".yml": true,
".toml": true,
".mod": true,
".sum": true,
".sh": true,
}

// syncMediaPrefixes are the repo-relative directory roots we sync from.
var syncMediaPrefixes = []string{
"content/articles",
Expand Down Expand Up @@ -258,7 +283,15 @@ func watchAndSync(
}

func isMediaPath(p string) bool {
return mediaExtensions[strings.ToLower(filepath.Ext(p))]
ext := strings.ToLower(filepath.Ext(p))
if codeExtensions[ext] {
return false
}
return mediaExtensions[ext]
}

func isCodePath(p string) bool {
return codeExtensions[strings.ToLower(filepath.Ext(p))]
}

// isLikelyDirEvent returns true if the event has no file extension (usually a
Expand Down Expand Up @@ -293,6 +326,9 @@ func collectLocalMedia(log *logrus.Logger) (map[string]*localMediaFile, error) {
return nil
}
ext := strings.ToLower(filepath.Ext(p))
if codeExtensions[ext] {
return nil
}
if !mediaExtensions[ext] {
return nil
}
Expand Down
42 changes: 42 additions & 0 deletions syncmedia_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestIsMediaPathAndIsCodePath(t *testing.T) {
mediaCases := []string{
"content/articles/foo/bar.png",
"content/images/logo.JPG",
"content/pages/about/pic.jpeg",
"content/articles/x/movie.mp4",
"content/articles/x/doc.pdf",
}
for _, p := range mediaCases {
require.Truef(t, isMediaPath(p), "expected media: %s", p)
require.Falsef(t, isCodePath(p), "expected not code: %s", p)
}

codeCases := []string{
"content/articles/foo/foo.md",
"content/stylesheets/tailwind.css",
"web/html/_nav.tmpl.html",
"build.go",
"package.json",
"go.mod",
}
for _, p := range codeCases {
require.Truef(t, isCodePath(p), "expected code: %s", p)
require.Falsef(t, isMediaPath(p), "expected not media: %s", p)
}

// Belt-and-suspenders: even if a code extension were mistakenly added to
// mediaExtensions, isMediaPath must still return false because
// codeExtensions is consulted first.
mediaExtensions[".md"] = true
t.Cleanup(func() { delete(mediaExtensions, ".md") })
require.False(t, isMediaPath("content/articles/foo/foo.md"),
"code denylist must win over media allowlist")
}
Loading