diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f23dde8031e..f92b1499248 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,18 +6,18 @@ name: Test env: GOPROXY: https://proxy.golang.org GO111MODULE: on - DART_SASS_VERSION: 1.56.2 - DART_SASS_SHA_LINUX: 9e4f455f7b8619959d7878af2862383be58392eb963a14ff87cc512c03701e2a - DART_SASS_SHA_MACOS: 5992e979e2c30ec363f8e338822bb2b4443c74232b3340501a76180f5652cb09 - DART_SASS_SHA_WINDOWS: 8d3d9117c54840e3e6a4919e43acf75ea52f28a64fc87a8e29d80ec72ee36cfb + SASS_VERSION: 1.63.2 + DART_SASS_SHA_LINUX: 3ea33c95ad5c35fda6e9a0956199eef38a398f496cfb8750e02479d7d1dd42af + DART_SASS_SHA_MACOS: 11c70f259836b250b44a9cb57fed70e030f21f45069b467d371685855f1eb4f0 + DART_SASS_SHA_WINDOWS: cd8cd36a619dd8e27f93d3186c52d70eb7d69472aa6c85f5094b29693e773f64 permissions: contents: read jobs: test: strategy: matrix: - go-version: [1.19.x, 1.20.x] - os: [ubuntu-latest, macos-latest, windows-latest] + go-version: [1.20.x] # 1.19.x, + os: [ubuntu-latest] # , macos-latest, windows-latest runs-on: ${{ matrix.os }} steps: - name: Checkout code @@ -66,26 +66,26 @@ jobs: - if: matrix.os == 'ubuntu-latest' name: Install dart-sass-embedded Linux run: | - echo "Install Dart Sass version ${DART_SASS_VERSION} ..." - curl -LJO "https://github.com/sass/dart-sass-embedded/releases/download/${DART_SASS_VERSION}/sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz"; - echo "${DART_SASS_SHA_LINUX} sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz" | sha256sum -c; - tar -xvf "sass_embedded-${DART_SASS_VERSION}-linux-x64.tar.gz"; - echo "$GITHUB_WORKSPACE/sass_embedded/" >> $GITHUB_PATH + echo "Install Dart Sass version ${SASS_VERSION} ..." + curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-linux-x64.tar.gz"; + echo "${DART_SASS_SHA_LINUX} dart-sass-${SASS_VERSION}-linux-x64.tar.gz" | sha256sum -c; + tar -xvf "dart-sass-${SASS_VERSION}-linux-x64.tar.gz"; + echo "DART_SASS_BINARY=$GITHUB_WORKSPACE/dart-sass/sass" >> $GITHUB_ENV - if: matrix.os == 'macos-latest' name: Install dart-sass-embedded MacOS run: | - echo "Install Dart Sass version ${DART_SASS_VERSION} ..." - curl -LJO "https://github.com/sass/dart-sass-embedded/releases/download/${DART_SASS_VERSION}/sass_embedded-${DART_SASS_VERSION}-macos-x64.tar.gz"; - echo "${DART_SASS_SHA_MACOS} sass_embedded-${DART_SASS_VERSION}-macos-x64.tar.gz" | shasum -a 256 -c; - tar -xvf "sass_embedded-${DART_SASS_VERSION}-macos-x64.tar.gz"; - echo "$GITHUB_WORKSPACE/sass_embedded/" >> $GITHUB_PATH + echo "Install Dart Sass version ${SASS_VERSION} ..." + curl -LJO "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-macos-x64.tar.gz"; + echo "${DART_SASS_SHA_MACOS} dart-sass-${SASS_VERSION}-macos-x64.tar.gz" | shasum -a 256 -c; + tar -xvf "dart-sass-${SASS_VERSION}-macos-x64.tar.gz"; + echo "DART_SASS_BINARY=$GITHUB_WORKSPACE/dart-sass/sass" >> $GITHUB_ENV - if: matrix.os == 'windows-latest' name: Install dart-sass-embedded Windows run: | - echo "Install Dart Sass version ${env:DART_SASS_VERSION} ..." - curl -LJO "https://github.com/sass/dart-sass-embedded/releases/download/${env:DART_SASS_VERSION}/sass_embedded-${env:DART_SASS_VERSION}-windows-x64.zip"; - Expand-Archive -Path "sass_embedded-${env:DART_SASS_VERSION}-windows-x64.zip" -DestinationPath .; - echo "$env:GITHUB_WORKSPACE/sass_embedded/" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf-8 -Append + echo "Install Dart Sass version ${env:SASS_VERSION} ..." + curl -LJO "https://github.com/sass/dart-sass/releases/download/${env:SASS_VERSION}/dart-sass-${env:SASS_VERSION}-windows-x64.zip"; + Expand-Archive -Path "dart-sass-${env:SASS_VERSION}-windows-x64.zip" -DestinationPath .; + echo "DART_SASS_BINARY=$env:GITHUB_WORKSPACE/dart-sass/sass.bat" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append - if: matrix.os != 'windows-latest' name: Check run: | diff --git a/common/herrors/file_error.go b/common/herrors/file_error.go index 30417897f6a..ea7e4834566 100644 --- a/common/herrors/file_error.go +++ b/common/herrors/file_error.go @@ -19,7 +19,7 @@ import ( "io" "path/filepath" - "github.com/bep/godartsass" + "github.com/bep/godartsass/v2" "github.com/bep/golibsass/libsass/libsasserrors" "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/common/text" diff --git a/common/hugo/hugo.go b/common/hugo/hugo.go index 4769852a096..c446da3d188 100644 --- a/common/hugo/hugo.go +++ b/common/hugo/hugo.go @@ -24,7 +24,7 @@ import ( "sync" "time" - "github.com/bep/godartsass" + "github.com/bep/godartsass/v2" "github.com/gohugoio/hugo/common/hexec" "github.com/gohugoio/hugo/hugofs/files" @@ -283,11 +283,39 @@ type Dependency struct { } func dartSassVersion() godartsass.DartSassVersion { - // This is also duplicated in the dartsass package. - const dartSassEmbeddedBinaryName = "dart-sass-embedded" - if !hexec.InPath(dartSassEmbeddedBinaryName) { + if DartSassBinaryName == "" { return godartsass.DartSassVersion{} } - v, _ := godartsass.Version(dartSassEmbeddedBinaryName) + v, _ := godartsass.Version(DartSassBinaryName) return v } + +// DartSassBinaryName is the name of the Dart Sass binary to use. +// TODO(beop) find a better place for this. +var DartSassBinaryName string + +func init() { + DartSassBinaryName = os.Getenv("DART_SASS_BINARY") + if DartSassBinaryName == "" { + for _, name := range dartSassBinaryNamesV2 { + if hexec.InPath(name) { + DartSassBinaryName = name + break + } + } + if DartSassBinaryName == "" { + if hexec.InPath(dartSassBinaryNameV1) { + DartSassBinaryName = dartSassBinaryNameV1 + } + } + } +} + +var ( + dartSassBinaryNameV1 = "dart-sass-embedded" + dartSassBinaryNamesV2 = []string{"dart-sass", "sass"} +) + +func IsDartSassV2() bool { + return !strings.Contains(DartSassBinaryName, "embedded") +} diff --git a/config/security/securityConfig.go b/config/security/securityConfig.go index f7d2beac8bf..bd3c5702a93 100644 --- a/config/security/securityConfig.go +++ b/config/security/securityConfig.go @@ -35,9 +35,9 @@ const securityConfigKey = "security" var DefaultConfig = Config{ Exec: Exec{ Allow: NewWhitelist( - "^dart-sass-embedded$", - "^go$", // for Go Modules - "^npx$", // used by all Node tools (Babel, PostCSS). + "(dart-)?sass(-embedded)?$", // sass, dart-sass, dart-sass-embedded. Note that this can be provided as a full path in DART_SASS_BINARY. + "^go$", // for Go Modules + "^npx$", // used by all Node tools (Babel, PostCSS). "^postcss$", ), // These have been tested to work with Hugo's external programs diff --git a/config/security/securityConfig_test.go b/config/security/securityConfig_test.go index edc1737e34a..b09512121b5 100644 --- a/config/security/securityConfig_test.go +++ b/config/security/securityConfig_test.go @@ -140,7 +140,7 @@ func TestToTOML(t *testing.T) { got := DefaultConfig.ToTOML() c.Assert(got, qt.Equals, - "[security]\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^dart-sass-embedded$', '^go$', '^npx$', '^postcss$']\n osEnv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\\w+)$']\n\n [security.funcs]\n getenv = ['^HUGO_', '^CI$']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['.*']", + "[security]\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['(dart-)?sass(-embedded)?$', '^go$', '^npx$', '^postcss$']\n osEnv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\\w+)$']\n\n [security.funcs]\n getenv = ['^HUGO_', '^CI$']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['.*']", ) } diff --git a/go.mod b/go.mod index 0144c0959f4..dcc7589a717 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/bep/debounce v1.2.0 github.com/bep/gitmap v1.1.2 github.com/bep/goat v0.5.0 - github.com/bep/godartsass v1.1.0 + github.com/bep/godartsass/v2 v2.0.0 github.com/bep/golibsass v1.1.1 github.com/bep/gowebp v0.2.0 github.com/bep/helpers v0.4.0 diff --git a/go.sum b/go.sum index 2a672cc1915..4b4328d260a 100644 --- a/go.sum +++ b/go.sum @@ -168,12 +168,8 @@ github.com/bep/gitmap v1.1.2 h1:zk04w1qc1COTZPPYWDQHvns3y1afOsdRfraFQ3qI840= github.com/bep/gitmap v1.1.2/go.mod h1:g9VRETxFUXNWzMiuxOwcudo6DfZkW9jOsOW0Ft4kYaY= github.com/bep/goat v0.5.0 h1:S8jLXHCVy/EHIoCY+btKkmcxcXFd34a0Q63/0D4TKeA= github.com/bep/goat v0.5.0/go.mod h1:Md9x7gRxiWKs85yHlVTvHQw9rg86Bm+Y4SuYE8CTH7c= -github.com/bep/godartsass v0.16.0 h1:nTpenrZBQjVSjLkCw3AgnYmBB2czauTJa4BLLv448qg= -github.com/bep/godartsass v0.16.0/go.mod h1:6LvK9RftsXMxGfsA0LDV12AGc4Jylnu6NgHL+Q5/pE8= -github.com/bep/godartsass v1.0.0 h1:vL5TTtPkpEAZowsXydfJ3M1BatR9fH513FP3but9TEM= -github.com/bep/godartsass v1.0.0/go.mod h1:6LvK9RftsXMxGfsA0LDV12AGc4Jylnu6NgHL+Q5/pE8= -github.com/bep/godartsass v1.1.0 h1:MYNXVQMFoohxue9sCbHi+bWp4AeykvH40gQj1fd9q1c= -github.com/bep/godartsass v1.1.0/go.mod h1:6LvK9RftsXMxGfsA0LDV12AGc4Jylnu6NgHL+Q5/pE8= +github.com/bep/godartsass/v2 v2.0.0 h1:Ruht+BpBWkpmW+yAM2dkp7RSSeN0VLaTobyW0CiSP3Y= +github.com/bep/godartsass/v2 v2.0.0/go.mod h1:AcP8QgC+OwOXEq6im0WgDRYK7scDsmZCEW62o1prQLo= github.com/bep/golibsass v1.1.1 h1:xkaet75ygImMYjM+FnHIT3xJn7H0xBA9UxSOJjk8Khw= github.com/bep/golibsass v1.1.1/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA= github.com/bep/gowebp v0.2.0 h1:ZVfK8i9PpZqKHEmthQSt3qCnnHycbLzBPEsVtk2ch2Q= @@ -202,7 +198,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/mxj/v2 v2.5.7 h1:7q5lvUpaPF/WOkqgIDiwjBJaznaLCCBd78pi8ZyAnE0= github.com/clbanning/mxj/v2 v2.5.7/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= -github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00= github.com/cli/safeexec v1.0.1/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -252,7 +247,6 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu github.com/frankban/quicktest v1.4.1/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ= github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= -github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= @@ -347,7 +341,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-replayers/grpcreplay v1.1.0 h1:S5+I3zYyZ+GQz68OfbURDdt/+cSMqCK1wrvNx7WBzTE= @@ -435,7 +428,6 @@ github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -502,7 +494,6 @@ github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.1-0.20230508101108-a4f6fabd84c5 h1:Tb1D114RozKzV2dDfarvSZn8lVYvjcGSCDaMQ+b4I+E= github.com/rogpeppe/go-internal v1.10.1-0.20230508101108-a4f6fabd84c5/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= diff --git a/hugolib/securitypolicies_test.go b/hugolib/securitypolicies_test.go index 5b9267b591d..74a4a94d1ed 100644 --- a/hugolib/securitypolicies_test.go +++ b/hugolib/securitypolicies_test.go @@ -42,7 +42,6 @@ func TestSecurityPolicies(t *testing.T) { } else { b.Build(BuildCfg{}) } - } httpTestVariant := func(c *qt.C, templ, expectErr string, withBuilder func(b *sitesBuilder)) { @@ -145,7 +144,7 @@ allow="none" `) b.WithTemplatesAdded("index.html", `{{ $scss := "body { color: #333; }" | resources.FromString "foo.scss" | resources.ToCSS (dict "transpiler" "dartsass") }}`) } - testVariant(c, cb, `(?s).*"dart-sass-embedded" is not whitelisted in policy "security\.exec\.allow".*`) + testVariant(c, cb, `(?s).*sass" is not whitelisted in policy "security\.exec\.allow".*`) }) c.Run("resources.GetRemote, OK", func(c *qt.C) { diff --git a/resources/resource_transformers/tocss/dartsass/client.go b/resources/resource_transformers/tocss/dartsass/client.go index f358b93e5f2..b797cc5c76e 100644 --- a/resources/resource_transformers/tocss/dartsass/client.go +++ b/resources/resource_transformers/tocss/dartsass/client.go @@ -21,6 +21,7 @@ import ( "strings" "github.com/gohugoio/hugo/common/herrors" + "github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/hugolib/filesystems" @@ -28,7 +29,8 @@ import ( "github.com/gohugoio/hugo/resources/resource" "github.com/spf13/afero" - "github.com/bep/godartsass" + "github.com/bep/godartsass/v2" + "github.com/mitchellh/mapstructure" ) @@ -44,11 +46,16 @@ func New(fs *filesystems.SourceFilesystem, rs *resources.Spec) (*Client, error) return &Client{dartSassNotAvailable: true}, nil } - if err := rs.ExecHelper.Sec().CheckAllowedExec(dartSassEmbeddedBinaryName); err != nil { + if hugo.DartSassBinaryName == "" { + return nil, fmt.Errorf("no Dart Sass binary found in $PATH") + } + + if err := rs.ExecHelper.Sec().CheckAllowedExec(hugo.DartSassBinaryName); err != nil { return nil, err } transpiler, err := godartsass.Start(godartsass.Options{ + DartSassEmbeddedFilename: hugo.DartSassBinaryName, LogEventHandler: func(event godartsass.LogEvent) { message := strings.ReplaceAll(event.Message, dartSassStdinPrefix, "") switch event.Type { @@ -99,7 +106,7 @@ func (c *Client) toCSS(args godartsass.Args, src io.Reader) (godartsass.Result, res, err := c.transpiler.Execute(args) if err != nil { if err.Error() == "unexpected EOF" { - return res, fmt.Errorf("got unexpected EOF when executing %q. The user running hugo must have read and execute permissions on this program. With execute permissions only, this error is thrown.", dartSassEmbeddedBinaryName) + return res, fmt.Errorf("got unexpected EOF when executing %q. The user running hugo must have read and execute permissions on this program. With execute permissions only, this error is thrown.", hugo.DartSassBinaryName) } return res, herrors.NewFileErrorFromFileInErr(err, hugofs.Os, herrors.OffsetMatcher) } diff --git a/resources/resource_transformers/tocss/dartsass/transform.go b/resources/resource_transformers/tocss/dartsass/transform.go index 95dfd594492..acd69cedec7 100644 --- a/resources/resource_transformers/tocss/dartsass/transform.go +++ b/resources/resource_transformers/tocss/dartsass/transform.go @@ -20,7 +20,7 @@ import ( "path/filepath" "strings" - "github.com/gohugoio/hugo/common/hexec" + "github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/htesting" "github.com/gohugoio/hugo/media" @@ -34,11 +34,7 @@ import ( "github.com/gohugoio/hugo/hugofs" - "github.com/bep/godartsass" -) - -const ( - dartSassEmbeddedBinaryName = "dart-sass-embedded" + "github.com/bep/godartsass/v2" ) // Supports returns whether dart-sass-embedded is found in $PATH. @@ -46,7 +42,7 @@ func Supports() bool { if htesting.SupportsAll() { return true } - return hexec.InPath(dartSassEmbeddedBinaryName) + return hugo.DartSassBinaryName != "" } type transform struct {