Core Rule Set: random passwords pass at sign-in (closes #276)
check / check (push) In progress

A password manager's random password set off 35 Core Rule Set rules,
about 1 in 40 refused. The password fields the app exclusions name now
skip those rules on their paths: WordPress pwd, Drupal pass, MediaWiki
wpPassword, lgpassword and password, Mastodon's sign-in, sign-up,
password change, move and delete passwords, and Nextcloud's sign-in
password. The tests draw 2,000 random passwords from a fixed seed, plus
one rare password for each rule the 2,000 do not set off. Those that set
off a rule in a field no exclusion names are sent to every password
field. An SQL injection or a path traversal sent as a password now
passes; a script injection is still refused.

Model: opus-5-5
This commit is contained in:
clawbot authored and sneak committed 2026-11-10 09:31:51 +00:00
1 parent 463a19ef2a
commit 4dbd134d49
12 files changed
+735 -27

No files matched your search

+35 -2
View File
@@ -15,6 +15,9 @@ import (
// nextcloudThreshold is SWW_WAF_ANOMALY_THRESHOLD's default.
const nextcloudThreshold = 5
// nextcloudSignIn is the sign-in page.
const nextcloudSignIn = "/login"
// nextcloudShare is a public share's page.
const nextcloudShare = "/s/AbCdEfGhIjKlMnO"
@@ -82,8 +85,7 @@ var nextcloudQueryFields = []string{
//
//nolint:gochecknoglobals // a constant cannot be a list
var nextcloudFormFields = []struct{ target, field string }{
{"/login", "user="},
{"/login", passwordField},
{nextcloudSignIn, "user="},
{nextcloudUsers, "userid="},
{nextcloudUsers, "displayName="},
{nextcloudUsers, "groups%5B%5D="},
@@ -253,6 +255,17 @@ func nextcloudTextScores(t *testing.T, text string) map[string]int {
return scores
}
// nextcloudPasswordScore returns the score that the Core Rule Set, reading
// bodies, gives signing in with value, a query value, as the password.
func nextcloudPasswordScore(t *testing.T, value string) int {
t.Helper()
r := request{http.MethodPost, nextcloudSignIn, []string{formData}}
got, _ := inspectBody(t, readingBodies(t), r, passwordField+value)
return got.Score
}
func TestNextcloudHardValuesPassInTheExcludedFields(t *testing.T) {
t.Parallel()
@@ -262,9 +275,25 @@ func TestNextcloudHardValuesPassInTheExcludedFields(t *testing.T) {
t.Errorf("%s refused: %d", request, score)
}
}
if score := nextcloudPasswordScore(t, value); score >= nextcloudThreshold {
t.Errorf("password %s refused: %d", value, score)
}
}
}
func TestNextcloudPasswordPasses(t *testing.T) {
t.Parallel()
r := request{http.MethodPost, nextcloudSignIn, []string{formData}}
wantRandomPasswordsPass(t, r, "password")
// No rule the password keeps catches the SQL injection or the path
// traversal.
wantPasswordAttacksRefused(t, r, "password", scriptTag)
}
func TestNextcloudSentencesPassInTheTexts(t *testing.T) {
t.Parallel()
@@ -313,6 +342,10 @@ func TestNextcloudAttacksAreRefusedInTheExcludedFields(t *testing.T) {
t.Errorf("%s let through: %d", request, score)
}
}
if score := nextcloudPasswordScore(t, commandInjection); score < nextcloudThreshold {
t.Errorf("password %s let through: %d", commandInjection, score)
}
}
func TestNextcloudAccountCookieSkipsTheFourRules(t *testing.T) {