Core Rule Set: a Ghost post's or page's canonical URL passes when it names a command or a file (closes #374)
check / check (push) Waiting to run
check / check (push) Waiting to run
While bodies are read, 932120 refused the canonical URL `https://example.com/blog/powershell/` and 930120 refused `https://example.com/blog/.htaccess/`. The new exclusions 900383 and 900384 switch both rules off for `posts.N.canonical_url` under `/ghost/api/admin/posts/` and `pages.N.canonical_url` under `/ghost/api/admin/pages/`, as `url` does, and only when the query has no field of that name. The recorded requests gain the saves the Ghost 6.76.0 admin sends when those addresses are set in a post's and a page's settings. Model: opus-5-5
This commit is contained in:
1 parent
c4bf76c966
commit
8e85d12e04
4 files changed
+56
-6
No files matched your search
@@ -143,6 +143,16 @@ var ghostAddressFields = []ghostField{
|
||||
{http.MethodPut, ghostUser, `{"users":[{"email":"VALUE"}]}`},
|
||||
}
|
||||
|
||||
// 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
|
||||
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.
|
||||
//
|
||||
@@ -220,13 +230,16 @@ var ghostQueryFields = []string{
|
||||
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
|
||||
var ghostURLs = []string{
|
||||
"https://github.com/example/site/blob/main/.htaccess",
|
||||
"https://example.com/blog/powershell-tips?ref=ghost",
|
||||
"https://example.com/docs/python3/",
|
||||
"https://example.com/blog/powershell/",
|
||||
"https://example.com/blog/.htaccess/",
|
||||
}
|
||||
|
||||
// ghostAddresses are addresses 930120, 932120 and 932260 refused.
|
||||
@@ -327,6 +340,10 @@ func TestGhostOrdinaryValuesPassInTheExcludedFields(t *testing.T) {
|
||||
|
||||
for _, address := range ghostURLs {
|
||||
wantGhostQuery(t, ghostEmbed+url.QueryEscape(address), false)
|
||||
|
||||
for _, f := range ghostCanonicalURLFields {
|
||||
wantGhostField(t, f, address, false)
|
||||
}
|
||||
}
|
||||
|
||||
for _, address := range ghostAddresses {
|
||||
@@ -395,7 +412,7 @@ func TestGhostAttacksAreRefusedInTheExcludedFields(t *testing.T) {
|
||||
|
||||
for _, attack := range ghostAttacks {
|
||||
for _, f := range slices.Concat(ghostTextFields, ghostAddressFields,
|
||||
ghostArticleFields, ghostCodeFields) {
|
||||
ghostCanonicalURLFields, ghostArticleFields, ghostCodeFields) {
|
||||
wantGhostField(t, f, attack, true)
|
||||
}
|
||||
|
||||
@@ -407,7 +424,7 @@ 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,
|
||||
ghostSnippetAndNewsletterFooterFields) {
|
||||
ghostCanonicalURLFields, ghostSnippetAndNewsletterFooterFields) {
|
||||
wantGhostField(t, f, scriptTag, true)
|
||||
}
|
||||
|
||||
@@ -416,8 +433,10 @@ func TestGhostAttacksAreRefusedInTheExcludedFields(t *testing.T) {
|
||||
}
|
||||
|
||||
// A command after a separator stays refused wherever its rules do not
|
||||
// misfire: in the query fields, the plain text and the addresses.
|
||||
for _, f := range slices.Concat(ghostTextFields, ghostAddressFields) {
|
||||
// misfire: in the query fields, the plain text, the addresses and the
|
||||
// canonical URLs.
|
||||
for _, f := range slices.Concat(ghostTextFields, ghostAddressFields,
|
||||
ghostCanonicalURLFields) {
|
||||
wantGhostField(t, f, commandAfterASeparator, true)
|
||||
}
|
||||
|
||||
@@ -462,6 +481,8 @@ func TestGhostExclusionsAreNarrow(t *testing.T) {
|
||||
setupEmail = `{"setup":[{"email":"python3.dev@example.com"}]}`
|
||||
passwordResetEmail = `{"password_reset":[{"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 }{
|
||||
@@ -498,6 +519,12 @@ func TestGhostExclusionsAreNarrow(t *testing.T) {
|
||||
{ghostPasswordReset + "?json.password_reset.0.email=x", passwordResetEmail},
|
||||
{ghostPosts, 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"}`},
|
||||
{"/ghost/api/admin/users/me/", `{"password":"' OR '1'='1"}`},
|
||||
{"/ghost/api/admin/posts/1/", `{"users":[{"profile_image":null}]}`},
|
||||
|
||||
Reference in new issue
Block a user