Core Rule Set: WordPress's names and addresses pass on more fields (closes #245)
check / check (push) Failing after 2m22s
check / check (push) Failing after 2m22s
The four rules on system files and commands (930120, 932120, 932250, 932260) now skip: - page, p, postId and slug on all of wp-admin; - url on the block editor's link previews and embeds: the REST group now covers /wp-block-editor/v1/ and /oembed/1.0/; - the JSON fields title, excerpt, slug and name on /wp/v2/ routes. A commenter's url skips 930120 and 932120 only. Every field keeps 932235. Add 61 requests recorded from WordPress 7.1.4 with these fields. Model: opus-5-5
This commit is contained in:
1 parent
decd9b4658
commit
5064c41178
5 files changed
+238
-17
No files matched your search
@@ -665,6 +665,15 @@ func wordpressAttackTargets(attack string) []string {
|
||||
"/wp-admin/plugin-editor.php?plugin=" + attack,
|
||||
"/wp-admin/plugins.php?plugin=" + attack,
|
||||
"/wp-admin/update.php?plugin=" + attack,
|
||||
"/wp-admin/admin.php?page=" + attack,
|
||||
"/wp-admin/options-general.php?page=" + attack,
|
||||
"/wp-admin/tools.php?page=" + attack,
|
||||
"/wp-admin/site-editor.php?p=" + attack,
|
||||
"/wp-admin/site-editor.php?postId=" + attack,
|
||||
"/wp-admin/admin-ajax.php?slug=" + attack,
|
||||
"/wp-json/wp-block-editor/v1/url-details?url=" + attack,
|
||||
"/wp-json/oembed/1.0/proxy?url=" + attack,
|
||||
"/?rest_route=/oembed/1.0/proxy&url=" + attack,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -717,6 +726,24 @@ func TestOrdinaryWordPressRequestsPassAndAttacksAreRefused(t *testing.T) {
|
||||
t.Errorf("POST %s %q let through: %+v", target, body, got)
|
||||
}
|
||||
}
|
||||
|
||||
// In the body fields that skip some rules.
|
||||
decoded, _ := url.QueryUnescape(attack)
|
||||
|
||||
for _, tt := range []struct{ target, contentType, body string }{
|
||||
{"/wp-admin/admin-ajax.php", formData, "action=install-plugin&slug=" + attack},
|
||||
{"/wp-comments-post.php", formData, "url=" + attack},
|
||||
{"/wp-json/wp/v2/posts/1", jsonBody, `{"title":` + strconv.Quote(decoded) + `}`},
|
||||
{"/wp-json/wp/v2/posts/1", jsonBody, `{"excerpt":` + strconv.Quote(decoded) + `}`},
|
||||
{"/wp-json/wp/v2/posts/1", jsonBody, `{"slug":` + strconv.Quote(decoded) + `}`},
|
||||
{"/wp-json/wp/v2/tags", jsonBody, `{"name":` + strconv.Quote(decoded) + `}`},
|
||||
} {
|
||||
got, _ := inspectBody(t, bodies,
|
||||
request{http.MethodPost, tt.target, []string{tt.contentType}}, tt.body)
|
||||
if got.Score < threshold {
|
||||
t.Errorf("POST %s %q let through: %+v", tt.target, tt.body, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -829,7 +856,7 @@ func TestWordPressGroupsSkipRulesOnTheirFields(t *testing.T) {
|
||||
"/wp-admin/options.php?",
|
||||
[]string{
|
||||
"s", "search", "q", "term", "tag", "category_name", "plugin",
|
||||
"checked%5B%5D", "theme", "stylesheet",
|
||||
"checked%5B%5D", "theme", "stylesheet", "page", "p", "postId", "slug",
|
||||
},
|
||||
[]string{"url", "return", "redirect_to", "wp_http_referer", "_wp_http_referer"},
|
||||
},
|
||||
@@ -863,6 +890,9 @@ func TestWordPressGroupsSkipRulesOnTheirFields(t *testing.T) {
|
||||
"/?rest_route=/wp/v2/tags&slug=python3",
|
||||
"/wp-json/oembed/1.0/embed?url=python3",
|
||||
"/?rest_route=/oembed/1.0/embed&url=python3",
|
||||
"/wp-json/oembed/1.0/proxy?url=python3",
|
||||
"/wp-json/wp-block-editor/v1/url-details?url=python3",
|
||||
"/?rest_route=/wp-block-editor/v1/url-details&url=python3",
|
||||
"/index.php?tag=python3",
|
||||
"/page/2/?tag=python3",
|
||||
"/index.php/page/2/?s=python3",
|
||||
@@ -891,9 +921,35 @@ func TestWordPressCommandAfterASeparatorIsRefusedOnSearchesAndNames(t *testing.T
|
||||
"/?tag=",
|
||||
"/wp-admin/edit.php?tag=",
|
||||
"/wp-admin/admin-ajax.php?q=",
|
||||
"/wp-admin/admin.php?page=",
|
||||
"/wp-admin/options-general.php?page=",
|
||||
"/wp-admin/tools.php?page=",
|
||||
"/wp-admin/site-editor.php?p=",
|
||||
"/wp-admin/site-editor.php?postId=",
|
||||
"/wp-admin/admin-ajax.php?slug=",
|
||||
"/wp-json/wp-block-editor/v1/url-details?url=",
|
||||
"/wp-json/oembed/1.0/proxy?url=",
|
||||
} {
|
||||
wantResult(t, crs, get(target+command), matched(932235))
|
||||
}
|
||||
|
||||
bodies := readingBodiesUpTo64K(t)
|
||||
decoded, _ := url.QueryUnescape(command)
|
||||
|
||||
for _, tt := range []struct{ target, contentType, body string }{
|
||||
{"/wp-admin/admin-ajax.php", formData, "action=install-plugin&slug=" + command},
|
||||
{"/wp-json/wp/v2/posts/1", jsonBody, `{"title":"` + decoded + `"}`},
|
||||
{"/wp-json/wp/v2/posts/1", jsonBody, `{"excerpt":"` + decoded + `"}`},
|
||||
{"/wp-json/wp/v2/posts/1", jsonBody, `{"slug":"` + decoded + `"}`},
|
||||
{"/wp-json/wp/v2/tags", jsonBody, `{"name":"` + decoded + `"}`},
|
||||
} {
|
||||
r := request{http.MethodPost, tt.target, []string{tt.contentType}}
|
||||
wantBody(t, bodies, r, tt.body, matched(932235), tt.body)
|
||||
}
|
||||
|
||||
// A commenter's address keeps 932260 too.
|
||||
comment := request{http.MethodPost, "/wp-comments-post.php", []string{formData}}
|
||||
wantBody(t, bodies, comment, "url="+command, matched(932235, 932260), "url="+command)
|
||||
}
|
||||
|
||||
// TestWordPressOtherFieldsAreExcludedOnlyThere checks each field and rule
|
||||
@@ -946,6 +1002,16 @@ func TestWordPressOtherFieldsAreExcludedOnlyThere(t *testing.T) {
|
||||
{"/wp-admin/plugin-editor.php?plugin=docker-helper", 932260},
|
||||
{"/wp-admin/plugins.php?plugin=powershell-tools", 932120},
|
||||
{"/wp-admin/update.php?plugin=docker-helper", 932260},
|
||||
{"/wp-admin/admin.php?page=docker-helper", 932260},
|
||||
{"/wp-admin/options-general.php?page=powershell-tools", 932120},
|
||||
{"/wp-admin/tools.php?page=ls+-la", 932250},
|
||||
{"/wp-admin/tools.php?page=.htaccess", 930120},
|
||||
{"/wp-admin/site-editor.php?p=%2Fwp_template%2Fpython3-theme%2F%2Findex", 932260},
|
||||
{"/wp-admin/site-editor.php?postId=powershell-theme%2F%2Findex", 932120},
|
||||
{"/wp-admin/admin-ajax.php?slug=docker-helper", 932260},
|
||||
{"/wp-json/wp-block-editor/v1/url-details?url=" +
|
||||
"http%3A%2F%2Fblog.example%2Fpowershell-basics%2F", 932120},
|
||||
{"/wp-json/oembed/1.0/proxy?url=http%3A%2F%2Fblog.example%2F.htaccess", 930120},
|
||||
} {
|
||||
_, query, _ := strings.Cut(tt.target, "?")
|
||||
wantChange(t, crs, get(tt.target), get(elsewhere+"?"+query), matched(tt.rule))
|
||||
@@ -1002,6 +1068,65 @@ func TestWordPressBodyFieldsAreCheckedInTheQuery(t *testing.T) {
|
||||
wantResult(t, crs,
|
||||
get("/wp-json/wp/v2/posts/1?json.content="+url.QueryEscape(wordpressBlock)),
|
||||
matched(941180))
|
||||
wantResult(t, crs, get("/wp-json/wp/v2/posts/1?json.title=python3"), matched(932260))
|
||||
wantResult(t, crs, get("/wp-comments-post.php?url=python3"), matched(932260))
|
||||
}
|
||||
|
||||
// TestWordPressBodyNamesAndAddressesAreExcludedOnlyThere checks the JSON
|
||||
// fields of a REST API save and a commenter's address, against the same
|
||||
// values in another field and off their paths.
|
||||
func TestWordPressBodyNamesAndAddressesAreExcludedOnlyThere(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
crs := readingBodiesUpTo64K(t)
|
||||
|
||||
for _, v := range []struct {
|
||||
value string
|
||||
rule int
|
||||
}{
|
||||
{".htaccess", 930120},
|
||||
{"powershell", 932120},
|
||||
{"ls -la", 932250},
|
||||
{"python3", 932260},
|
||||
} {
|
||||
for _, name := range []string{"title", "excerpt", "slug", "name"} {
|
||||
body := `{"` + name + `":"` + v.value + `"}`
|
||||
for _, address := range wordpressRESTAddresses("/wp/v2/posts/1") {
|
||||
r := request{http.MethodPost, address, []string{jsonBody, wordpressOverride}}
|
||||
wantBody(t, crs, r, body, coreruleset.Result{}, body)
|
||||
}
|
||||
|
||||
r := request{http.MethodPost, "/wp-json/example/v1/things", []string{jsonBody}}
|
||||
wantBody(t, crs, r, body, matched(v.rule), body)
|
||||
}
|
||||
|
||||
other := `{"description":"` + v.value + `"}`
|
||||
r := request{http.MethodPost, "/wp-json/wp/v2/tags", []string{jsonBody}}
|
||||
wantBody(t, crs, r, other, matched(v.rule), other)
|
||||
}
|
||||
|
||||
comment := request{http.MethodPost, "/wp-comments-post.php", []string{formData}}
|
||||
elsewhere := request{http.MethodPost, "/example/things", []string{formData}}
|
||||
|
||||
// A commenter's address skips the two rules that refused an address.
|
||||
for _, v := range []struct {
|
||||
address string
|
||||
rule int
|
||||
}{
|
||||
{"http://blog.example/.htaccess", 930120},
|
||||
{"http://blog.example/powershell-basics/", 932120},
|
||||
} {
|
||||
body := "url=" + url.QueryEscape(v.address)
|
||||
wantBody(t, crs, comment, body, coreruleset.Result{}, body)
|
||||
wantBody(t, crs, elsewhere, body, matched(v.rule), body)
|
||||
|
||||
author := "author=" + url.QueryEscape(v.address)
|
||||
wantBody(t, crs, comment, author, matched(v.rule), author)
|
||||
}
|
||||
|
||||
// The rules on Unix commands keep it.
|
||||
wantBody(t, crs, comment, "url=python3", matched(932260), "url=python3")
|
||||
wantBody(t, crs, comment, "url=ls+-la", matched(932250), "url=ls+-la")
|
||||
}
|
||||
|
||||
func TestWordPressExclusionsAreNarrow(t *testing.T) {
|
||||
|
||||
Reference in new issue
Block a user