Core Rule Set: a repository's name passes in the adopt form's id (closes #365)
check / check (push) Waiting to run
check / check (push) Waiting to run
While bodies are read, the forms that adopt or delete an unadopted repository were refused when the repository's name held a command, a system file's name or a shell path, such as alice/python3-app, alice/.profile or robin/assets. id there now skips 930120, 932120, 932160 and 932260. The paths are gitea's /-/admin/repos/unadopted, Forgejo's /admin/repos/unadopted, and /user/settings/repos/unadopted, which both serve. Model: opus-5-5
This commit is contained in:
1 parent
4b62bc84ce
commit
d5ff3ef763
4 files changed
+108
-12
No files matched your search
@@ -8,6 +8,13 @@ import (
|
||||
"sneak.berlin/go/smallwebwaf/internal/coreruleset"
|
||||
)
|
||||
|
||||
// giteaAdminUnadoptedRepos and forgejoAdminUnadoptedRepos are the admin pages
|
||||
// of unadopted repositories. The forms that adopt or delete one post there.
|
||||
const (
|
||||
giteaAdminUnadoptedRepos = "/-/admin/repos/unadopted"
|
||||
forgejoAdminUnadoptedRepos = "/admin/repos/unadopted"
|
||||
)
|
||||
|
||||
// giteaSearches are searches of the pages gitea and Forgejo share whose q
|
||||
// skips the lists of system files and command names, in the eighth change.
|
||||
// gitea serves its admin pages under /-/admin/ and Forgejo under /admin/.
|
||||
@@ -30,12 +37,12 @@ var giteaSearches = []string{
|
||||
"/-/admin/users",
|
||||
"/-/admin/orgs",
|
||||
"/-/admin/repos",
|
||||
"/-/admin/repos/unadopted",
|
||||
giteaAdminUnadoptedRepos,
|
||||
"/-/admin/emails",
|
||||
"/-/admin/emails/activate",
|
||||
"/admin/actions/runners",
|
||||
"/admin/users",
|
||||
"/admin/repos/unadopted",
|
||||
forgejoAdminUnadoptedRepos,
|
||||
"/admin/emails/activate",
|
||||
}
|
||||
|
||||
@@ -60,7 +67,7 @@ func TestGiteaAdminFormsLetHardValuesThroughInTheBody(t *testing.T) {
|
||||
for _, value := range hardValues {
|
||||
adopt := "id=alice%2Flegacy-site&action=adopt&q=" + value + "&page=1"
|
||||
|
||||
for _, path := range []string{"/-/admin/repos/unadopted", "/admin/repos/unadopted"} {
|
||||
for _, path := range []string{giteaAdminUnadoptedRepos, forgejoAdminUnadoptedRepos} {
|
||||
r := request{http.MethodPost, path, []string{formData}}
|
||||
wantBody(t, crs, r, adopt, coreruleset.Result{}, adopt)
|
||||
}
|
||||
@@ -75,6 +82,67 @@ func TestGiteaAdminFormsLetHardValuesThroughInTheBody(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGiteaFormsThatAdoptARepositoryLetItsNameThrough(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const threshold = 5 // SWW_WAF_ANOMALY_THRESHOLD's default
|
||||
|
||||
crs := readingBodies(t)
|
||||
|
||||
// The admin forms send the repository's owner and name in id, and the
|
||||
// form in a user's settings only its name. robin/assets holds bin/as, a
|
||||
// shell path.
|
||||
ownerAndName := []string{
|
||||
"alice%2Fpython3-app",
|
||||
"alice%2Fpowershell-scripts",
|
||||
"alice%2F.profile",
|
||||
"robin%2Fassets",
|
||||
}
|
||||
|
||||
for _, form := range []struct {
|
||||
path string
|
||||
ids []string
|
||||
}{
|
||||
{giteaAdminUnadoptedRepos, ownerAndName},
|
||||
{forgejoAdminUnadoptedRepos, ownerAndName},
|
||||
{
|
||||
"/user/settings/repos/unadopted", // gitea's and Forgejo's
|
||||
[]string{"python3-app", "powershell-scripts", ".profile"},
|
||||
},
|
||||
} {
|
||||
r := request{http.MethodPost, form.path, []string{formData}}
|
||||
|
||||
for _, action := range []string{"adopt", "delete"} {
|
||||
for _, id := range form.ids {
|
||||
body := "id=" + id + "&action=" + action
|
||||
wantBody(t, crs, r, body, coreruleset.Result{}, body)
|
||||
}
|
||||
}
|
||||
|
||||
// Another field keeps those rules.
|
||||
body := "id=" + form.ids[0] + "&action=adopt&page=python3"
|
||||
wantBody(t, crs, r, body, matched(932260), body)
|
||||
|
||||
// Attacks in id are still refused.
|
||||
for _, attack := range []string{
|
||||
sqlInjection,
|
||||
scriptInjection,
|
||||
commandInjection,
|
||||
"..%2F..%2Fetc%2Fpasswd",
|
||||
} {
|
||||
body := "id=" + attack + "&action=adopt"
|
||||
if got, _ := inspectBody(t, crs, r, body); got.Score < threshold {
|
||||
t.Errorf("POST %s %s let through: %+v", form.path, body, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// On another path, id keeps those rules.
|
||||
r := request{http.MethodPost, "/-/admin/repos", []string{formData}}
|
||||
body := "id=alice%2Fpython3-app&action=adopt&q=&page=1"
|
||||
wantBody(t, crs, r, body, matched(932260), body)
|
||||
}
|
||||
|
||||
func TestGiteaAttacksAreRefusedInItsSearches(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in new issue
Block a user