fix(chromium): remove local files from extra ling tags & extra script tags (closes #418)

This commit is contained in:
Julien Neuhart
2022-02-08 19:45:10 +01:00
parent b7a8b95820
commit 84875aaf82
6 changed files with 6 additions and 342 deletions

View File

@@ -130,10 +130,8 @@ func convertURLRoute(chromium API, engine gotenberg.PDFEngine) api.Route {
form, options := FormDataChromiumPDFOptions(ctx)
var (
URL string
PDFformat string
linkPaths []string
scriptPaths []string
URL string
PDFformat string
)
err := form.
@@ -163,176 +161,12 @@ func convertURLRoute(chromium API, engine gotenberg.PDFEngine) api.Route {
return nil
}).
Paths([]string{".woff2", ".woff", ".ttf", ".css"}, &linkPaths).
Paths([]string{".js"}, &scriptPaths).
Validate()
if err != nil {
return fmt.Errorf("validate form data: %w", err)
}
// Thanks to Options.LinkTags and Options.ScriptTags, one may
// "hijack" the content of a remote HTML document (for instance, a
// website) by loading external assets like scripts or CSS
// stylesheets.
//
// There are two possibilities:
//
// 1. We auto-detect all files sent in the request that match the
// following file extensions: ".woff2", ".woff", ".ttf", ".css",
// ".css", and ".js".
//
// 2. The user has sent both files and a JSON mapping via the
// "extraLinkTags" and/or "extraScriptTags" form fields. In such a
// scenario, the JSON mapping has the priority for ordering, and
// files which are a not mapped are added at the end. The user may
// also have sent remote URLs in the JSON mapping.
// First, let's handle the HTML <link> elements.
hasExtraLinkTags := len(options.ExtraLinkTags) > 0
hasLinkPaths := len(linkPaths) > 0
if !hasExtraLinkTags && hasLinkPaths {
// First scenario: there is no JSON mapping, we simply add the
// paths.
options.ExtraLinkTags = make([]LinkTag, len(linkPaths))
for i, path := range linkPaths {
options.ExtraLinkTags[i] = LinkTag{
Href: filepath.Base(path),
}
}
} else if hasExtraLinkTags && hasLinkPaths {
// Second scenario: there are both files and a JSON mapping.
// First, find the filenames of the files.
filenames := make([]string, len(linkPaths))
for i, path := range linkPaths {
filenames[i] = filepath.Base(path)
}
var extraLinkTags []LinkTag
// Then, let's find the filenames that exist in the JSON
// mapping, plus the entries that do only exist in the JSON
// mapping.
for _, linkTagFromMapping := range options.ExtraLinkTags {
found := false
for _, filename := range filenames {
if linkTagFromMapping.Href == filename {
extraLinkTags = append(extraLinkTags, linkTagFromMapping)
found = true
break
}
}
if !found {
// This entry only exist in the JSON mapping.
extraLinkTags = append(extraLinkTags, linkTagFromMapping)
}
}
// Then, add the remaining filenames.
for _, filename := range filenames {
found := false
for _, linkTag := range extraLinkTags {
if linkTag.Href == filename {
found = true
break
}
}
if !found {
extraLinkTags = append(extraLinkTags, LinkTag{
Href: filename,
})
}
}
// Last but not least, update the options.
options.ExtraLinkTags = extraLinkTags
}
// Next, let's handle the HTML <script> elements.
hasExtraScriptTags := len(options.ExtraScriptTags) > 0
hasScriptPaths := len(scriptPaths) > 0
if !hasExtraScriptTags && hasScriptPaths {
// First scenario: there is no JSON mapping, we simply add the
// paths.
options.ExtraScriptTags = make([]ScriptTag, len(scriptPaths))
for i, path := range scriptPaths {
options.ExtraScriptTags[i] = ScriptTag{
Src: filepath.Base(path),
}
}
} else if hasExtraScriptTags && hasScriptPaths {
// Second scenario: there are both files and a JSON mapping.
// First, find the filenames of the files.
filenames := make([]string, len(scriptPaths))
for i, path := range scriptPaths {
filenames[i] = filepath.Base(path)
}
var extraScriptTags []ScriptTag
// Then, let's find the filenames that exist in the JSON
// mapping, plus the entries that do only exist in the JSON
// mapping.
for _, scriptTagFromMapping := range options.ExtraScriptTags {
found := false
for _, filename := range filenames {
if scriptTagFromMapping.Src == filename {
extraScriptTags = append(extraScriptTags, scriptTagFromMapping)
found = true
break
}
}
if !found {
// This entry only exist in the JSON mapping.
extraScriptTags = append(extraScriptTags, scriptTagFromMapping)
}
}
// Then, add the remaining filenames.
for _, filename := range filenames {
found := false
for _, scriptTag := range extraScriptTags {
if scriptTag.Src == filename {
found = true
break
}
}
if !found {
extraScriptTags = append(extraScriptTags, ScriptTag{
Src: filename,
})
}
}
// Last but not least, update the options.
options.ExtraScriptTags = extraScriptTags
}
// For both <link> and <script> HTML elements, other scenarios are:
//
// 1. No files and no JSON mapping.
// 2. No files but JSON mapping.
//
// Nothing to do in such cases.
err = convertURL(ctx, chromium, engine, URL, PDFformat, options)
if err != nil {
return fmt.Errorf("convert URL to PDF: %w", err)

View File

@@ -3,7 +3,6 @@ package chromium
import (
"context"
"errors"
"fmt"
"net/http"
"os"
"reflect"
@@ -208,90 +207,6 @@ func TestConvertURLHandler(t *testing.T) {
expectHTTPErr: true,
expectHTTPStatus: http.StatusBadRequest,
},
{
ctx: func() *api.MockContext {
ctx := &api.MockContext{Context: &api.Context{}}
ctx.SetValues(map[string][]string{
"url": {
"foo",
},
})
ctx.SetFiles(map[string]string{
"b.css": "/b.css",
"a.woff": "/a.woff",
})
return ctx
}(),
api: func() API {
chromiumAPI := struct{ ProtoAPI }{}
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, options Options) error {
expectOptions := DefaultOptions()
expectOptions.ExtraLinkTags = []LinkTag{
{
Href: "a.woff",
},
{
Href: "b.css",
},
}
if !reflect.DeepEqual(options, expectOptions) {
return fmt.Errorf("expected options %+v, but got: %+v", expectOptions, options)
}
return nil
}
return chromiumAPI
}(),
expectOutputPathsCount: 1,
},
{
ctx: func() *api.MockContext {
ctx := &api.MockContext{Context: &api.Context{}}
ctx.SetValues(map[string][]string{
"url": {
"foo",
},
"extraLinkTags": {
`[{"href":"https://cdn.foo"},{"href":"b.css"}]`,
},
})
ctx.SetFiles(map[string]string{
"b.css": "/b.css",
"a.woff": "/a.woff",
})
return ctx
}(),
api: func() API {
chromiumAPI := struct{ ProtoAPI }{}
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, options Options) error {
expectOptions := DefaultOptions()
expectOptions.ExtraLinkTags = []LinkTag{
{
Href: "https://cdn.foo",
},
{
Href: "b.css",
},
{
Href: "a.woff",
},
}
if !reflect.DeepEqual(options, expectOptions) {
return fmt.Errorf("expected options %+v, but got: %+v", expectOptions, options)
}
return nil
}
return chromiumAPI
}(),
expectOutputPathsCount: 1,
},
{
ctx: func() *api.MockContext {
ctx := &api.MockContext{Context: &api.Context{}}
@@ -325,76 +240,19 @@ func TestConvertURLHandler(t *testing.T) {
"url": {
"foo",
},
})
ctx.SetFiles(map[string]string{
"b.js": "/b.js",
"a.js": "/a.js",
})
return ctx
}(),
api: func() API {
chromiumAPI := struct{ ProtoAPI }{}
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, options Options) error {
expectOptions := DefaultOptions()
expectOptions.ExtraScriptTags = []ScriptTag{
{
Src: "a.js",
},
{
Src: "b.js",
},
}
if !reflect.DeepEqual(options, expectOptions) {
return fmt.Errorf("expected options %+v, but got: %+v", expectOptions, options)
}
return nil
}
return chromiumAPI
}(),
expectOutputPathsCount: 1,
},
{
ctx: func() *api.MockContext {
ctx := &api.MockContext{Context: &api.Context{}}
ctx.SetValues(map[string][]string{
"url": {
"foo",
"extraLinkTags": {
`[{"href":"https://cdn.foo/foo.css"},{"href":"https://cdn.bar/bar.css"}]`,
},
"extraScriptTags": {
`[{"src":"https://cdn.foo"},{"src":"b.js"}]`,
`[{"src":"https://cdn.foo/foo.js"},{"src":"https://cdn.bar/bar.js"}]`,
},
})
ctx.SetFiles(map[string]string{
"b.js": "/b.js",
"a.js": "/a.js",
})
return ctx
}(),
api: func() API {
chromiumAPI := struct{ ProtoAPI }{}
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, options Options) error {
expectOptions := DefaultOptions()
expectOptions.ExtraScriptTags = []ScriptTag{
{
Src: "https://cdn.foo",
},
{
Src: "b.js",
},
{
Src: "a.js",
},
}
if !reflect.DeepEqual(options, expectOptions) {
return fmt.Errorf("expected options %+v, but got: %+v", expectOptions, options)
}
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, _ Options) error {
return nil
}