Core Rule Set: Drupal's ordinary requests pass by default (closes #236)
check / check (push) Successful in 5m4s

Adds drupal.conf, the tenth change: on Drupal's paths, the fields that
carry a search, a name, a file name or an address skip the rules that
refused ordinary requests there. The recorded requests from Drupal 11.4
pass at the defaults and while bodies are read; SQL injection, path
traversal and script injection stay refused on those fields. Drupal's
tests share the reader of recorded requests and the attack constants
with the other apps' tests.

Model: opus-5-5
This commit was merged in pull request #243.
This commit is contained in:
clawbot committed 2026-11-09 19:57:53 +01:00
1 parent fd734fcc22
commit decd9b4658
7 files changed
+1046 -31

No files matched your search

+19 -10
View File
@@ -126,7 +126,7 @@ func atDefaults(t *testing.T) *coreruleset.CoreRuleSet {
}
// 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
// ten 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,
@@ -529,6 +529,13 @@ func giteaRequests(t *testing.T) []request {
return requests
}
// An SQL injection and a script injection, as a query value. Every app's
// tests check that its fields still refuse them.
const (
sqlInjection = "1'%20OR%20'1'='1"
scriptInjection = "%3Cscript%3Ealert(1)%3C%2Fscript%3E"
)
func TestOrdinaryGiteaRequestsPassAndAttacksAreRefused(t *testing.T) {
t.Parallel()
@@ -545,9 +552,9 @@ func TestOrdinaryGiteaRequestsPassAndAttacksAreRefused(t *testing.T) {
// In q too, off gitea's search pages.
for _, parameter := range []string{"id", "q"} {
for _, attack := range []string{
"1'%20OR%20'1'='1",
sqlInjection,
"..%2Fcustom%2Fconf%2Fapp.ini",
"%3Cscript%3Ealert(1)%3C%2Fscript%3E",
scriptInjection,
} {
target := "/owner/repo?" + parameter + "=" + attack
if got := inspect(t, crs, get(target)); got.Score < threshold {
@@ -568,12 +575,14 @@ type recordedRequest struct {
Body string `json:"body"`
}
// wordpressRequests returns the recorded ordinary WordPress requests in
// testdata, each with its body.
func wordpressRequests(t *testing.T) []recordedRequest {
// recordedRequests returns the recorded ordinary requests in the file name
// in testdata, each with its body.
func recordedRequests(t *testing.T, name string) []recordedRequest {
t.Helper()
text, err := os.ReadFile(filepath.Join("testdata", "wordpress-requests.txt"))
path := filepath.Join("testdata", name)
text, err := os.ReadFile(path) //nolint:gosec // a file in testdata
if err != nil {
t.Fatal(err)
}
@@ -666,7 +675,7 @@ func TestOrdinaryWordPressRequestsPassAndAttacksAreRefused(t *testing.T) {
defaults, bodies := atDefaults(t), readingBodiesUpTo64K(t)
for _, recorded := range wordpressRequests(t) {
for _, recorded := range recordedRequests(t, "wordpress-requests.txt") {
r := request{recorded.Method, recorded.Target, recorded.Headers}
crsSets := []*coreruleset.CoreRuleSet{defaults}
@@ -683,9 +692,9 @@ func TestOrdinaryWordPressRequestsPassAndAttacksAreRefused(t *testing.T) {
}
for _, attack := range []string{
"1'%20OR%20'1'='1",
sqlInjection,
"..%2F..%2Fwp-config.php",
"%3Cscript%3Ealert(1)%3C%2Fscript%3E",
scriptInjection,
} {
for _, target := range wordpressAttackTargets(attack) {
if got := inspect(t, defaults, get(target)); got.Score < threshold {