mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-17 20:52:14 +01:00
fix(chromium): remove local files from extra ling tags & extra script tags (closes #418)
This commit is contained in:
@@ -132,8 +132,6 @@ func convertURLRoute(chromium API, engine gotenberg.PDFEngine) api.Route {
|
|||||||
var (
|
var (
|
||||||
URL string
|
URL string
|
||||||
PDFformat string
|
PDFformat string
|
||||||
linkPaths []string
|
|
||||||
scriptPaths []string
|
|
||||||
)
|
)
|
||||||
|
|
||||||
err := form.
|
err := form.
|
||||||
@@ -163,176 +161,12 @@ func convertURLRoute(chromium API, engine gotenberg.PDFEngine) api.Route {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}).
|
}).
|
||||||
Paths([]string{".woff2", ".woff", ".ttf", ".css"}, &linkPaths).
|
|
||||||
Paths([]string{".js"}, &scriptPaths).
|
|
||||||
Validate()
|
Validate()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("validate form data: %w", err)
|
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)
|
err = convertURL(ctx, chromium, engine, URL, PDFformat, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("convert URL to PDF: %w", err)
|
return fmt.Errorf("convert URL to PDF: %w", err)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package chromium
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -208,90 +207,6 @@ func TestConvertURLHandler(t *testing.T) {
|
|||||||
expectHTTPErr: true,
|
expectHTTPErr: true,
|
||||||
expectHTTPStatus: http.StatusBadRequest,
|
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: func() *api.MockContext {
|
||||||
ctx := &api.MockContext{Context: &api.Context{}}
|
ctx := &api.MockContext{Context: &api.Context{}}
|
||||||
@@ -325,76 +240,19 @@ func TestConvertURLHandler(t *testing.T) {
|
|||||||
"url": {
|
"url": {
|
||||||
"foo",
|
"foo",
|
||||||
},
|
},
|
||||||
})
|
"extraLinkTags": {
|
||||||
ctx.SetFiles(map[string]string{
|
`[{"href":"https://cdn.foo/foo.css"},{"href":"https://cdn.bar/bar.css"}]`,
|
||||||
"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",
|
|
||||||
},
|
},
|
||||||
"extraScriptTags": {
|
"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
|
return ctx
|
||||||
}(),
|
}(),
|
||||||
api: func() API {
|
api: func() API {
|
||||||
chromiumAPI := struct{ ProtoAPI }{}
|
chromiumAPI := struct{ ProtoAPI }{}
|
||||||
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, options Options) error {
|
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, _ 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
test/testdata/chromium/html/sample11/font.woff
vendored
BIN
test/testdata/chromium/html/sample11/font.woff
vendored
Binary file not shown.
12
test/testdata/chromium/html/sample11/index.html
vendored
12
test/testdata/chromium/html/sample11/index.html
vendored
@@ -1,12 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset=v"utf-8">
|
|
||||||
<title>Gutenberg</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<p id="extra">Extra link tags <span id="not">not</span> working</p>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
let extra = document.getElementById('extra')
|
|
||||||
extra.innerText = extra.innerText + ' and extra JavaScript tags too!'
|
|
||||||
14
test/testdata/chromium/html/sample11/style.css
vendored
14
test/testdata/chromium/html/sample11/style.css
vendored
@@ -1,14 +0,0 @@
|
|||||||
@font-face {
|
|
||||||
font-family: 'Local';
|
|
||||||
src: url('font.woff') format('woff');
|
|
||||||
font-weight: normal;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
#extra {
|
|
||||||
font-family: 'Local'
|
|
||||||
}
|
|
||||||
|
|
||||||
#not {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user