Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d4ae61abf |
No files matched your search
@@ -609,9 +609,9 @@ lowers a client's limits by tag. See "Request tags" in [`SPEC.md`](SPEC.md).
|
|||||||
- `nextcloud`, whose fixes let through its sync client, its WebDAV methods,
|
- `nextcloud`, whose fixes let through its sync client, its WebDAV methods,
|
||||||
its searches, its file names, its account names, its comments and its
|
its searches, its file names, its account names, its comments and its
|
||||||
documents;
|
documents;
|
||||||
- `ghost`, whose fixes let through the text its admin and members type, its
|
- `ghost`, whose fixes let through its filters, its searches, its names, its
|
||||||
analytics, its code injection, ActivityPub and a post saved from its
|
email addresses, its code injection, its settings and a post saved from
|
||||||
editor, HTML included.
|
its editor, HTML included.
|
||||||
- A random password, as a password manager makes one, gets through these forms:
|
- A random password, as a password manager makes one, gets through these forms:
|
||||||
- signing in, signing up, and changing or resetting a password, on
|
- signing in, signing up, and changing or resetting a password, on
|
||||||
WordPress, Drupal, MediaWiki, Mastodon, Nextcloud, gitea and Ghost;
|
WordPress, Drupal, MediaWiki, Mastodon, Nextcloud, gitea and Ghost;
|
||||||
|
|||||||
@@ -883,13 +883,11 @@ one entry of `apps` in `internal/coreruleset/coreruleset.go`. gitea's fixes are:
|
|||||||
which refused `org.apache.commons.logging`.
|
which refused `org.apache.commons.logging`.
|
||||||
|
|
||||||
A branch, tag or package name can hold a slash, as `feat/docker-support`
|
A branch, tag or package name can hold a slash, as `feat/docker-support`
|
||||||
does. So `q` skips the rule for shell paths (932160) too on these
|
does. So `q` skips the rule for shell paths (932160) too on these pages,
|
||||||
searches, where it refused `/bin/sh`:
|
where it refused `/bin/sh`:
|
||||||
- a repository's branches, tags and packages pages;
|
- a repository's branches, tags and packages pages;
|
||||||
- an owner's packages page;
|
- an owner's packages page;
|
||||||
- the admin page of packages;
|
- the admin page of packages.
|
||||||
- the API's search of packages;
|
|
||||||
- the searches of the Composer and Conan registries.
|
|
||||||
|
|
||||||
- `q` on gitea's other searches, for the same four rules. Words such as
|
- `q` on gitea's other searches, for the same four rules. Words such as
|
||||||
`python3`, `powershell`, `ls -la` and `.htaccess` were refused there. The
|
`python3`, `powershell`, `ls -la` and `.htaccess` were refused there. The
|
||||||
@@ -904,43 +902,12 @@ one entry of `apps` in `internal/coreruleset/coreruleset.go`. gitea's fixes are:
|
|||||||
- the runner lists of the site, a user, an organisation and a
|
- the runner lists of the site, a user, an organisation and a
|
||||||
repository;
|
repository;
|
||||||
- the admin lists of users, organisations, repositories, unadopted
|
- the admin lists of users, organisations, repositories, unadopted
|
||||||
repositories and emails;
|
repositories and emails.
|
||||||
- the API's searches of topics, of an organisation's teams and of
|
|
||||||
emails;
|
|
||||||
- the searches of the Cargo, Chef, Composer and Conan registries, and
|
|
||||||
the NuGet registry's search service.
|
|
||||||
|
|
||||||
Most send `q` in the query string. The admin forms that adopt a
|
Most send `q` in the query string. The admin forms that adopt a
|
||||||
repository or activate an email send it in the body. gitea serves its
|
repository or activate an email send it in the body. gitea serves its
|
||||||
admin pages under `/-/admin/` and Forgejo under `/admin/`.
|
admin pages under `/-/admin/` and Forgejo under `/admin/`.
|
||||||
|
|
||||||
- Two fields of the API's searches other than `q`, for the same four rules:
|
|
||||||
- `query` of the search of unadopted repositories, which holds an owner
|
|
||||||
and a name, such as `alice/python3-app`;
|
|
||||||
- `name` of the search of a repository's milestones.
|
|
||||||
|
|
||||||
- Fields of the package registries' searches:
|
|
||||||
- `text` of the npm registry's search, for the same four rules and the
|
|
||||||
rule for shell paths (932160). An npm name can hold a slash, as
|
|
||||||
`@alice/tools-cli` does.
|
|
||||||
- `searchTerm` of the NuGet registry's older searches, for the same four
|
|
||||||
rules. These are the searches:
|
|
||||||
- `Search()` and `Packages()`;
|
|
||||||
- `Search()/$count` and `Packages()/$count`, which count their
|
|
||||||
results.
|
|
||||||
- `$filter` of those searches, for the lists of system files and Windows
|
|
||||||
commands (930120, 932120). It holds an expression such as
|
|
||||||
`tolower(Id) eq 'python3'`.
|
|
||||||
- The names `$filter`, `$orderby` and `$top` on those searches, for the
|
|
||||||
rule for MongoDB operators (942290). It refused every such search.
|
|
||||||
- `q` of a Conan search, for the rule for SQL injection (942100), which
|
|
||||||
refused the pattern `zlib/*`. `q` skips it only when the request has
|
|
||||||
one `q`, in the query string or the body. That `q` has the shape of a
|
|
||||||
Conan reference and holds no `*/`, which ends an SQL comment. A
|
|
||||||
reference is a name, then optionally `/` and a version, then
|
|
||||||
optionally `@` and a user, then optionally `/` and a channel. Each
|
|
||||||
part is made of letters, digits, `_`, `.`, `+`, `-` and `*`.
|
|
||||||
|
|
||||||
- The path of these requests, which names a file:
|
- The path of these requests, which names a file:
|
||||||
- a file's page;
|
- a file's page;
|
||||||
- its raw file;
|
- its raw file;
|
||||||
@@ -1789,87 +1756,35 @@ Ghost's fixes: on Ghost's paths, some fields skip the rules that refused
|
|||||||
ordinary Ghost requests there. `internal/coreruleset/ghost.conf` lists each rule
|
ordinary Ghost requests there. `internal/coreruleset/ghost.conf` lists each rule
|
||||||
and the request it refused. Ghost's admin and integrations call the Admin API
|
and the request it refused. Ghost's admin and integrations call the Admin API
|
||||||
under `/ghost/api/admin/`. Themes and Ghost's scripts call the Content API under
|
under `/ghost/api/admin/`. Themes and Ghost's scripts call the Content API under
|
||||||
`/ghost/api/content/`, and members call `/members/api/`. The admin calls
|
`/ghost/api/content/`, and members call `/members/api/`.
|
||||||
ActivityPub, a service of its own, under `/.ghost/activitypub/`.
|
|
||||||
|
|
||||||
Eight rules refused words that name commands and files, such as `python3`,
|
Three query fields skip some rules:
|
||||||
`ls -la`, `.htaccess` and the title `powershell & python3`. They are 930120,
|
|
||||||
932120, 932125, 932230, 932235, 932250, 932260 and 932380.
|
|
||||||
|
|
||||||
These query fields skip some rules:
|
- `filter`, a query in Ghost's own filter language, skips 932120 on the Admin
|
||||||
|
API and the Content API, so `tag:powershell` passes;
|
||||||
- `filter`, a query in Ghost's own filter language, skips those eight rules
|
|
||||||
except 930120, on the Admin API and the Content API;
|
|
||||||
- `search`, a search for a member, skips 930120, 932120, 932250 and 932260 on
|
- `search`, a search for a member, skips 930120, 932120, 932250 and 932260 on
|
||||||
the Admin API;
|
the Admin API;
|
||||||
- `url`, the address the editor looks up for an embed or a bookmark, skips
|
- `url`, the address the editor looks up for an embed or a bookmark, skips
|
||||||
930120 and 932120;
|
930120 and 932120.
|
||||||
- `source` and the `utm_` fields, which filter the analytics, skip those eight
|
|
||||||
rules under `/ghost/api/admin/stats/`;
|
|
||||||
- `pathname`, the page the analytics show, skips 930120, 932120 and 932260
|
|
||||||
there;
|
|
||||||
- `email_segment`, the members a post is sent to as a newsletter, skips 932120
|
|
||||||
on the Admin API's posts;
|
|
||||||
- `query`, a search for an account in ActivityPub, skips those eight rules;
|
|
||||||
- `email`, when an invitation is checked, skips 930120, 932120 and 932260.
|
|
||||||
|
|
||||||
The fields of JSON bodies where a person types plain text skip those eight
|
The fields of JSON bodies where a person types plain text skip 930120, 932120,
|
||||||
rules. They are these fields of posts, pages, tags, labels, members, newsletters
|
932125, 932230, 932250, 932260 and 932380. They are these fields of posts,
|
||||||
and snippets, on the Admin API:
|
pages, tags, labels, members, newsletters and snippets, on the Admin API:
|
||||||
|
|
||||||
- `title`, `name` and `slug`;
|
- `title`, `name` and `slug`;
|
||||||
- a post's `tags` and a member's `labels`, by name or slug;
|
- a post's `tags` and a member's `labels`, by name or slug;
|
||||||
- `custom_excerpt`, `description`, `note` and `sender_name`;
|
- `custom_excerpt`, `description`, `note` and `sender_name`;
|
||||||
- the `meta_`, `og_` and `twitter_` titles and descriptions.
|
- the `meta_`, `og_` and `twitter_` titles and descriptions.
|
||||||
|
|
||||||
These fields on the Admin API skip them too:
|
A member's `name` at `/members/api/` skips them too. A member's `email` skips
|
||||||
|
930120, 932120 and 932260, at `/members/api/` and when the admin adds or edits a
|
||||||
|
member. A staff user's address skips them too: `username` when signing in, and
|
||||||
|
`email` when creating the first staff user, asking for a password reset or
|
||||||
|
saving a profile.
|
||||||
|
|
||||||
- a staff user's `name`, `slug`, `bio`, `location` and `website`;
|
The `canonical_url` of a post or a page skips 930120 and 932120, as `url` does.
|
||||||
- `blogTitle` and `name` when creating the first staff user;
|
A post's skips them under `/ghost/api/admin/posts/`, and a page's under
|
||||||
- `name` when accepting an invitation;
|
`/ghost/api/admin/pages/`.
|
||||||
- `name`, `description` and `benefits` of tiers;
|
|
||||||
- `name`, `code`, `display_title` and `display_description` of offers;
|
|
||||||
- `title`, `description` and `excerpt` of recommendations;
|
|
||||||
- `name` and `description` of integrations, and `name` of webhooks;
|
|
||||||
- `feature_image_alt` and `feature_image_caption` of posts and pages.
|
|
||||||
|
|
||||||
These fields skip them on their own paths:
|
|
||||||
|
|
||||||
- a member's `name` at `/members/api/`;
|
|
||||||
- `html` of a member's comment at `/members/api/comments/`;
|
|
||||||
- `labels`, when the admin imports members from a CSV file;
|
|
||||||
- `content` of a note or a reply in ActivityPub, and `image.altText`, the
|
|
||||||
description of its image;
|
|
||||||
- `name`, `username` and `bio` of the account in ActivityPub.
|
|
||||||
|
|
||||||
A member's `email` skips 930120, 932120 and 932260, at `/members/api/` and when
|
|
||||||
the admin adds or edits a member. A staff user's address skips them too. It is
|
|
||||||
sent as `username` when signing in, and as `email` in these requests:
|
|
||||||
|
|
||||||
- creating the first staff user;
|
|
||||||
- asking for a password reset;
|
|
||||||
- saving a profile;
|
|
||||||
- inviting a staff user;
|
|
||||||
- accepting an invitation.
|
|
||||||
|
|
||||||
In ActivityPub, an account's handle and its address skip 930120 and 932120. The
|
|
||||||
domain of the site's handle skips 930120, 932120 and 932260. Each field skips
|
|
||||||
its rules only on its own path:
|
|
||||||
|
|
||||||
- `sourceHandle`, an account that moves here, when it is added at
|
|
||||||
`/.ghost/activitypub/v1/aliases`;
|
|
||||||
- `actorUri`, that account's address, when the admin removes it there;
|
|
||||||
- `targetHandle`, the account this one moves to, at
|
|
||||||
`/.ghost/activitypub/v1/migration`;
|
|
||||||
- `domain`, saved at `/.ghost/activitypub/v1/domain` and checked at
|
|
||||||
`/.ghost/activitypub/v1/domain/validate`.
|
|
||||||
|
|
||||||
The `canonical_url` of a post, a page or a tag skips 930120 and 932120, as `url`
|
|
||||||
does. Each skips them only on its own path:
|
|
||||||
|
|
||||||
- a post's under `/ghost/api/admin/posts/`;
|
|
||||||
- a page's under `/ghost/api/admin/pages/`;
|
|
||||||
- a tag's under `/ghost/api/admin/tags/`.
|
|
||||||
|
|
||||||
The articles carry HTML on purpose. These fields on the Admin API hold them:
|
The articles carry HTML on purpose. These fields on the Admin API hold them:
|
||||||
|
|
||||||
@@ -1889,14 +1804,12 @@ The articles skip these rules:
|
|||||||
Code injection is the HTML that Ghost adds to the head and the foot of a page.
|
Code injection is the HTML that Ghost adds to the head and the foot of a page.
|
||||||
These fields on the Admin API hold it:
|
These fields on the Admin API hold it:
|
||||||
|
|
||||||
- `codeinjection_head` and `codeinjection_foot` of posts, pages and tags;
|
- `codeinjection_head` and `codeinjection_foot` of posts and pages;
|
||||||
- `value` of settings, since the site's settings hold its code injection.
|
- `value` of settings, since the site's settings hold its code injection.
|
||||||
|
|
||||||
Code injection skips these rules:
|
Code injection skips these rules:
|
||||||
|
|
||||||
- 930120, 932120, 932125, 932230, 932235, 932250, 932260 and 932380, which
|
- 930120, 932120, 932125, 932230, 932250, 932260 and 932380, as plain text does;
|
||||||
refused words that name commands and files, such as the site's title
|
|
||||||
`powershell & python3`;
|
|
||||||
- 921130, 941100, 941160 and 941270, which refused `<meta>` and `<link>`
|
- 921130, 941100, 941160 and 941270, which refused `<meta>` and `<link>`
|
||||||
elements.
|
elements.
|
||||||
|
|
||||||
@@ -1905,22 +1818,15 @@ card can embed a widget and code injection can load analytics. These are the
|
|||||||
fields, on the Admin API:
|
fields, on the Admin API:
|
||||||
|
|
||||||
- `lexical`, `mobiledoc` and `html` of posts and pages;
|
- `lexical`, `mobiledoc` and `html` of posts and pages;
|
||||||
- `codeinjection_head` and `codeinjection_foot` of posts, pages and tags;
|
- `codeinjection_head` and `codeinjection_foot` of posts and pages;
|
||||||
- `value` of settings.
|
- `value` of settings.
|
||||||
|
|
||||||
`<script>alert(1)</script>` is still refused in snippets and newsletters.
|
`<script>alert(1)</script>` is still refused in snippets and newsletters.
|
||||||
`javascript:alert(1)` is still refused in the articles and code injection
|
`javascript:alert(1)` is still refused in the articles and code injection
|
||||||
(941170 and 941210). Path traversal and SQL injection are still refused in every
|
(941170 and 941210). Path traversal and SQL injection are still refused in every
|
||||||
field above. Script injection is too, outside the articles and code injection.
|
field above. Script injection is too, outside the articles and code injection.
|
||||||
|
`; wget http://example.com/x.sh` is still refused in the query fields, the plain
|
||||||
`; wget http://example.com/x.sh` is still refused in these fields:
|
text, the addresses and the canonical URLs.
|
||||||
|
|
||||||
- the addresses, the handles and the domain;
|
|
||||||
- the `canonical_url` of posts, pages and tags;
|
|
||||||
- `search`, `url`, `pathname`, `email_segment` and `email`.
|
|
||||||
|
|
||||||
The other fields above skip 932235, which refuses that command but also refuses
|
|
||||||
`powershell & python3`.
|
|
||||||
|
|
||||||
Coraza names a staff user's `profile_image` field `json.users.0.profile_image`.
|
Coraza names a staff user's `profile_image` field `json.users.0.profile_image`.
|
||||||
That name holds `.profile`, a file on the list of system files, so the name
|
That name holds `.profile`, a file on the list of system files, so the name
|
||||||
|
|||||||
@@ -919,10 +919,6 @@ const retypeKey = "retype"
|
|||||||
// system files (930120) holds.
|
// system files (930120) holds.
|
||||||
const htaccess = ".htaccess"
|
const htaccess = ".htaccess"
|
||||||
|
|
||||||
// binSh is the shell path /bin/sh as a query or form value, which the rule for
|
|
||||||
// shell paths (932160) refuses.
|
|
||||||
const binSh = "%2Fbin%2Fsh"
|
|
||||||
|
|
||||||
// hardValues are words that name commands and files, as a query or form
|
// hardValues are words that name commands and files, as a query or form
|
||||||
// value. An app's tests check that its fields let them through.
|
// value. An app's tests check that its fields let them through.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ func TestForgejoExclusionsAreNarrow(t *testing.T) {
|
|||||||
"/alice/-/packages/npm/x?q=",
|
"/alice/-/packages/npm/x?q=",
|
||||||
"/alice/tools/branches?other=",
|
"/alice/tools/branches?other=",
|
||||||
} {
|
} {
|
||||||
for _, value := range slices.Concat([]string{binSh}, hardValues) {
|
for _, value := range slices.Concat([]string{"%2Fbin%2Fsh"}, hardValues) {
|
||||||
if got := inspect(t, crs, get(target+value)); got.Score < threshold {
|
if got := inspect(t, crs, get(target+value)); got.Score < threshold {
|
||||||
t.Errorf("%s%s let through: %+v", target, value, got)
|
t.Errorf("%s%s let through: %+v", target, value, got)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,7 @@
|
|||||||
#
|
#
|
||||||
# Ghost's admin and its integrations call the Admin API under /ghost/api/admin/,
|
# Ghost's admin and its integrations call the Admin API under /ghost/api/admin/,
|
||||||
# themes and Ghost's own scripts call the Content API under /ghost/api/content/,
|
# themes and Ghost's own scripts call the Content API under /ghost/api/content/,
|
||||||
# and members call /members/api/. The admin calls ActivityPub, a service of its
|
# and members call /members/api/. Coraza names a field of a JSON body by its
|
||||||
# own, under /.ghost/activitypub/. Coraza names a field of a JSON body by its
|
|
||||||
# path, so the title of the first post saved is json.posts.0.title.
|
# path, so the title of the first post saved is json.posts.0.title.
|
||||||
|
|
||||||
# Every rule below is skipped unless Inspect has set tx.app_ghost. It sets it
|
# Every rule below is skipped unless Inspect has set tx.app_ghost. It sets it
|
||||||
@@ -18,22 +17,11 @@
|
|||||||
SecRule &TX:app_ghost "@eq 0" \
|
SecRule &TX:app_ghost "@eq 0" \
|
||||||
"id:900365,phase:1,pass,nolog,skipAfter:END_GHOST"
|
"id:900365,phase:1,pass,nolog,skipAfter:END_GHOST"
|
||||||
|
|
||||||
# filter, a query in Ghost's own filter language, skips seven rules on the
|
# filter, a query in Ghost's own filter language, skips 932120 on the Admin API
|
||||||
# Admin API and the Content API:
|
# and the Content API: filter=tag:powershell, filter=name:~'powershell'.
|
||||||
# 932120: filter=tag:powershell, filter=name:~'powershell'
|
|
||||||
# 932125, 932230, 932250: filter=title:~'powershell & ls -la'
|
|
||||||
# 932235, 932260: filter=title:~'powershell & python3', which the admin's
|
|
||||||
# post picker sends when a person types that title
|
|
||||||
# 932380: filter=title:~'python3 & powershell'
|
|
||||||
SecRule REQUEST_FILENAME "@rx ^/ghost/api/(?:admin|content)/" \
|
SecRule REQUEST_FILENAME "@rx ^/ghost/api/(?:admin|content)/" \
|
||||||
"id:900350,phase:1,pass,nolog,\
|
"id:900350,phase:1,pass,nolog,\
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:filter,\
|
ctl:ruleRemoveTargetById=932120;ARGS:filter"
|
||||||
ctl:ruleRemoveTargetById=932125;ARGS:filter,\
|
|
||||||
ctl:ruleRemoveTargetById=932230;ARGS:filter,\
|
|
||||||
ctl:ruleRemoveTargetById=932235;ARGS:filter,\
|
|
||||||
ctl:ruleRemoveTargetById=932250;ARGS:filter,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:filter,\
|
|
||||||
ctl:ruleRemoveTargetById=932380;ARGS:filter"
|
|
||||||
|
|
||||||
# search, the search for a member by name or address, skips four rules on the
|
# search, the search for a member by name or address, skips four rules on the
|
||||||
# Admin API:
|
# Admin API:
|
||||||
@@ -57,61 +45,10 @@ SecRule REQUEST_FILENAME "@streq /ghost/api/admin/oembed/" \
|
|||||||
ctl:ruleRemoveTargetById=930120;ARGS:url,\
|
ctl:ruleRemoveTargetById=930120;ARGS:url,\
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:url"
|
ctl:ruleRemoveTargetById=932120;ARGS:url"
|
||||||
|
|
||||||
# The query fields that filter the analytics skip the eight rules that 900353
|
# The fields a person types plain text into skip seven rules on the Admin API:
|
||||||
# lists, on /ghost/api/admin/stats/:
|
|
||||||
# 932120: source=powershell.example.com
|
|
||||||
# 932235, 932260: utm_campaign=powershell & python3
|
|
||||||
# 932250: utm_term=ls -la
|
|
||||||
# These fields filter the figures by the site a visit came from, or a
|
|
||||||
# campaign: source, utm_source, utm_medium, utm_campaign, utm_content and
|
|
||||||
# utm_term.
|
|
||||||
SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/stats/" \
|
|
||||||
"id:900367,phase:1,pass,nolog,\
|
|
||||||
ctl:ruleRemoveTargetById=930120;ARGS:/^(?:source|utm_(?:source|medium|campaign|content|term))$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:/^(?:source|utm_(?:source|medium|campaign|content|term))$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932125;ARGS:/^(?:source|utm_(?:source|medium|campaign|content|term))$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932230;ARGS:/^(?:source|utm_(?:source|medium|campaign|content|term))$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932235;ARGS:/^(?:source|utm_(?:source|medium|campaign|content|term))$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932250;ARGS:/^(?:source|utm_(?:source|medium|campaign|content|term))$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:/^(?:source|utm_(?:source|medium|campaign|content|term))$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932380;ARGS:/^(?:source|utm_(?:source|medium|campaign|content|term))$/"
|
|
||||||
|
|
||||||
# pathname, the page the analytics show, skips three rules on
|
|
||||||
# /ghost/api/admin/stats/, which refused the path of a post and of tag pages:
|
|
||||||
# 930120: pathname=/tag/proc/
|
|
||||||
# 932120: pathname=/python3-and-powershell/
|
|
||||||
# 932260: pathname=/tag/python3/
|
|
||||||
SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/stats/" \
|
|
||||||
"id:900385,phase:1,pass,nolog,\
|
|
||||||
ctl:ruleRemoveTargetById=930120;ARGS:pathname,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:pathname,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:pathname"
|
|
||||||
|
|
||||||
# email_segment, the members a post is sent to as a newsletter, skips 932120
|
|
||||||
# on the Admin API's posts: email_segment=label:powershell-users.
|
|
||||||
SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/posts/" \
|
|
||||||
"id:900368,phase:1,pass,nolog,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:email_segment"
|
|
||||||
|
|
||||||
# query, a search for an account in ActivityPub, skips the eight rules that
|
|
||||||
# 900353 lists: query=python3, query=@powershell@example.com.
|
|
||||||
SecRule REQUEST_FILENAME "@streq /.ghost/activitypub/v1/actions/search" \
|
|
||||||
"id:900369,phase:1,pass,nolog,\
|
|
||||||
ctl:ruleRemoveTargetById=930120;ARGS:query,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:query,\
|
|
||||||
ctl:ruleRemoveTargetById=932125;ARGS:query,\
|
|
||||||
ctl:ruleRemoveTargetById=932230;ARGS:query,\
|
|
||||||
ctl:ruleRemoveTargetById=932235;ARGS:query,\
|
|
||||||
ctl:ruleRemoveTargetById=932250;ARGS:query,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:query,\
|
|
||||||
ctl:ruleRemoveTargetById=932380;ARGS:query"
|
|
||||||
|
|
||||||
# The fields a person types plain text into skip eight rules on the Admin API,
|
|
||||||
# which refused words that name commands and files:
|
|
||||||
# 930120: the title "Using python3 and ls -la to tidy .htaccess files"
|
# 930120: the title "Using python3 and ls -la to tidy .htaccess files"
|
||||||
# 932120: the page's slug about-python3-powershell-htaccess
|
# 932120: the page's slug about-python3-powershell-htaccess
|
||||||
# 932125, 932230, 932250: the tag "Linux & ls -la"
|
# 932125, 932230, 932250: the tag "Linux & ls -la"
|
||||||
# 932235: the title "powershell & python3"
|
|
||||||
# 932260: the tag python3
|
# 932260: the tag python3
|
||||||
# 932380: the newsletter "Shell tips weekly: python3 & powershell"
|
# 932380: the newsletter "Shell tips weekly: python3 & powershell"
|
||||||
# These fields hold plain text:
|
# These fields hold plain text:
|
||||||
@@ -127,12 +64,11 @@ SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/" \
|
|||||||
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.(?:posts|pages|tags|labels|members|newsletters|snippets)\.\d+\.(?:title|name|slug|custom_excerpt|description|note|sender_name|(?:meta|og|twitter)_(?:title|description)|(?:tags|labels)\.\d+(?:\.(?:name|slug))?)$/,\
|
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.(?:posts|pages|tags|labels|members|newsletters|snippets)\.\d+\.(?:title|name|slug|custom_excerpt|description|note|sender_name|(?:meta|og|twitter)_(?:title|description)|(?:tags|labels)\.\d+(?:\.(?:name|slug))?)$/,\
|
||||||
ctl:ruleRemoveTargetById=932125;ARGS:/^json\.(?:posts|pages|tags|labels|members|newsletters|snippets)\.\d+\.(?:title|name|slug|custom_excerpt|description|note|sender_name|(?:meta|og|twitter)_(?:title|description)|(?:tags|labels)\.\d+(?:\.(?:name|slug))?)$/,\
|
ctl:ruleRemoveTargetById=932125;ARGS:/^json\.(?:posts|pages|tags|labels|members|newsletters|snippets)\.\d+\.(?:title|name|slug|custom_excerpt|description|note|sender_name|(?:meta|og|twitter)_(?:title|description)|(?:tags|labels)\.\d+(?:\.(?:name|slug))?)$/,\
|
||||||
ctl:ruleRemoveTargetById=932230;ARGS:/^json\.(?:posts|pages|tags|labels|members|newsletters|snippets)\.\d+\.(?:title|name|slug|custom_excerpt|description|note|sender_name|(?:meta|og|twitter)_(?:title|description)|(?:tags|labels)\.\d+(?:\.(?:name|slug))?)$/,\
|
ctl:ruleRemoveTargetById=932230;ARGS:/^json\.(?:posts|pages|tags|labels|members|newsletters|snippets)\.\d+\.(?:title|name|slug|custom_excerpt|description|note|sender_name|(?:meta|og|twitter)_(?:title|description)|(?:tags|labels)\.\d+(?:\.(?:name|slug))?)$/,\
|
||||||
ctl:ruleRemoveTargetById=932235;ARGS:/^json\.(?:posts|pages|tags|labels|members|newsletters|snippets)\.\d+\.(?:title|name|slug|custom_excerpt|description|note|sender_name|(?:meta|og|twitter)_(?:title|description)|(?:tags|labels)\.\d+(?:\.(?:name|slug))?)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932250;ARGS:/^json\.(?:posts|pages|tags|labels|members|newsletters|snippets)\.\d+\.(?:title|name|slug|custom_excerpt|description|note|sender_name|(?:meta|og|twitter)_(?:title|description)|(?:tags|labels)\.\d+(?:\.(?:name|slug))?)$/,\
|
ctl:ruleRemoveTargetById=932250;ARGS:/^json\.(?:posts|pages|tags|labels|members|newsletters|snippets)\.\d+\.(?:title|name|slug|custom_excerpt|description|note|sender_name|(?:meta|og|twitter)_(?:title|description)|(?:tags|labels)\.\d+(?:\.(?:name|slug))?)$/,\
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.(?:posts|pages|tags|labels|members|newsletters|snippets)\.\d+\.(?:title|name|slug|custom_excerpt|description|note|sender_name|(?:meta|og|twitter)_(?:title|description)|(?:tags|labels)\.\d+(?:\.(?:name|slug))?)$/,\
|
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.(?:posts|pages|tags|labels|members|newsletters|snippets)\.\d+\.(?:title|name|slug|custom_excerpt|description|note|sender_name|(?:meta|og|twitter)_(?:title|description)|(?:tags|labels)\.\d+(?:\.(?:name|slug))?)$/,\
|
||||||
ctl:ruleRemoveTargetById=932380;ARGS:/^json\.(?:posts|pages|tags|labels|members|newsletters|snippets)\.\d+\.(?:title|name|slug|custom_excerpt|description|note|sender_name|(?:meta|og|twitter)_(?:title|description)|(?:tags|labels)\.\d+(?:\.(?:name|slug))?)$/"
|
ctl:ruleRemoveTargetById=932380;ARGS:/^json\.(?:posts|pages|tags|labels|members|newsletters|snippets)\.\d+\.(?:title|name|slug|custom_excerpt|description|note|sender_name|(?:meta|og|twitter)_(?:title|description)|(?:tags|labels)\.\d+(?:\.(?:name|slug))?)$/"
|
||||||
|
|
||||||
# name, the name a member saves at /members/api/, skips the same eight rules.
|
# name, the name a member saves at /members/api/, skips the same seven rules.
|
||||||
SecRule REQUEST_FILENAME "@beginsWith /members/api/" \
|
SecRule REQUEST_FILENAME "@beginsWith /members/api/" \
|
||||||
"id:900359,phase:1,pass,nolog,chain"
|
"id:900359,phase:1,pass,nolog,chain"
|
||||||
SecRule &ARGS_GET:/^json\.name$/ "@eq 0" \
|
SecRule &ARGS_GET:/^json\.name$/ "@eq 0" \
|
||||||
@@ -140,99 +76,10 @@ SecRule REQUEST_FILENAME "@beginsWith /members/api/" \
|
|||||||
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.name$/,\
|
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.name$/,\
|
||||||
ctl:ruleRemoveTargetById=932125;ARGS:/^json\.name$/,\
|
ctl:ruleRemoveTargetById=932125;ARGS:/^json\.name$/,\
|
||||||
ctl:ruleRemoveTargetById=932230;ARGS:/^json\.name$/,\
|
ctl:ruleRemoveTargetById=932230;ARGS:/^json\.name$/,\
|
||||||
ctl:ruleRemoveTargetById=932235;ARGS:/^json\.name$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932250;ARGS:/^json\.name$/,\
|
ctl:ruleRemoveTargetById=932250;ARGS:/^json\.name$/,\
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.name$/,\
|
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.name$/,\
|
||||||
ctl:ruleRemoveTargetById=932380;ARGS:/^json\.name$/"
|
ctl:ruleRemoveTargetById=932380;ARGS:/^json\.name$/"
|
||||||
|
|
||||||
# More fields of plain text skip the same eight rules on the Admin API, which
|
|
||||||
# refused "python3 & powershell club", "Weekly ls -la and .htaccess tips" and
|
|
||||||
# "powershell & python3 deal" in them:
|
|
||||||
# users.<n>.name, slug, bio, location and website, a staff user's profile
|
|
||||||
# setup.<n>.blogTitle and name, creating the first staff user
|
|
||||||
# invitation.<n>.name, accepting a staff invitation
|
|
||||||
# tiers.<n>.name, description and benefits
|
|
||||||
# offers.<n>.name, code, display_title and display_description
|
|
||||||
# recommendations.<n>.title, description and excerpt
|
|
||||||
# integrations.<n>.name and description, and webhooks.<n>.name
|
|
||||||
# posts.<n>.feature_image_alt and feature_image_caption, and the same fields
|
|
||||||
# of pages
|
|
||||||
# Coraza matches a field's name in lower case, so blogTitle is blogtitle.
|
|
||||||
SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/" \
|
|
||||||
"id:900370,phase:1,pass,nolog,chain"
|
|
||||||
SecRule &ARGS_GET:/^json\.(?:users\.\d+\.(?:name|slug|bio|location|website)|setup\.\d+\.(?:blogtitle|name)|invitation\.\d+\.name|tiers\.\d+\.(?:name|description|benefits\.\d+)|offers\.\d+\.(?:name|code|display_title|display_description)|recommendations\.\d+\.(?:title|description|excerpt)|integrations\.\d+\.(?:name|description)|webhooks\.\d+\.name|(?:posts|pages)\.\d+\.feature_image_(?:alt|caption))$/ "@eq 0" \
|
|
||||||
"ctl:ruleRemoveTargetById=930120;ARGS:/^json\.(?:users\.\d+\.(?:name|slug|bio|location|website)|setup\.\d+\.(?:blogtitle|name)|invitation\.\d+\.name|tiers\.\d+\.(?:name|description|benefits\.\d+)|offers\.\d+\.(?:name|code|display_title|display_description)|recommendations\.\d+\.(?:title|description|excerpt)|integrations\.\d+\.(?:name|description)|webhooks\.\d+\.name|(?:posts|pages)\.\d+\.feature_image_(?:alt|caption))$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.(?:users\.\d+\.(?:name|slug|bio|location|website)|setup\.\d+\.(?:blogtitle|name)|invitation\.\d+\.name|tiers\.\d+\.(?:name|description|benefits\.\d+)|offers\.\d+\.(?:name|code|display_title|display_description)|recommendations\.\d+\.(?:title|description|excerpt)|integrations\.\d+\.(?:name|description)|webhooks\.\d+\.name|(?:posts|pages)\.\d+\.feature_image_(?:alt|caption))$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932125;ARGS:/^json\.(?:users\.\d+\.(?:name|slug|bio|location|website)|setup\.\d+\.(?:blogtitle|name)|invitation\.\d+\.name|tiers\.\d+\.(?:name|description|benefits\.\d+)|offers\.\d+\.(?:name|code|display_title|display_description)|recommendations\.\d+\.(?:title|description|excerpt)|integrations\.\d+\.(?:name|description)|webhooks\.\d+\.name|(?:posts|pages)\.\d+\.feature_image_(?:alt|caption))$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932230;ARGS:/^json\.(?:users\.\d+\.(?:name|slug|bio|location|website)|setup\.\d+\.(?:blogtitle|name)|invitation\.\d+\.name|tiers\.\d+\.(?:name|description|benefits\.\d+)|offers\.\d+\.(?:name|code|display_title|display_description)|recommendations\.\d+\.(?:title|description|excerpt)|integrations\.\d+\.(?:name|description)|webhooks\.\d+\.name|(?:posts|pages)\.\d+\.feature_image_(?:alt|caption))$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932235;ARGS:/^json\.(?:users\.\d+\.(?:name|slug|bio|location|website)|setup\.\d+\.(?:blogtitle|name)|invitation\.\d+\.name|tiers\.\d+\.(?:name|description|benefits\.\d+)|offers\.\d+\.(?:name|code|display_title|display_description)|recommendations\.\d+\.(?:title|description|excerpt)|integrations\.\d+\.(?:name|description)|webhooks\.\d+\.name|(?:posts|pages)\.\d+\.feature_image_(?:alt|caption))$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932250;ARGS:/^json\.(?:users\.\d+\.(?:name|slug|bio|location|website)|setup\.\d+\.(?:blogtitle|name)|invitation\.\d+\.name|tiers\.\d+\.(?:name|description|benefits\.\d+)|offers\.\d+\.(?:name|code|display_title|display_description)|recommendations\.\d+\.(?:title|description|excerpt)|integrations\.\d+\.(?:name|description)|webhooks\.\d+\.name|(?:posts|pages)\.\d+\.feature_image_(?:alt|caption))$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.(?:users\.\d+\.(?:name|slug|bio|location|website)|setup\.\d+\.(?:blogtitle|name)|invitation\.\d+\.name|tiers\.\d+\.(?:name|description|benefits\.\d+)|offers\.\d+\.(?:name|code|display_title|display_description)|recommendations\.\d+\.(?:title|description|excerpt)|integrations\.\d+\.(?:name|description)|webhooks\.\d+\.name|(?:posts|pages)\.\d+\.feature_image_(?:alt|caption))$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932380;ARGS:/^json\.(?:users\.\d+\.(?:name|slug|bio|location|website)|setup\.\d+\.(?:blogtitle|name)|invitation\.\d+\.name|tiers\.\d+\.(?:name|description|benefits\.\d+)|offers\.\d+\.(?:name|code|display_title|display_description)|recommendations\.\d+\.(?:title|description|excerpt)|integrations\.\d+\.(?:name|description)|webhooks\.\d+\.name|(?:posts|pages)\.\d+\.feature_image_(?:alt|caption))$/"
|
|
||||||
|
|
||||||
# A member's comment, html at /members/api/comments/, skips the same eight
|
|
||||||
# rules, which refused "I ran ls -la and fixed my .htaccess with python3". The
|
|
||||||
# rules for scripts and HTML still refuse a <script> element, an event handler
|
|
||||||
# and a javascript: link in it.
|
|
||||||
SecRule REQUEST_FILENAME "@beginsWith /members/api/comments/" \
|
|
||||||
"id:900371,phase:1,pass,nolog,chain"
|
|
||||||
SecRule &ARGS_GET:/^json\.comments\.\d+\.html$/ "@eq 0" \
|
|
||||||
"ctl:ruleRemoveTargetById=930120;ARGS:/^json\.comments\.\d+\.html$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.comments\.\d+\.html$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932125;ARGS:/^json\.comments\.\d+\.html$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932230;ARGS:/^json\.comments\.\d+\.html$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932235;ARGS:/^json\.comments\.\d+\.html$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932250;ARGS:/^json\.comments\.\d+\.html$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.comments\.\d+\.html$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932380;ARGS:/^json\.comments\.\d+\.html$/"
|
|
||||||
|
|
||||||
# labels, which the admin gives the members imported from a CSV file at
|
|
||||||
# /ghost/api/admin/members/upload/, skips the same eight rules, which refused
|
|
||||||
# "python3 import" and "powershell & .htaccess".
|
|
||||||
SecRule REQUEST_FILENAME "@streq /ghost/api/admin/members/upload/" \
|
|
||||||
"id:900372,phase:1,pass,nolog,chain"
|
|
||||||
SecRule &ARGS_GET:labels "@eq 0" \
|
|
||||||
"ctl:ruleRemoveTargetById=930120;ARGS:labels,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:labels,\
|
|
||||||
ctl:ruleRemoveTargetById=932125;ARGS:labels,\
|
|
||||||
ctl:ruleRemoveTargetById=932230;ARGS:labels,\
|
|
||||||
ctl:ruleRemoveTargetById=932235;ARGS:labels,\
|
|
||||||
ctl:ruleRemoveTargetById=932250;ARGS:labels,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:labels,\
|
|
||||||
ctl:ruleRemoveTargetById=932380;ARGS:labels"
|
|
||||||
|
|
||||||
# In ActivityPub, a note's or a reply's text and the description of its image,
|
|
||||||
# and an account's name, username and bio, skip the same eight rules. They
|
|
||||||
# refused "Trying python3 & powershell", the image's description "A terminal
|
|
||||||
# running ls -la on .htaccess with python3" and the bio "ls -la and .htaccess
|
|
||||||
# tips". Each field skips them on its own path:
|
|
||||||
# content and image.altText, posting a note at
|
|
||||||
# /.ghost/activitypub/v1/actions/note or a reply under
|
|
||||||
# /.ghost/activitypub/v1/actions/reply/
|
|
||||||
# name, username and bio, saving the account at /.ghost/activitypub/v1/account
|
|
||||||
SecRule REQUEST_FILENAME "@rx ^/\.ghost/activitypub/v1/actions/(?:note$|reply/)" \
|
|
||||||
"id:900373,phase:1,pass,nolog,chain"
|
|
||||||
SecRule &ARGS_GET:/^json\.(?:content|image\.alttext)$/ "@eq 0" \
|
|
||||||
"ctl:ruleRemoveTargetById=930120;ARGS:/^json\.(?:content|image\.alttext)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.(?:content|image\.alttext)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932125;ARGS:/^json\.(?:content|image\.alttext)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932230;ARGS:/^json\.(?:content|image\.alttext)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932235;ARGS:/^json\.(?:content|image\.alttext)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932250;ARGS:/^json\.(?:content|image\.alttext)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.(?:content|image\.alttext)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932380;ARGS:/^json\.(?:content|image\.alttext)$/"
|
|
||||||
SecRule REQUEST_FILENAME "@streq /.ghost/activitypub/v1/account" \
|
|
||||||
"id:900374,phase:1,pass,nolog,chain"
|
|
||||||
SecRule &ARGS_GET:/^json\.(?:name|username|bio)$/ "@eq 0" \
|
|
||||||
"ctl:ruleRemoveTargetById=930120;ARGS:/^json\.(?:name|username|bio)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.(?:name|username|bio)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932125;ARGS:/^json\.(?:name|username|bio)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932230;ARGS:/^json\.(?:name|username|bio)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932235;ARGS:/^json\.(?:name|username|bio)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932250;ARGS:/^json\.(?:name|username|bio)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.(?:name|username|bio)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932380;ARGS:/^json\.(?:name|username|bio)$/"
|
|
||||||
|
|
||||||
# A member's email address skips 930120, 932120 and 932260, which refused
|
# A member's email address skips 930120, 932120 and 932260, which refused
|
||||||
# erin.htaccess@example.com, dave.powershell@example.com and
|
# erin.htaccess@example.com, dave.powershell@example.com and
|
||||||
# python3.dev@example.com. These fields hold it:
|
# python3.dev@example.com. These fields hold it:
|
||||||
@@ -285,63 +132,6 @@ SecRule REQUEST_FILENAME "@rx ^/ghost/api/admin/users/[0-9a-f]{24}/$" \
|
|||||||
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.users\.\d+\.email$/,\
|
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.users\.\d+\.email$/,\
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.users\.\d+\.email$/"
|
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.users\.\d+\.email$/"
|
||||||
|
|
||||||
# The address of a staff invitation skips the same three rules. Each of these
|
|
||||||
# fields skips them only on its own path:
|
|
||||||
# invites.<n>.email, inviting a staff user at /ghost/api/admin/invites/
|
|
||||||
# email in the query, checking an invitation at
|
|
||||||
# /ghost/api/admin/authentication/invitation/
|
|
||||||
# invitation.<n>.email, accepting it at the same path
|
|
||||||
SecRule REQUEST_FILENAME "@streq /ghost/api/admin/invites/" \
|
|
||||||
"id:900375,phase:1,pass,nolog,chain"
|
|
||||||
SecRule &ARGS_GET:/^json\.invites\.\d+\.email$/ "@eq 0" \
|
|
||||||
"ctl:ruleRemoveTargetById=930120;ARGS:/^json\.invites\.\d+\.email$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.invites\.\d+\.email$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.invites\.\d+\.email$/"
|
|
||||||
SecRule REQUEST_FILENAME "@streq /ghost/api/admin/authentication/invitation/" \
|
|
||||||
"id:900376,phase:1,pass,nolog,\
|
|
||||||
ctl:ruleRemoveTargetById=930120;ARGS:email,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:email,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:email"
|
|
||||||
SecRule REQUEST_FILENAME "@streq /ghost/api/admin/authentication/invitation/" \
|
|
||||||
"id:900377,phase:1,pass,nolog,chain"
|
|
||||||
SecRule &ARGS_GET:/^json\.invitation\.\d+\.email$/ "@eq 0" \
|
|
||||||
"ctl:ruleRemoveTargetById=930120;ARGS:/^json\.invitation\.\d+\.email$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.invitation\.\d+\.email$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.invitation\.\d+\.email$/"
|
|
||||||
|
|
||||||
# In ActivityPub, an account's handle and its address skip 930120 and 932120.
|
|
||||||
# They refused the handles @erin.htaccess@example.com and
|
|
||||||
# @powershell@example.com, and the addresses
|
|
||||||
# https://example.com/users/erin.htaccess and
|
|
||||||
# https://example.com/users/powershell. A handle starts with an @, so 932260
|
|
||||||
# does not refuse @python3.dev@example.com. The domain of the site's handle
|
|
||||||
# skips the same three rules as an email address, which refused
|
|
||||||
# erin.htaccess.example.com, powershell.example.com and python3.example.com.
|
|
||||||
# Each of these fields skips them only on its own path:
|
|
||||||
# sourceHandle, an account that moves here, added at
|
|
||||||
# /.ghost/activitypub/v1/aliases
|
|
||||||
# actorUri, that account's address, when the admin removes it there
|
|
||||||
# targetHandle, the account this one moves to, at
|
|
||||||
# /.ghost/activitypub/v1/migration
|
|
||||||
# domain, saved at /.ghost/activitypub/v1/domain and checked at
|
|
||||||
# /.ghost/activitypub/v1/domain/validate
|
|
||||||
SecRule REQUEST_FILENAME "@streq /.ghost/activitypub/v1/aliases" \
|
|
||||||
"id:900379,phase:1,pass,nolog,chain"
|
|
||||||
SecRule &ARGS_GET:/^json\.(?:sourcehandle|actoruri)$/ "@eq 0" \
|
|
||||||
"ctl:ruleRemoveTargetById=930120;ARGS:/^json\.(?:sourcehandle|actoruri)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.(?:sourcehandle|actoruri)$/"
|
|
||||||
SecRule REQUEST_FILENAME "@streq /.ghost/activitypub/v1/migration" \
|
|
||||||
"id:900380,phase:1,pass,nolog,chain"
|
|
||||||
SecRule &ARGS_GET:/^json\.targethandle$/ "@eq 0" \
|
|
||||||
"ctl:ruleRemoveTargetById=930120;ARGS:/^json\.targethandle$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.targethandle$/"
|
|
||||||
SecRule REQUEST_FILENAME "@rx ^/\.ghost/activitypub/v1/domain(?:/validate)?$" \
|
|
||||||
"id:900381,phase:1,pass,nolog,chain"
|
|
||||||
SecRule &ARGS_GET:/^json\.domain$/ "@eq 0" \
|
|
||||||
"ctl:ruleRemoveTargetById=930120;ARGS:/^json\.domain$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.domain$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.domain$/"
|
|
||||||
|
|
||||||
# The articles skip these rules on the Admin API:
|
# The articles skip these rules on the Admin API:
|
||||||
# 930120: ls -la /var/www/html/.htaccess in a code block
|
# 930120: ls -la /var/www/html/.htaccess in a code block
|
||||||
# 932120: powershell -ExecutionPolicy Bypass -File build.ps1
|
# 932120: powershell -ExecutionPolicy Bypass -File build.ps1
|
||||||
@@ -388,10 +178,9 @@ SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/" \
|
|||||||
|
|
||||||
# Code injection, the HTML Ghost adds to the head and the foot of a page, and
|
# Code injection, the HTML Ghost adds to the head and the foot of a page, and
|
||||||
# the site's settings, which hold the site-wide code injection besides the
|
# the site's settings, which hold the site-wide code injection besides the
|
||||||
# title and the description, skip the eight rules that 900353 lists and these:
|
# title and the description, skip the rules of plain text above and these:
|
||||||
# 921130, 941100, 941160: <meta name="google-site-verification" ...>
|
# 921130, 941100, 941160: <meta name="google-site-verification" ...>
|
||||||
# 941270: <link rel="preconnect" href="https://fonts.example.com">
|
# 941270: <link rel="preconnect" href="https://fonts.example.com">
|
||||||
# 932235, one of the eight, refused the site's title "powershell & python3".
|
|
||||||
# These fields hold it, and skip the rules anywhere on the Admin API:
|
# These fields hold it, and skip the rules anywhere on the Admin API:
|
||||||
# posts.<n>.codeinjection_head and _foot, and the same fields of pages
|
# posts.<n>.codeinjection_head and _foot, and the same fields of pages
|
||||||
# settings.<n>.value, the value of a setting
|
# settings.<n>.value, the value of a setting
|
||||||
@@ -405,7 +194,6 @@ SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/" \
|
|||||||
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/,\
|
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/,\
|
||||||
ctl:ruleRemoveTargetById=932125;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/,\
|
ctl:ruleRemoveTargetById=932125;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/,\
|
||||||
ctl:ruleRemoveTargetById=932230;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/,\
|
ctl:ruleRemoveTargetById=932230;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/,\
|
||||||
ctl:ruleRemoveTargetById=932235;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932250;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/,\
|
ctl:ruleRemoveTargetById=932250;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/,\
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/,\
|
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/,\
|
||||||
ctl:ruleRemoveTargetById=932380;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/,\
|
ctl:ruleRemoveTargetById=932380;ARGS:/^json\.(?:(?:posts|pages)\.\d+\.codeinjection_(?:head|foot)|settings\.\d+\.value)$/,\
|
||||||
@@ -452,36 +240,6 @@ SecRule REQUEST_FILENAME "@rx ^/ghost/api/admin/users/[0-9a-f]{24}/$" \
|
|||||||
SecRule &ARGS_GET:/^json\.users\.\d+\.profile_image$/ "@eq 0" \
|
SecRule &ARGS_GET:/^json\.users\.\d+\.profile_image$/ "@eq 0" \
|
||||||
"ctl:ruleRemoveTargetById=930120;ARGS_NAMES:/^json\.users\.\d+\.profile_image$/"
|
"ctl:ruleRemoveTargetById=930120;ARGS_NAMES:/^json\.users\.\d+\.profile_image$/"
|
||||||
|
|
||||||
# A tag's code injection, tags.<n>.codeinjection_head and _foot under
|
|
||||||
# /ghost/api/admin/tags/, skips the rules that 900356 and 900366 switch off in
|
|
||||||
# the code injection of posts. A javascript: link is still refused in it.
|
|
||||||
SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/tags/" \
|
|
||||||
"id:900378,phase:1,pass,nolog,chain"
|
|
||||||
SecRule &ARGS_GET:/^json\.tags\.\d+\.codeinjection_(?:head|foot)$/ "@eq 0" \
|
|
||||||
"ctl:ruleRemoveTargetById=921130;ARGS:/^json\.tags\.\d+\.codeinjection_(?:head|foot)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=930120;ARGS:/^json\.tags\.\d+\.codeinjection_(?:head|foot)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.tags\.\d+\.codeinjection_(?:head|foot)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932125;ARGS:/^json\.tags\.\d+\.codeinjection_(?:head|foot)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932230;ARGS:/^json\.tags\.\d+\.codeinjection_(?:head|foot)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932235;ARGS:/^json\.tags\.\d+\.codeinjection_(?:head|foot)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932250;ARGS:/^json\.tags\.\d+\.codeinjection_(?:head|foot)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:/^json\.tags\.\d+\.codeinjection_(?:head|foot)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932380;ARGS:/^json\.tags\.\d+\.codeinjection_(?:head|foot)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=941100;ARGS:/^json\.tags\.\d+\.codeinjection_(?:head|foot)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=941110;ARGS:/^json\.tags\.\d+\.codeinjection_(?:head|foot)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=941160;ARGS:/^json\.tags\.\d+\.codeinjection_(?:head|foot)$/,\
|
|
||||||
ctl:ruleRemoveTargetById=941270;ARGS:/^json\.tags\.\d+\.codeinjection_(?:head|foot)$/"
|
|
||||||
|
|
||||||
# A tag's canonical URL, tags.<n>.canonical_url under /ghost/api/admin/tags/,
|
|
||||||
# skips the two rules that url skips:
|
|
||||||
# 930120: https://example.com/tag/.htaccess/
|
|
||||||
# 932120: https://example.com/tag/powershell/
|
|
||||||
SecRule REQUEST_FILENAME "@beginsWith /ghost/api/admin/tags/" \
|
|
||||||
"id:900382,phase:1,pass,nolog,chain"
|
|
||||||
SecRule &ARGS_GET:/^json\.tags\.\d+\.canonical_url$/ "@eq 0" \
|
|
||||||
"ctl:ruleRemoveTargetById=930120;ARGS:/^json\.tags\.\d+\.canonical_url$/,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:/^json\.tags\.\d+\.canonical_url$/"
|
|
||||||
|
|
||||||
# The passwords, which skip each rule that random passwords set off:
|
# The passwords, which skip each rule that random passwords set off:
|
||||||
# password, signing in at /ghost/api/admin/session/
|
# password, signing in at /ghost/api/admin/session/
|
||||||
# setup.<n>.password, creating the first staff user
|
# setup.<n>.password, creating the first staff user
|
||||||
|
|||||||
@@ -28,19 +28,6 @@ const (
|
|||||||
ghostUser = "/ghost/api/admin/users/6af4570fd281b00001a59891/"
|
ghostUser = "/ghost/api/admin/users/6af4570fd281b00001a59891/"
|
||||||
ghostMember = "/members/api/member/"
|
ghostMember = "/members/api/member/"
|
||||||
ghostMagicLink = "/members/api/send-magic-link/"
|
ghostMagicLink = "/members/api/send-magic-link/"
|
||||||
ghostInvites = "/ghost/api/admin/invites/"
|
|
||||||
ghostInvitation = "/ghost/api/admin/authentication/invitation/"
|
|
||||||
ghostTiers = "/ghost/api/admin/tiers/6af45710d281b00001a598a3/"
|
|
||||||
ghostOffers = "/ghost/api/admin/offers/"
|
|
||||||
ghostRecommended = "/ghost/api/admin/recommendations/"
|
|
||||||
ghostTags = "/ghost/api/admin/tags/"
|
|
||||||
ghostImport = "/ghost/api/admin/members/upload/"
|
|
||||||
ghostComments = "/members/api/comments/"
|
|
||||||
ghostNote = "/.ghost/activitypub/v1/actions/note"
|
|
||||||
ghostAccount = "/.ghost/activitypub/v1/account"
|
|
||||||
ghostAliases = "/.ghost/activitypub/v1/aliases"
|
|
||||||
ghostMigration = "/.ghost/activitypub/v1/migration"
|
|
||||||
ghostDomain = "/.ghost/activitypub/v1/domain"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGhostFixesApplyOnlyToAHostnameThatServesGhost(t *testing.T) {
|
func TestGhostFixesApplyOnlyToAHostnameThatServesGhost(t *testing.T) {
|
||||||
@@ -77,7 +64,7 @@ func (f ghostField) with(t *testing.T, value string) string {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ghostTextFields hold plain text and skip the eight rules that 900353 lists.
|
// ghostTextFields hold plain text and skip the rules of 900353 and 900359.
|
||||||
//
|
//
|
||||||
//nolint:gochecknoglobals // a constant cannot be a list
|
//nolint:gochecknoglobals // a constant cannot be a list
|
||||||
var ghostTextFields = []ghostField{
|
var ghostTextFields = []ghostField{
|
||||||
@@ -129,111 +116,8 @@ var ghostTextFields = []ghostField{
|
|||||||
`{"snippets":[{"name":"VALUE"}]}`,
|
`{"snippets":[{"name":"VALUE"}]}`,
|
||||||
},
|
},
|
||||||
{http.MethodPut, ghostMember, ghostMemberName},
|
{http.MethodPut, ghostMember, ghostMemberName},
|
||||||
|
|
||||||
// A staff user's profile, the first staff user, an invitation accepted.
|
|
||||||
{http.MethodPut, ghostUser, `{"users":[{"name":"VALUE"}]}`},
|
|
||||||
{http.MethodPut, ghostUser, `{"users":[{"slug":"VALUE"}]}`},
|
|
||||||
{http.MethodPut, ghostUser, `{"users":[{"bio":"VALUE"}]}`},
|
|
||||||
{http.MethodPut, ghostUser, `{"users":[{"location":"VALUE"}]}`},
|
|
||||||
{http.MethodPut, ghostUser, `{"users":[{"website":"VALUE"}]}`},
|
|
||||||
{http.MethodPost, ghostSetup, `{"setup":[{"blogTitle":"VALUE"}]}`},
|
|
||||||
{http.MethodPost, ghostSetup, `{"setup":[{"name":"VALUE"}]}`},
|
|
||||||
{http.MethodPost, ghostInvitation, `{"invitation":[{"name":"VALUE"}]}`},
|
|
||||||
|
|
||||||
// Tiers, offers, recommendations, integrations and webhooks.
|
|
||||||
{http.MethodPut, ghostTiers, `{"tiers":[{"name":"VALUE"}]}`},
|
|
||||||
{http.MethodPut, ghostTiers, `{"tiers":[{"description":"VALUE"}]}`},
|
|
||||||
{
|
|
||||||
http.MethodPut,
|
|
||||||
ghostTiers,
|
|
||||||
`{"tiers":[{"benefits":["Tips","VALUE"]}]}`,
|
|
||||||
},
|
|
||||||
{http.MethodPost, ghostOffers, `{"offers":[{"name":"VALUE"}]}`},
|
|
||||||
{http.MethodPost, ghostOffers, `{"offers":[{"code":"VALUE"}]}`},
|
|
||||||
{http.MethodPost, ghostOffers, `{"offers":[{"display_title":"VALUE"}]}`},
|
|
||||||
{
|
|
||||||
http.MethodPost,
|
|
||||||
ghostOffers,
|
|
||||||
`{"offers":[{"display_description":"VALUE"}]}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
http.MethodPost,
|
|
||||||
ghostRecommended,
|
|
||||||
`{"recommendations":[{"title":"VALUE"}]}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
http.MethodPost,
|
|
||||||
ghostRecommended,
|
|
||||||
`{"recommendations":[{"description":"VALUE"}]}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
http.MethodPost,
|
|
||||||
ghostRecommended,
|
|
||||||
`{"recommendations":[{"excerpt":"VALUE"}]}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
http.MethodPost,
|
|
||||||
"/ghost/api/admin/integrations/",
|
|
||||||
`{"integrations":[{"name":"VALUE","description":"VALUE"}]}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
http.MethodPost,
|
|
||||||
"/ghost/api/admin/webhooks/",
|
|
||||||
`{"webhooks":[{"event":"post.published","name":"VALUE"}]}`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// A feature image's alt text and caption.
|
|
||||||
{http.MethodPut, ghostPost, `{"posts":[{"feature_image_alt":"VALUE"}]}`},
|
|
||||||
{
|
|
||||||
http.MethodPut,
|
|
||||||
ghostPost,
|
|
||||||
`{"posts":[{"feature_image_caption":` +
|
|
||||||
`"Photo: <a href=\"https://example.com/\">VALUE</a>"}]}`,
|
|
||||||
},
|
|
||||||
{http.MethodPut, ghostPage, `{"pages":[{"feature_image_alt":"VALUE"}]}`},
|
|
||||||
|
|
||||||
// A member's comment, written and edited.
|
|
||||||
ghostNewComment,
|
|
||||||
{
|
|
||||||
http.MethodPut,
|
|
||||||
ghostComments + "6af5426ae56ecb0001938894/",
|
|
||||||
`{"comments":[{"id":"6af5426ae56ecb0001938894","html":"<p>VALUE</p>"}]}`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// ActivityPub: a note, a reply, the description of their image, and the
|
|
||||||
// account.
|
|
||||||
{http.MethodPost, ghostNote, `{"content":"VALUE"}`},
|
|
||||||
{http.MethodPost, ghostReply, `{"content":"VALUE"}`},
|
|
||||||
{http.MethodPost, ghostNote, ghostNoteImage},
|
|
||||||
{http.MethodPost, ghostReply, ghostNoteImage},
|
|
||||||
{http.MethodPut, ghostAccount, `{"name":"VALUE","username":"alice"}`},
|
|
||||||
{http.MethodPut, ghostAccount, `{"username":"VALUE"}`},
|
|
||||||
{http.MethodPut, ghostAccount, `{"bio":"VALUE"}`},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ghostNewComment is a member's comment on a post, with VALUE in a paragraph.
|
|
||||||
//
|
|
||||||
//nolint:gochecknoglobals // a constant cannot be a struct
|
|
||||||
var ghostNewComment = ghostField{
|
|
||||||
http.MethodPost,
|
|
||||||
ghostComments,
|
|
||||||
`{"comments":[{"post_id":"6af458a5d281b00001a59969","html":"<p>VALUE</p>"}]}`,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ghostComment is the HTML of an ordinary comment, with a link.
|
|
||||||
const ghostComment = `Try <strong>python3</strong>, then ` +
|
|
||||||
`<a href="https://example.com/ls-la" rel="ugc">ls -la</a>.`
|
|
||||||
|
|
||||||
// ghostNoteImage is the body of a note or a reply in ActivityPub with an
|
|
||||||
// image, with VALUE as the image's description.
|
|
||||||
const ghostNoteImage = `{"content":"My desk","image":{"url":` +
|
|
||||||
`"https://example.com/content/images/desk.png","altText":"VALUE"}}`
|
|
||||||
|
|
||||||
// ghostReply is the path that posts a reply to a note in ActivityPub.
|
|
||||||
const ghostReply = "/.ghost/activitypub/v1/actions/reply/" +
|
|
||||||
"https%3A%2F%2Fexample.com%2F.ghost%2Factivitypub%2Fnote%2F" +
|
|
||||||
"0d6e3b4c-2a1f-4b5e-9c8d-7e6f5a4b3c2d"
|
|
||||||
|
|
||||||
// ghostMemberName is the body that saves a member's name at /members/api/.
|
// ghostMemberName is the body that saves a member's name at /members/api/.
|
||||||
const ghostMemberName = `{"name":"VALUE"}`
|
const ghostMemberName = `{"name":"VALUE"}`
|
||||||
|
|
||||||
@@ -251,8 +135,6 @@ var ghostAddressFields = []ghostField{
|
|||||||
{http.MethodPost, ghostMembers, `{"members":[{"email":"VALUE"}]}`},
|
{http.MethodPost, ghostMembers, `{"members":[{"email":"VALUE"}]}`},
|
||||||
{http.MethodPost, ghostSession, `{"username":"VALUE"}`},
|
{http.MethodPost, ghostSession, `{"username":"VALUE"}`},
|
||||||
{http.MethodPost, ghostSetup, `{"setup":[{"email":"VALUE"}]}`},
|
{http.MethodPost, ghostSetup, `{"setup":[{"email":"VALUE"}]}`},
|
||||||
{http.MethodPost, ghostInvites, `{"invites":[{"email":"VALUE"}]}`},
|
|
||||||
{http.MethodPost, ghostInvitation, `{"invitation":[{"email":"VALUE"}]}`},
|
|
||||||
{
|
{
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
ghostPasswordReset,
|
ghostPasswordReset,
|
||||||
@@ -261,41 +143,14 @@ var ghostAddressFields = []ghostField{
|
|||||||
{http.MethodPut, ghostUser, `{"users":[{"email":"VALUE"}]}`},
|
{http.MethodPut, ghostUser, `{"users":[{"email":"VALUE"}]}`},
|
||||||
}
|
}
|
||||||
|
|
||||||
// ghostHandleFields hold an account's handle in ActivityPub, which the admin
|
// ghostCanonicalURLFields hold the canonical URL of a post or a page, and skip
|
||||||
// sends as an @ followed by an address, with no spaces. They skip 930120 and
|
// 930120 and 932120, as url does.
|
||||||
// 932120.
|
|
||||||
//
|
//
|
||||||
//nolint:gochecknoglobals // a constant cannot be a list
|
//nolint:gochecknoglobals // a constant cannot be a list
|
||||||
var ghostHandleFields = []ghostField{
|
var ghostCanonicalURLFields = []ghostField{
|
||||||
{http.MethodPost, ghostAliases, `{"sourceHandle":"@VALUE"}`},
|
|
||||||
{http.MethodPost, ghostMigration, `{"targetHandle":"@VALUE"}`},
|
|
||||||
}
|
|
||||||
|
|
||||||
// ghostDomainFields hold the domain of the site's handle in ActivityPub and
|
|
||||||
// skip 930120, 932120 and 932260.
|
|
||||||
//
|
|
||||||
//nolint:gochecknoglobals // a constant cannot be a list
|
|
||||||
var ghostDomainFields = []ghostField{
|
|
||||||
{http.MethodPut, ghostDomain, `{"domain":"VALUE"}`},
|
|
||||||
{http.MethodPost, ghostDomain + "/validate", `{"domain":"VALUE"}`},
|
|
||||||
}
|
|
||||||
|
|
||||||
// ghostURLFields hold the canonical URL of a post, a page or a tag, or the
|
|
||||||
// address of an account removed from those that move here in ActivityPub.
|
|
||||||
// They skip 930120 and 932120, as url does.
|
|
||||||
//
|
|
||||||
//nolint:gochecknoglobals // a constant cannot be a list
|
|
||||||
var ghostURLFields = []ghostField{
|
|
||||||
{http.MethodPut, ghostPost, `{"posts":[{"canonical_url":"VALUE"}]}`},
|
{http.MethodPut, ghostPost, `{"posts":[{"canonical_url":"VALUE"}]}`},
|
||||||
{http.MethodPost, ghostPosts, `{"posts":[{"canonical_url":"VALUE"}]}`},
|
{http.MethodPost, ghostPosts, `{"posts":[{"canonical_url":"VALUE"}]}`},
|
||||||
{http.MethodPut, ghostPage, `{"pages":[{"canonical_url":"VALUE"}]}`},
|
{http.MethodPut, ghostPage, `{"pages":[{"canonical_url":"VALUE"}]}`},
|
||||||
{http.MethodPost, ghostTags, `{"tags":[{"canonical_url":"VALUE"}]}`},
|
|
||||||
{
|
|
||||||
http.MethodPut,
|
|
||||||
ghostTags + "6af45d4ed281b00001a599a8/",
|
|
||||||
`{"tags":[{"canonical_url":"VALUE"}]}`,
|
|
||||||
},
|
|
||||||
{http.MethodDelete, ghostAliases, `{"actorUri":"VALUE"}`},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ghostPostFields hold a post or a page, skip the rules of 900355, and take a
|
// ghostPostFields hold a post or a page, skip the rules of 900355, and take a
|
||||||
@@ -338,8 +193,8 @@ var ghostArticleFields = slices.Concat(
|
|||||||
ghostSnippetAndNewsletterFooterFields,
|
ghostSnippetAndNewsletterFooterFields,
|
||||||
)
|
)
|
||||||
|
|
||||||
// ghostCodeFields hold code injection or a setting, skip the rules of 900356
|
// ghostCodeFields hold code injection or a setting, skip the rules of 900356,
|
||||||
// and 900378, and take a <script> element by 900366 and 900378.
|
// and take a <script> element by 900366.
|
||||||
//
|
//
|
||||||
//nolint:gochecknoglobals // a constant cannot be a list
|
//nolint:gochecknoglobals // a constant cannot be a list
|
||||||
var ghostCodeFields = []ghostField{
|
var ghostCodeFields = []ghostField{
|
||||||
@@ -358,16 +213,6 @@ var ghostCodeFields = []ghostField{
|
|||||||
`{"settings":[{"key":"title","value":"Blog"},` +
|
`{"settings":[{"key":"title","value":"Blog"},` +
|
||||||
`{"key":"codeinjection_head","value":"VALUE"}]}`,
|
`{"key":"codeinjection_head","value":"VALUE"}]}`,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
http.MethodPost,
|
|
||||||
ghostTags,
|
|
||||||
`{"tags":[{"codeinjection_head":"VALUE"}]}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
http.MethodPut,
|
|
||||||
ghostTags + "6af45d4ed281b00001a599a8/",
|
|
||||||
`{"tags":[{"codeinjection_foot":"VALUE"}]}`,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The query fields that skip some rules, each ending in "=".
|
// The query fields that skip some rules, each ending in "=".
|
||||||
@@ -376,42 +221,17 @@ const (
|
|||||||
ghostContentFilter = "/ghost/api/content/posts/?key=c695aa1f5cdfa62f7b677c3c83&filter="
|
ghostContentFilter = "/ghost/api/content/posts/?key=c695aa1f5cdfa62f7b677c3c83&filter="
|
||||||
ghostSearch = ghostMembers + "?limit=100&search="
|
ghostSearch = ghostMembers + "?limit=100&search="
|
||||||
ghostEmbed = "/ghost/api/admin/oembed/?type=bookmark&url="
|
ghostEmbed = "/ghost/api/admin/oembed/?type=bookmark&url="
|
||||||
ghostStats = "/ghost/api/admin/stats/top-content/?date_from=2026-10-13"
|
|
||||||
ghostPathname = ghostStats + "&pathname="
|
|
||||||
ghostSource = ghostStats + "&source="
|
|
||||||
ghostCampaign = ghostStats + "&utm_campaign="
|
|
||||||
ghostSegment = ghostPost + "?newsletter=default-newsletter&email_segment="
|
|
||||||
ghostAccountSearch = "/.ghost/activitypub/v1/actions/search?query="
|
|
||||||
ghostInvitedEmail = ghostInvitation + "?email="
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ghostAnalyticsFilters are source and the utm_ fields, which filter the
|
|
||||||
// analytics, each ending in "=".
|
|
||||||
//
|
|
||||||
//nolint:gochecknoglobals // a constant cannot be a list
|
|
||||||
var ghostAnalyticsFilters = []string{
|
|
||||||
ghostSource,
|
|
||||||
ghostStats + "&utm_source=",
|
|
||||||
ghostStats + "&utm_medium=",
|
|
||||||
ghostCampaign,
|
|
||||||
ghostStats + "&utm_content=",
|
|
||||||
ghostStats + "&utm_term=",
|
|
||||||
}
|
|
||||||
|
|
||||||
// ghostQueryFields are the query fields that skip some rules.
|
// ghostQueryFields are the query fields that skip some rules.
|
||||||
//
|
//
|
||||||
//nolint:gochecknoglobals // a constant cannot be a list
|
//nolint:gochecknoglobals // a constant cannot be a list
|
||||||
var ghostQueryFields = []string{
|
var ghostQueryFields = []string{
|
||||||
ghostAdminFilter, ghostContentFilter, ghostSearch, ghostEmbed,
|
ghostAdminFilter, ghostContentFilter, ghostSearch, ghostEmbed,
|
||||||
ghostPathname, ghostSource, ghostCampaign, ghostSegment,
|
|
||||||
ghostAccountSearch, ghostInvitedEmail,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ghostCommandNames joins two command names, as an ordinary title can.
|
// ghostURLs are addresses 930120 and 932120 refused in url and in a canonical
|
||||||
const ghostCommandNames = "powershell & python3"
|
// URL.
|
||||||
|
|
||||||
// ghostURLs are addresses 930120 and 932120 refused in url and in
|
|
||||||
// ghostURLFields.
|
|
||||||
//
|
//
|
||||||
//nolint:gochecknoglobals // a constant cannot be a list
|
//nolint:gochecknoglobals // a constant cannot be a list
|
||||||
var ghostURLs = []string{
|
var ghostURLs = []string{
|
||||||
@@ -431,15 +251,6 @@ var ghostAddresses = []string{
|
|||||||
"dave.powershell@example.com",
|
"dave.powershell@example.com",
|
||||||
}
|
}
|
||||||
|
|
||||||
// ghostDomains are domains 930120, 932120 and 932260 refused.
|
|
||||||
//
|
|
||||||
//nolint:gochecknoglobals // a constant cannot be a list
|
|
||||||
var ghostDomains = []string{
|
|
||||||
"erin.htaccess.example.com",
|
|
||||||
"python3.example.com",
|
|
||||||
"powershell.example.com",
|
|
||||||
}
|
|
||||||
|
|
||||||
// ghostArticle is an ordinary article with a code block, an HTML card and an
|
// ghostArticle is an ordinary article with a code block, an HTML card and an
|
||||||
// embed, which each of the rules of 900355 refused.
|
// embed, which each of the rules of 900355 refused.
|
||||||
const ghostArticle = `<h2>Tidy .htaccess with python3</h2>` +
|
const ghostArticle = `<h2>Tidy .htaccess with python3</h2>` +
|
||||||
@@ -530,23 +341,17 @@ func TestGhostOrdinaryValuesPassInTheExcludedFields(t *testing.T) {
|
|||||||
for _, address := range ghostURLs {
|
for _, address := range ghostURLs {
|
||||||
wantGhostQuery(t, ghostEmbed+url.QueryEscape(address), false)
|
wantGhostQuery(t, ghostEmbed+url.QueryEscape(address), false)
|
||||||
|
|
||||||
for _, f := range ghostURLFields {
|
for _, f := range ghostCanonicalURLFields {
|
||||||
wantGhostField(t, f, address, false)
|
wantGhostField(t, f, address, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, address := range ghostAddresses {
|
for _, address := range ghostAddresses {
|
||||||
for _, f := range slices.Concat(ghostAddressFields, ghostHandleFields) {
|
for _, f := range ghostAddressFields {
|
||||||
wantGhostField(t, f, address, false)
|
wantGhostField(t, f, address, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, domain := range ghostDomains {
|
|
||||||
for _, f := range ghostDomainFields {
|
|
||||||
wantGhostField(t, f, domain, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range ghostArticleFields {
|
for _, f := range ghostArticleFields {
|
||||||
wantGhostField(t, f, ghostArticle, false)
|
wantGhostField(t, f, ghostArticle, false)
|
||||||
}
|
}
|
||||||
@@ -555,14 +360,6 @@ func TestGhostOrdinaryValuesPassInTheExcludedFields(t *testing.T) {
|
|||||||
wantGhostField(t, f, ghostCodeInjection, false)
|
wantGhostField(t, f, ghostCodeInjection, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
wantGhostField(t, ghostNewComment, ghostComment, false)
|
|
||||||
wantGhostField(
|
|
||||||
t,
|
|
||||||
ghostField{http.MethodPost, ghostNote, ghostNoteImage},
|
|
||||||
"A terminal running ls -la on .htaccess with python3",
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
|
|
||||||
wantGhostQuery(t, "/ghost/api/admin/posts/?filter="+
|
wantGhostQuery(t, "/ghost/api/admin/posts/?filter="+
|
||||||
url.QueryEscape(
|
url.QueryEscape(
|
||||||
"tag:powershell+status:[draft,scheduled,published,sent]",
|
"tag:powershell+status:[draft,scheduled,published,sent]",
|
||||||
@@ -596,8 +393,7 @@ func TestGhostScriptsPassInPostsAndCodeInjection(t *testing.T) {
|
|||||||
wantGhostField(t, f, ghostWidget, false)
|
wantGhostField(t, f, ghostWidget, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Site code injection, saved in settings, and a post's, a page's or a
|
// Site code injection, saved in settings, and a post's or a page's own.
|
||||||
// tag's own.
|
|
||||||
for _, f := range ghostCodeFields {
|
for _, f := range ghostCodeFields {
|
||||||
wantGhostField(t, f, ghostAnalytics, false)
|
wantGhostField(t, f, ghostAnalytics, false)
|
||||||
}
|
}
|
||||||
@@ -616,8 +412,7 @@ func TestGhostAttacksAreRefusedInTheExcludedFields(t *testing.T) {
|
|||||||
|
|
||||||
for _, attack := range ghostAttacks {
|
for _, attack := range ghostAttacks {
|
||||||
for _, f := range slices.Concat(ghostTextFields, ghostAddressFields,
|
for _, f := range slices.Concat(ghostTextFields, ghostAddressFields,
|
||||||
ghostHandleFields, ghostDomainFields, ghostURLFields,
|
ghostCanonicalURLFields, ghostArticleFields, ghostCodeFields) {
|
||||||
ghostArticleFields, ghostCodeFields) {
|
|
||||||
wantGhostField(t, f, attack, true)
|
wantGhostField(t, f, attack, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -629,8 +424,7 @@ func TestGhostAttacksAreRefusedInTheExcludedFields(t *testing.T) {
|
|||||||
// A script injection stays refused in every field that does not take a
|
// A script injection stays refused in every field that does not take a
|
||||||
// <script> element.
|
// <script> element.
|
||||||
for _, f := range slices.Concat(ghostTextFields, ghostAddressFields,
|
for _, f := range slices.Concat(ghostTextFields, ghostAddressFields,
|
||||||
ghostHandleFields, ghostDomainFields, ghostURLFields,
|
ghostCanonicalURLFields, ghostSnippetAndNewsletterFooterFields) {
|
||||||
ghostSnippetAndNewsletterFooterFields) {
|
|
||||||
wantGhostField(t, f, scriptTag, true)
|
wantGhostField(t, f, scriptTag, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,19 +432,15 @@ func TestGhostAttacksAreRefusedInTheExcludedFields(t *testing.T) {
|
|||||||
wantGhostQuery(t, target+url.QueryEscape(scriptTag), true)
|
wantGhostQuery(t, target+url.QueryEscape(scriptTag), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A command after a separator stays refused wherever 932235 does not
|
// A command after a separator stays refused wherever its rules do not
|
||||||
// misfire: in the addresses, handles, the domain, ghostURLFields, search,
|
// misfire: in the query fields, the plain text, the addresses and the
|
||||||
// url, pathname, email_segment and email. The other fields skip 932235,
|
// canonical URLs.
|
||||||
// which refuses "powershell & python3" as it refuses this.
|
for _, f := range slices.Concat(ghostTextFields, ghostAddressFields,
|
||||||
for _, f := range slices.Concat(ghostAddressFields, ghostHandleFields,
|
ghostCanonicalURLFields) {
|
||||||
ghostDomainFields, ghostURLFields) {
|
|
||||||
wantGhostField(t, f, commandAfterASeparator, true)
|
wantGhostField(t, f, commandAfterASeparator, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, target := range []string{
|
for _, target := range ghostQueryFields {
|
||||||
ghostSearch, ghostEmbed, ghostPathname, ghostSegment,
|
|
||||||
ghostInvitedEmail,
|
|
||||||
} {
|
|
||||||
wantGhostQuery(
|
wantGhostQuery(
|
||||||
t,
|
t,
|
||||||
target+url.QueryEscape(commandAfterASeparator),
|
target+url.QueryEscape(commandAfterASeparator),
|
||||||
@@ -658,15 +448,6 @@ func TestGhostAttacksAreRefusedInTheExcludedFields(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A comment keeps the rules for scripts and HTML.
|
|
||||||
wantGhostField(t, ghostNewComment, `<img src=x onerror=alert(1)>`, true)
|
|
||||||
wantGhostField(
|
|
||||||
t,
|
|
||||||
ghostNewComment,
|
|
||||||
`<a href="javascript:alert(1)">x</a>`,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
|
|
||||||
// The articles and code injection carry HTML on purpose, but a
|
// The articles and code injection carry HTML on purpose, but a
|
||||||
// javascript: link is still refused there, and a script element in
|
// javascript: link is still refused there, and a script element in
|
||||||
// snippets and a newsletter's footer.
|
// snippets and a newsletter's footer.
|
||||||
@@ -678,138 +459,12 @@ func TestGhostAttacksAreRefusedInTheExcludedFields(t *testing.T) {
|
|||||||
wantGhostField(t, f, `<p>Hi</p><script src="/x.js"></script>`, true)
|
wantGhostField(t, f, `<p>Hi</p><script src="/x.js"></script>`, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An address and the domain keep 932250, which refuses a listing of
|
// An address keeps 932250, which refuses a listing of files.
|
||||||
// files. A handle keeps it too, but its @ comes before any command.
|
for _, f := range ghostAddressFields {
|
||||||
for _, f := range slices.Concat(ghostAddressFields, ghostDomainFields) {
|
|
||||||
wantGhostField(t, f, "ls -la /root", true)
|
wantGhostField(t, f, "ls -la /root", true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGhostTwoCommandNamesPass(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
// The site's title, saved through settings.
|
|
||||||
siteTitle := ghostField{
|
|
||||||
http.MethodPut,
|
|
||||||
ghostSettings,
|
|
||||||
`{"settings":[{"key":"title","value":"VALUE"}]}`,
|
|
||||||
}
|
|
||||||
wantGhostField(t, siteTitle, ghostCommandNames, false)
|
|
||||||
|
|
||||||
// Values that join two command names, as a person types them.
|
|
||||||
for _, typed := range []string{
|
|
||||||
ghostCommandNames, "python3 & powershell", "powershell & ls -la",
|
|
||||||
"ls -la & python3",
|
|
||||||
} {
|
|
||||||
for _, f := range slices.Concat(ghostTextFields, ghostCodeFields) {
|
|
||||||
wantGhostField(t, f, typed, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
wantGhostImportLabel(t, ghostImport, typed, false)
|
|
||||||
|
|
||||||
for _, target := range slices.Concat(
|
|
||||||
ghostAnalyticsFilters,
|
|
||||||
[]string{ghostAccountSearch},
|
|
||||||
) {
|
|
||||||
wantGhostQuery(t, target+url.QueryEscape(typed), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter as the admin's post picker writes it, after a person types
|
|
||||||
// such a title.
|
|
||||||
filter := "status:published+title:~'" + typed + "'"
|
|
||||||
wantGhostQuery(t, ghostAdminFilter+url.QueryEscape(filter), false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestGhostOrdinaryQueriesPass checks the query fields of the analytics
|
|
||||||
// pages, email_segment, the search of ActivityPub and the check of an
|
|
||||||
// invitation.
|
|
||||||
func TestGhostOrdinaryQueriesPass(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, encoded := range hardValues {
|
|
||||||
value, _ := url.QueryUnescape(encoded)
|
|
||||||
text := url.QueryEscape("Notes on " + value + " & tidy files")
|
|
||||||
|
|
||||||
for _, target := range ghostAnalyticsFilters {
|
|
||||||
wantGhostQuery(t, target+encoded, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
wantGhostQuery(t, ghostCampaign+text, false)
|
|
||||||
wantGhostQuery(t, ghostAccountSearch+text, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// pathname, the page the analytics show: a post and two tag pages.
|
|
||||||
for _, path := range []string{
|
|
||||||
"/tag/proc/", "/python3-and-powershell/", "/tag/python3/",
|
|
||||||
} {
|
|
||||||
wantGhostQuery(t, ghostPathname+url.QueryEscape(path), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
wantGhostQuery(
|
|
||||||
t,
|
|
||||||
ghostCampaign+url.QueryEscape(ghostCommandNames),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
wantGhostQuery(
|
|
||||||
t,
|
|
||||||
ghostAccountSearch+"%40powershell%40example.com",
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, segment := range []string{
|
|
||||||
"label:powershell-users", "label:python3", "label:ls-la",
|
|
||||||
"label:htaccess", "status:free,tier:powershell-club",
|
|
||||||
} {
|
|
||||||
wantGhostQuery(t, ghostSegment+url.QueryEscape(segment), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, address := range ghostAddresses {
|
|
||||||
wantGhostQuery(t, ghostInvitedEmail+url.QueryEscape(address), false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// wantGhostImportLabel checks that the Core Rule Set, reading bodies,
|
|
||||||
// refuses label, sent as labels in a CSV import to target, or lets it
|
|
||||||
// through.
|
|
||||||
func wantGhostImportLabel(
|
|
||||||
t *testing.T, target, label string, refused bool,
|
|
||||||
) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
r := request{http.MethodPost, target, []string{multipart}}
|
|
||||||
body := field("labels", label) + field("mapping[email]", "email") + end
|
|
||||||
|
|
||||||
got, _ := inspectBody(t, readingBodies(t), r, body)
|
|
||||||
if refused && got.Score < ghostThreshold {
|
|
||||||
t.Errorf("labels=%q at %s let through: %+v", label, target, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !refused && got.Score >= ghostThreshold {
|
|
||||||
t.Errorf("labels=%q at %s refused: %+v", label, target, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGhostImportLabelsPass(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, encoded := range hardValues {
|
|
||||||
value, _ := url.QueryUnescape(encoded)
|
|
||||||
wantGhostImportLabel(t, ghostImport, value+" import", false)
|
|
||||||
}
|
|
||||||
|
|
||||||
wantGhostImportLabel(t, ghostImport, ghostCommandNames, false)
|
|
||||||
|
|
||||||
for _, attack := range ghostAttacks {
|
|
||||||
wantGhostImportLabel(t, ghostImport, attack, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// labels keeps its rules on other paths, and when the query has a field
|
|
||||||
// of the same name.
|
|
||||||
wantGhostImportLabel(t, ghostMembers, "python3 import", true)
|
|
||||||
wantGhostImportLabel(t, ghostImport+"?labels=x", "python3 import", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGhostExclusionsAreNarrow(t *testing.T) {
|
func TestGhostExclusionsAreNarrow(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -847,6 +502,7 @@ func TestGhostExclusionsAreNarrow(t *testing.T) {
|
|||||||
`{"settings":[{"value":"<meta name=x content=y>"}]}`,
|
`{"settings":[{"value":"<meta name=x content=y>"}]}`,
|
||||||
},
|
},
|
||||||
{"/ghost/api/admin/members/", `{"members":[{"geolocation":"python3"}]}`},
|
{"/ghost/api/admin/members/", `{"members":[{"geolocation":"python3"}]}`},
|
||||||
|
{"/ghost/api/admin/invites/", `{"invites":[{"email":"python3.dev@example.com"}]}`},
|
||||||
{ghostPosts, email},
|
{ghostPosts, email},
|
||||||
{ghostMembers, email},
|
{ghostMembers, email},
|
||||||
{ghostMember, `{"members":[{"email":"python3.dev@example.com"}]}`},
|
{ghostMember, `{"members":[{"email":"python3.dev@example.com"}]}`},
|
||||||
@@ -904,7 +560,6 @@ func TestGhostScriptExclusionIsNarrow(t *testing.T) {
|
|||||||
script = `<script defer src=\"/script.js\"></script>`
|
script = `<script defer src=\"/script.js\"></script>`
|
||||||
scriptInPost = `{"posts":[{"lexical":"` + script + `"}]}`
|
scriptInPost = `{"posts":[{"lexical":"` + script + `"}]}`
|
||||||
scriptInSettings = `{"settings":[{"value":"` + script + `"}]}`
|
scriptInSettings = `{"settings":[{"value":"` + script + `"}]}`
|
||||||
scriptInTag = `{"tags":[{"codeinjection_head":"` + script + `"}]}`
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, refused := range []struct{ target, body string }{
|
for _, refused := range []struct{ target, body string }{
|
||||||
@@ -914,8 +569,6 @@ func TestGhostScriptExclusionIsNarrow(t *testing.T) {
|
|||||||
{"/ghost/api/admin/users/me/", `{"users":[{"bio":"` + script + `"}]}`},
|
{"/ghost/api/admin/users/me/", `{"users":[{"bio":"` + script + `"}]}`},
|
||||||
{"/api/settings/", scriptInSettings},
|
{"/api/settings/", scriptInSettings},
|
||||||
{ghostSettings + "?json.settings.0.value=x", scriptInSettings},
|
{ghostSettings + "?json.settings.0.value=x", scriptInSettings},
|
||||||
{ghostPosts, scriptInTag},
|
|
||||||
{ghostTags + "?json.tags.0.codeinjection_head=x", scriptInTag},
|
|
||||||
} {
|
} {
|
||||||
r := request{http.MethodPut, refused.target, []string{jsonBody}}
|
r := request{http.MethodPut, refused.target, []string{jsonBody}}
|
||||||
|
|
||||||
@@ -937,8 +590,7 @@ func TestGhostQueryExclusionsAreNarrow(t *testing.T) {
|
|||||||
|
|
||||||
// The query fields keep their rules off Ghost's paths, and in other
|
// The query fields keep their rules off Ghost's paths, and in other
|
||||||
// query fields there. search skips its rules only on the Admin API, and
|
// query fields there. search skips its rules only on the Admin API, and
|
||||||
// url only on the editor's lookup of embeds. pathname, email_segment,
|
// url only on the editor's lookup of embeds.
|
||||||
// query and email skip theirs only on their own paths.
|
|
||||||
for _, target := range []string{
|
for _, target := range []string{
|
||||||
"/api/posts/?filter=powershell",
|
"/api/posts/?filter=powershell",
|
||||||
"/ghost/posts/?filter=powershell",
|
"/ghost/posts/?filter=powershell",
|
||||||
@@ -947,79 +599,11 @@ func TestGhostQueryExclusionsAreNarrow(t *testing.T) {
|
|||||||
"/members/api/member/?search=python3",
|
"/members/api/member/?search=python3",
|
||||||
"/ghost/api/admin/posts/?url=powershell",
|
"/ghost/api/admin/posts/?url=powershell",
|
||||||
"/oembed/?url=powershell",
|
"/oembed/?url=powershell",
|
||||||
"/ghost/api/admin/posts/?pathname=%2Fpowershell%2F",
|
|
||||||
"/ghost/api/admin/stats/top-content/?order=powershell",
|
|
||||||
"/ghost/api/admin/pages/?email_segment=label%3Apowershell-users",
|
|
||||||
"/ghost/api/admin/posts/?query=python3",
|
|
||||||
"/.ghost/activitypub/v1/actions/note?query=python3",
|
|
||||||
"/ghost/api/admin/invites/?email=python3.dev%40example.com",
|
|
||||||
} {
|
} {
|
||||||
wantGhostQuery(t, target, true)
|
wantGhostQuery(t, target, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestGhostStaffCommentAndActivityPubExclusionsAreNarrow checks the body
|
|
||||||
// fields that 900370 to 900382 name, as TestGhostExclusionsAreNarrow checks
|
|
||||||
// the others: each keeps its rules on another path, and when the query has
|
|
||||||
// a field of the same name.
|
|
||||||
func TestGhostStaffCommentAndActivityPubExclusionsAreNarrow(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
const (
|
|
||||||
bio = `{"users":[{"bio":"python3"}]}`
|
|
||||||
comment = `{"comments":[{"html":"<p>Notes on .htaccess</p>"}]}`
|
|
||||||
content = `{"content":"python3"}`
|
|
||||||
altText = `{"image":{"altText":"python3"}}`
|
|
||||||
accountBio = `{"bio":"python3"}`
|
|
||||||
invited = `{"invites":[{"email":"python3.dev@example.com"}]}`
|
|
||||||
accepted = `{"invitation":[{"email":"python3.dev@example.com"}]}`
|
|
||||||
sourceHandle = `{"sourceHandle":"@powershell@example.com"}`
|
|
||||||
actorURI = `{"actorUri":"https://example.com/users/powershell"}`
|
|
||||||
targetHandle = `{"targetHandle":"@powershell@example.com"}`
|
|
||||||
domain = `{"domain":"python3.example.com"}`
|
|
||||||
tagCode = `{"tags":[{"codeinjection_head":"<meta name=x content=y>"}]}`
|
|
||||||
tagURL = `{"tags":[{"canonical_url":"https://example.com/tag/powershell/"}]}`
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, refused := range []struct{ target, body string }{
|
|
||||||
{"/api/users/1/", bio},
|
|
||||||
{ghostMember, bio},
|
|
||||||
{ghostUser + "?json.users.0.bio=x", bio},
|
|
||||||
{ghostPosts, comment},
|
|
||||||
{ghostComments + "?json.comments.0.html=x", comment},
|
|
||||||
{ghostAccount, content},
|
|
||||||
{ghostNote + "?json.content=x", content},
|
|
||||||
{ghostAccount, altText},
|
|
||||||
{ghostNote + "?json.image.altText=x", altText},
|
|
||||||
{ghostNote, accountBio},
|
|
||||||
{ghostAccount + "?json.bio=x", accountBio},
|
|
||||||
{ghostPosts, invited},
|
|
||||||
{ghostInvites + "?json.invites.0.email=x", invited},
|
|
||||||
{ghostInvites, accepted},
|
|
||||||
{ghostInvitation + "?json.invitation.0.email=x", accepted},
|
|
||||||
{ghostMigration, sourceHandle},
|
|
||||||
{ghostAliases + "?json.sourceHandle=x", sourceHandle},
|
|
||||||
{ghostMigration, actorURI},
|
|
||||||
{ghostAliases + "?json.actorUri=x", actorURI},
|
|
||||||
{ghostAliases, targetHandle},
|
|
||||||
{ghostMigration + "?json.targetHandle=x", targetHandle},
|
|
||||||
{ghostAccount, domain},
|
|
||||||
{ghostDomain + "?json.domain=x", domain},
|
|
||||||
{ghostPosts, tagCode},
|
|
||||||
{ghostTags + "?json.tags.0.codeinjection_head=x", tagCode},
|
|
||||||
{ghostPosts, tagURL},
|
|
||||||
{ghostTags + "?json.tags.0.canonical_url=x", tagURL},
|
|
||||||
} {
|
|
||||||
r := request{http.MethodPut, refused.target, []string{jsonBody}}
|
|
||||||
|
|
||||||
got, _ := inspectBody(t, readingBodies(t), r, refused.body)
|
|
||||||
if got.Score < ghostThreshold {
|
|
||||||
t.Errorf("PUT %s %s let through: %+v", refused.target,
|
|
||||||
refused.body, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGhostPasswordsPass(t *testing.T) {
|
func TestGhostPasswordsPass(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -172,13 +172,11 @@ SecRule REQUEST_FILENAME "@rx ^/(?:explore/repos|user/search_candidates|\
|
|||||||
|
|
||||||
# A branch, tag or package name can hold a slash, as feat/docker-support and
|
# A branch, tag or package name can hold a slash, as feat/docker-support and
|
||||||
# npm's @scope/name do. So q on a repository's branches, tags and packages
|
# npm's @scope/name do. So q on a repository's branches, tags and packages
|
||||||
# pages, on an owner's packages page, on the admin page of packages, on the
|
# pages, on an owner's packages page and on the admin page of packages also
|
||||||
# API's search of packages and on the Composer and Conan registries' searches
|
# skips this rule:
|
||||||
# also skips this rule:
|
|
||||||
# 932160: /bin/sh
|
# 932160: /bin/sh
|
||||||
SecRule REQUEST_FILENAME "@rx ^/(?:[^/]+/[^/]+/(?:branches|tags|packages)|\
|
SecRule REQUEST_FILENAME "@rx ^/(?:[^/]+/[^/]+/(?:branches|tags|packages)|\
|
||||||
admin/packages|api/v1/packages/[^/]+|\
|
admin/packages)$" \
|
||||||
api/packages/[^/]+/(?:composer/search\.json|conan/v[12]/conans/search))$" \
|
|
||||||
"id:900605,phase:1,pass,nolog,ctl:ruleRemoveTargetById=932160;ARGS:q"
|
"id:900605,phase:1,pass,nolog,ctl:ruleRemoveTargetById=932160;ARGS:q"
|
||||||
|
|
||||||
# gitea's other searches send a name or a few words in q too, which skips only
|
# gitea's other searches send a name or a few words in q too, which skips only
|
||||||
@@ -190,11 +188,7 @@ SecRule REQUEST_FILENAME "@rx ^/(?:[^/]+/[^/]+/(?:branches|tags|packages)|\
|
|||||||
# The searches:
|
# The searches:
|
||||||
# - the dashboard's filter of repositories;
|
# - the dashboard's filter of repositories;
|
||||||
# - the author filter of the issue and pull request lists;
|
# - the author filter of the issue and pull request lists;
|
||||||
# - the lists of users and organisations;
|
# - the lists of users and organisations, and the API's search for users;
|
||||||
# - the API's searches of users, of topics, of an organisation's teams and of
|
|
||||||
# emails;
|
|
||||||
# - the searches of the Cargo, Chef, Composer and Conan registries, and the
|
|
||||||
# NuGet registry's search service;
|
|
||||||
# - the list of repositories a user watches;
|
# - the list of repositories a user watches;
|
||||||
# - an organisation's milestones of one team, and its search for teams;
|
# - an organisation's milestones of one team, and its search for teams;
|
||||||
# - the milestones of a repository or an organisation, and the projects of a
|
# - the milestones of a repository or an organisation, and the projects of a
|
||||||
@@ -211,11 +205,7 @@ SecRule REQUEST_FILENAME "@rx ^/(?:[^/]+/[^/]+/(?:branches|tags|packages)|\
|
|||||||
# versions holds the package's name, which can hold a slash, as npm's
|
# versions holds the package's name, which can hold a slash, as npm's
|
||||||
# @scope/name does.
|
# @scope/name does.
|
||||||
SecRule REQUEST_FILENAME "@rx ^/(?:repo/search|notifications/watching|\
|
SecRule REQUEST_FILENAME "@rx ^/(?:repo/search|notifications/watching|\
|
||||||
explore/(?:users|organizations)|\
|
explore/(?:users|organizations)|api/v1/users/search|\
|
||||||
api/v1/(?:users/search|topics/search|orgs/[^/]+/teams/search|\
|
|
||||||
admin/emails/search)|\
|
|
||||||
api/packages/[^/]+/(?:cargo/api/v1/crates|chef/api/v1/(?:search|cookbooks)|\
|
|
||||||
composer/search\.json|conan/v[12]/conans/search|nuget/query)|\
|
|
||||||
org/[^/]+/(?:milestones/[^/]+|teams/-/search)|\
|
org/[^/]+/(?:milestones/[^/]+|teams/-/search)|\
|
||||||
[^/]+/-/packages/[^/]+/.+/versions|\
|
[^/]+/-/packages/[^/]+/.+/versions|\
|
||||||
[^/]+/[^/]+/(?:(?:issues|pulls)/posters|issues/suggestions|milestones|\
|
[^/]+/[^/]+/(?:(?:issues|pulls)/posters|issues/suggestions|milestones|\
|
||||||
@@ -229,88 +219,6 @@ SecRule REQUEST_FILENAME "@rx ^/(?:repo/search|notifications/watching|\
|
|||||||
ctl:ruleRemoveTargetById=932250;ARGS:q,\
|
ctl:ruleRemoveTargetById=932250;ARGS:q,\
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:q"
|
ctl:ruleRemoveTargetById=932260;ARGS:q"
|
||||||
|
|
||||||
# Two of the API's searches send their words in a field other than q, which
|
|
||||||
# skips these rules there:
|
|
||||||
# 930120: .htaccess
|
|
||||||
# 932120: powershell
|
|
||||||
# 932250: ls -la
|
|
||||||
# 932260: python3, alice/python3-app
|
|
||||||
# The search of unadopted repositories sends an owner and a name, such as
|
|
||||||
# alice/python3-app, in query.
|
|
||||||
SecRule REQUEST_FILENAME "@rx ^/api/v1/admin/unadopted$" \
|
|
||||||
"id:900607,phase:1,pass,nolog,\
|
|
||||||
ctl:ruleRemoveTargetById=930120;ARGS:query,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:query,\
|
|
||||||
ctl:ruleRemoveTargetById=932250;ARGS:query,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:query"
|
|
||||||
|
|
||||||
# The search of a repository's milestones sends a milestone's name in name.
|
|
||||||
SecRule REQUEST_FILENAME "@rx ^/api/v1/repos/[^/]+/[^/]+/milestones$" \
|
|
||||||
"id:900608,phase:1,pass,nolog,\
|
|
||||||
ctl:ruleRemoveTargetById=930120;ARGS:name,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:name,\
|
|
||||||
ctl:ruleRemoveTargetById=932250;ARGS:name,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:name"
|
|
||||||
|
|
||||||
# The npm registry's search sends its words in text, which skips these rules
|
|
||||||
# there:
|
|
||||||
# 930120: .htaccess
|
|
||||||
# 932120: powershell
|
|
||||||
# 932160: /bin/sh, as an npm name such as @alice/tools-cli holds a slash
|
|
||||||
# 932250: ls -la
|
|
||||||
# 932260: python3
|
|
||||||
SecRule REQUEST_FILENAME "@rx ^/api/packages/[^/]+/npm/-/v1/search$" \
|
|
||||||
"id:900609,phase:1,pass,nolog,\
|
|
||||||
ctl:ruleRemoveTargetById=930120;ARGS:text,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:text,\
|
|
||||||
ctl:ruleRemoveTargetById=932160;ARGS:text,\
|
|
||||||
ctl:ruleRemoveTargetById=932250;ARGS:text,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:text"
|
|
||||||
|
|
||||||
# A Conan search takes a pattern such as zlib/*, which the rule for SQL
|
|
||||||
# injection (942100) refused in q. q skips that rule only when the request has
|
|
||||||
# one q, in the query string or the body, with the shape of a Conan reference.
|
|
||||||
# A reference is a name, then optionally / and a version, then optionally @
|
|
||||||
# and a user, then optionally / and a channel. Each part is made of letters,
|
|
||||||
# digits, _, ., +, - and *. q must also hold no */, which ends an SQL comment.
|
|
||||||
# Such a q has no quote, no space and no comment between two words, so SQL
|
|
||||||
# injection such as 1' OR '1'='1, 1/**/or/**/1 or 1/*@*/or-1 stays refused.
|
|
||||||
# This rule is in phase 2, because a phase 1 rule runs before the body is read
|
|
||||||
# and cannot see a q there. It checks tx.app_gitea, since the skip above does
|
|
||||||
# not reach phase 2.
|
|
||||||
SecRule REQUEST_FILENAME "@rx ^/api/packages/[^/]+/conan/v[12]/conans/search$" \
|
|
||||||
"id:900611,phase:2,pass,nolog,chain"
|
|
||||||
SecRule &TX:app_gitea "@eq 1" "chain"
|
|
||||||
SecRule &ARGS:q "@eq 1" "chain"
|
|
||||||
SecRule ARGS:q "!@contains */" "chain"
|
|
||||||
SecRule ARGS:q "@rx ^[\w.+*-]+(?:/[\w.+*-]+)?(?:@[\w.+*-]+(?:/[\w.+*-]+)?)?$" \
|
|
||||||
"ctl:ruleRemoveTargetById=942100;ARGS:q"
|
|
||||||
|
|
||||||
# The NuGet registry's older searches, Search() and Packages(), send their words
|
|
||||||
# in searchTerm, or in $filter as an expression such as
|
|
||||||
# tolower(Id) eq 'python3'. Search()/$count and Packages()/$count count a
|
|
||||||
# search's results and read the same fields. searchTerm skips these rules:
|
|
||||||
# 930120: .htaccess
|
|
||||||
# 932120: powershell
|
|
||||||
# 932250: ls -la
|
|
||||||
# 932260: python3
|
|
||||||
# $filter skips these:
|
|
||||||
# 930120: .htaccess
|
|
||||||
# 932120: powershell
|
|
||||||
# The names $filter, $orderby and $top skip the rule for MongoDB operators
|
|
||||||
# (942290), which refused every such search.
|
|
||||||
SecRule REQUEST_FILENAME "@rx ^/api/packages/[^/]+/nuget/(?:Search|Packages)\(\)(?:/\$count)?$" \
|
|
||||||
"id:900612,phase:1,pass,nolog,\
|
|
||||||
ctl:ruleRemoveTargetById=930120;ARGS:searchTerm,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:searchTerm,\
|
|
||||||
ctl:ruleRemoveTargetById=932250;ARGS:searchTerm,\
|
|
||||||
ctl:ruleRemoveTargetById=932260;ARGS:searchTerm,\
|
|
||||||
ctl:ruleRemoveTargetById=930120;ARGS:$filter,\
|
|
||||||
ctl:ruleRemoveTargetById=932120;ARGS:$filter,\
|
|
||||||
ctl:ruleRemoveTargetById=942290;ARGS_NAMES:$filter,\
|
|
||||||
ctl:ruleRemoveTargetById=942290;ARGS_NAMES:$orderby,\
|
|
||||||
ctl:ruleRemoveTargetById=942290;ARGS_NAMES:$top"
|
|
||||||
|
|
||||||
# The repositories on a user's or an organisation's page are searched with q
|
# The repositories on a user's or an organisation's page are searched with q
|
||||||
# too. Their path has one segment, so q skips these rules on every path of one
|
# too. Their path has one segment, so q skips these rules on every path of one
|
||||||
# segment, other apps' pages such as /index.php included:
|
# segment, other apps' pages such as /index.php included:
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package coreruleset_test
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"sneak.berlin/go/smallwebwaf/internal/coreruleset"
|
"sneak.berlin/go/smallwebwaf/internal/coreruleset"
|
||||||
@@ -183,7 +182,7 @@ func TestGiteaSearchExclusionsAreNarrow(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
attacks := []string{binSh, commandInjection, pathTraversal}
|
attacks := []string{"%2Fbin%2Fsh", commandInjection, pathTraversal}
|
||||||
|
|
||||||
// /bin/sh, a shell command after a ";", a path traversal and the hard
|
// /bin/sh, a shell command after a ";", a path traversal and the hard
|
||||||
// values stay refused in q on paths near the searches, and in another
|
// values stay refused in q on paths near the searches, and in another
|
||||||
@@ -249,239 +248,3 @@ func TestGiteaSearchExclusionsAreNarrow(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// giteaAPISearches are the API's searches that gitea and Forgejo share whose
|
|
||||||
// field skips the lists of system files and command names. Each ends in the
|
|
||||||
// field it sends its words in.
|
|
||||||
//
|
|
||||||
//nolint:gochecknoglobals // a constant cannot be a list
|
|
||||||
var giteaAPISearches = []string{
|
|
||||||
"/api/v1/users/search?q=",
|
|
||||||
"/api/v1/topics/search?q=",
|
|
||||||
"/api/v1/orgs/acme/teams/search?q=",
|
|
||||||
"/api/v1/admin/emails/search?q=",
|
|
||||||
"/api/v1/admin/unadopted?query=",
|
|
||||||
"/api/v1/repos/alice/tools/milestones?name=",
|
|
||||||
}
|
|
||||||
|
|
||||||
// giteaAPIPackageSearch is the API's search of an owner's packages, whose q
|
|
||||||
// also lets /bin/sh through, since a package name can hold a slash.
|
|
||||||
const giteaAPIPackageSearch = "/api/v1/packages/alice?q="
|
|
||||||
|
|
||||||
func TestGiteaAttacksAreRefusedInItsAPISearches(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
checkSearchesRefuseAttacks(
|
|
||||||
t,
|
|
||||||
giteaAPISearches,
|
|
||||||
[]string{giteaAPIPackageSearch},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkSearchesRefuseAttacks checks that SQL injection, a path traversal,
|
|
||||||
// script injection and a shell command stay refused in the field of each
|
|
||||||
// search, in the query string and in a form data body. Each search ends in
|
|
||||||
// its field. /bin/sh stays refused too in the field of each of
|
|
||||||
// refusingBinSh, but not of lettingBinSh.
|
|
||||||
func checkSearchesRefuseAttacks(
|
|
||||||
t *testing.T, refusingBinSh, lettingBinSh []string,
|
|
||||||
) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
const threshold = 5 // SWW_WAF_CORE_RULE_SET_SCORE_THRESHOLD's default
|
|
||||||
|
|
||||||
defaults, bodies := atDefaults(t), readingBodies(t)
|
|
||||||
|
|
||||||
for _, search := range slices.Concat(refusingBinSh, lettingBinSh) {
|
|
||||||
path, field, _ := strings.Cut(search, "?")
|
|
||||||
|
|
||||||
for _, attack := range []string{
|
|
||||||
sqlInjection, pathTraversal, scriptInjection, commandInjection,
|
|
||||||
} {
|
|
||||||
if got := inspect(t, defaults, get(search+attack)); got.Score < threshold {
|
|
||||||
t.Errorf("GET %s%s let through: %+v", search, attack, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The same field in a form data body.
|
|
||||||
r := request{http.MethodPost, path, []string{formData}}
|
|
||||||
if got, _ := inspectBody(t, bodies, r, field+attack); got.Score < threshold {
|
|
||||||
t.Errorf(
|
|
||||||
"POST %s %s%s let through: %+v",
|
|
||||||
path,
|
|
||||||
field,
|
|
||||||
attack,
|
|
||||||
got,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, search := range refusingBinSh {
|
|
||||||
if got := inspect(t, defaults, get(search+binSh)); got.Score < threshold {
|
|
||||||
t.Errorf("%s%s let through: %+v", search, binSh, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGiteaAPISearchExclusionsAreNarrow(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
// Each search's field on paths near the searches, and another field on
|
|
||||||
// them.
|
|
||||||
checkValuesStayRefused(t, []string{
|
|
||||||
"/api/v1/topics/search/x?q=",
|
|
||||||
"/api/v1/orgs/acme/teams?q=",
|
|
||||||
"/api/v1/orgs/acme/teams/search/x?q=",
|
|
||||||
"/api/v1/admin/emails?q=",
|
|
||||||
"/api/v1/admin/unadopted/alice/legacy-site?query=",
|
|
||||||
"/api/v1/admin/users?query=",
|
|
||||||
"/api/v1/repos/alice/tools/milestones/1?name=",
|
|
||||||
"/api/v1/repos/alice/tools/labels?name=",
|
|
||||||
"/api/v1/packages/alice/generic/tools-cli?q=",
|
|
||||||
"/api/v1/topics/search?other=",
|
|
||||||
"/api/v1/orgs/acme/teams/search?other=",
|
|
||||||
"/api/v1/admin/emails/search?other=",
|
|
||||||
"/api/v1/admin/unadopted?q=",
|
|
||||||
"/api/v1/repos/alice/tools/milestones?q=",
|
|
||||||
"/api/v1/packages/alice?other=",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkValuesStayRefused checks that /bin/sh, a shell command after a ";", a
|
|
||||||
// path traversal and the hard values stay refused at the end of each target.
|
|
||||||
func checkValuesStayRefused(t *testing.T, targets []string) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
const threshold = 5 // SWW_WAF_CORE_RULE_SET_SCORE_THRESHOLD's default
|
|
||||||
|
|
||||||
crs := atDefaults(t)
|
|
||||||
|
|
||||||
for _, target := range targets {
|
|
||||||
for _, value := range slices.Concat(
|
|
||||||
[]string{binSh, commandInjection, pathTraversal},
|
|
||||||
hardValues,
|
|
||||||
) {
|
|
||||||
if got := inspect(t, crs, get(target+value)); got.Score < threshold {
|
|
||||||
t.Errorf("%s%s let through: %+v", target, value, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// giteaRegistrySearches are the package registries' searches whose field
|
|
||||||
// skips the lists of system files and command names. Each ends in the field
|
|
||||||
// it sends its words in.
|
|
||||||
//
|
|
||||||
//nolint:gochecknoglobals // a constant cannot be a list
|
|
||||||
var giteaRegistrySearches = []string{
|
|
||||||
"/api/packages/alice/cargo/api/v1/crates?q=",
|
|
||||||
"/api/packages/alice/chef/api/v1/search?q=",
|
|
||||||
"/api/packages/alice/chef/api/v1/cookbooks?q=",
|
|
||||||
"/api/packages/alice/nuget/query?q=",
|
|
||||||
"/api/packages/alice/nuget/Search()?searchTerm=",
|
|
||||||
"/api/packages/alice/nuget/Packages()?$filter=",
|
|
||||||
"/api/packages/alice/nuget/Search()/$count?searchTerm=",
|
|
||||||
"/api/packages/alice/nuget/Packages()/$count?$filter=",
|
|
||||||
}
|
|
||||||
|
|
||||||
// giteaRegistryNameSearches are the package registries' searches whose field
|
|
||||||
// also lets /bin/sh through, since a Composer, Conan or npm name can hold a
|
|
||||||
// slash.
|
|
||||||
//
|
|
||||||
//nolint:gochecknoglobals // a constant cannot be a list
|
|
||||||
var giteaRegistryNameSearches = []string{
|
|
||||||
"/api/packages/alice/composer/search.json?q=",
|
|
||||||
"/api/packages/alice/conan/v1/conans/search?q=",
|
|
||||||
"/api/packages/alice/conan/v2/conans/search?q=",
|
|
||||||
"/api/packages/alice/npm/-/v1/search?text=",
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGiteaAttacksAreRefusedInItsRegistrySearches(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
checkSearchesRefuseAttacks(
|
|
||||||
t,
|
|
||||||
giteaRegistrySearches,
|
|
||||||
giteaRegistryNameSearches,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGiteaRegistrySearchExclusionsAreNarrow(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
// Each search's field on paths near the searches, and another field on
|
|
||||||
// them.
|
|
||||||
checkValuesStayRefused(t, []string{
|
|
||||||
"/api/packages/alice/cargo/api/v1/crates/tools-cli?q=",
|
|
||||||
"/api/packages/alice/chef/api/v1/cookbooks/tools?q=",
|
|
||||||
"/api/packages/alice/composer/packages.json?q=",
|
|
||||||
"/api/packages/alice/conan/v1/conans/tools/1.0/alice/stable/search?q=",
|
|
||||||
"/api/packages/alice/nuget/query/x?q=",
|
|
||||||
"/api/packages/alice/nuget/FindPackagesById()?id=",
|
|
||||||
"/api/packages/alice/nuget/FindPackagesById()/$count?id=",
|
|
||||||
"/api/packages/alice/nuget/Search()/$count/x?searchTerm=",
|
|
||||||
"/api/packages/alice/npm/tools-cli?text=",
|
|
||||||
"/api/packages/alice/cargo/api/v1/crates?other=",
|
|
||||||
"/api/packages/alice/chef/api/v1/search?other=",
|
|
||||||
"/api/packages/alice/composer/search.json?type=",
|
|
||||||
"/api/packages/alice/conan/v2/conans/search?other=",
|
|
||||||
"/api/packages/alice/nuget/query?other=",
|
|
||||||
"/api/packages/alice/nuget/Search()?targetFramework=",
|
|
||||||
"/api/packages/alice/nuget/Packages()?$orderby=",
|
|
||||||
"/api/packages/alice/nuget/Search()/$count?targetFramework=",
|
|
||||||
"/api/packages/alice/nuget/Packages()/$count?$orderby=",
|
|
||||||
"/api/packages/alice/npm/-/v1/search?q=",
|
|
||||||
})
|
|
||||||
|
|
||||||
const threshold = 5 // SWW_WAF_CORE_RULE_SET_SCORE_THRESHOLD's default
|
|
||||||
|
|
||||||
crs := atDefaults(t)
|
|
||||||
|
|
||||||
// The rule for MongoDB operators (942290) still refuses the name of
|
|
||||||
// another operator on NuGet's searches, and $filter on NuGet's other
|
|
||||||
// paths. The rule for SQL injection (942100) still refuses tools/* in a
|
|
||||||
// field other than a Conan search's, SQL injection in a second q after a
|
|
||||||
// Conan pattern, and SQL comments that stand in for spaces.
|
|
||||||
for _, target := range []string{
|
|
||||||
"/api/packages/alice/nuget/Search()?$where=1",
|
|
||||||
"/api/packages/alice/nuget/Packages()?$ne=1",
|
|
||||||
"/api/packages/alice/nuget/Search()/$count?$where=1",
|
|
||||||
"/api/packages/alice/nuget/FindPackagesById()?$filter=IsLatestVersion",
|
|
||||||
"/api/packages/alice/nuget/FindPackagesById()/$count?$filter=IsLatestVersion",
|
|
||||||
"/api/packages/alice/composer/search.json?q=tools%2F%2A",
|
|
||||||
"/api/packages/alice/conan/v2/conans/search?other=tools%2F%2A",
|
|
||||||
"/api/packages/alice/conan/v2/conans/search?q=tools%2F%2A&q=" +
|
|
||||||
sqlInjection,
|
|
||||||
"/api/packages/alice/conan/v2/conans/search?q=1%2F%2A%2A%2For%2F%2A%2A%2F1",
|
|
||||||
"/api/packages/alice/conan/v2/conans/search?q=1%2F%2A%40%2A%2For-1",
|
|
||||||
} {
|
|
||||||
if got := inspect(t, crs, get(target)); got.Score < threshold {
|
|
||||||
t.Errorf("%s let through: %+v", target, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SQL injection in a q in the body is refused, even after a Conan
|
|
||||||
// pattern in the query string.
|
|
||||||
search := request{
|
|
||||||
http.MethodPost,
|
|
||||||
"/api/packages/alice/conan/v2/conans/search?q=zlib",
|
|
||||||
[]string{formData},
|
|
||||||
}
|
|
||||||
|
|
||||||
body := "q=" + sqlInjection
|
|
||||||
if got, _ := inspectBody(t, readingBodies(t), search, body); got.Score < threshold {
|
|
||||||
t.Errorf("POST %s %s let through: %+v", search.target, body, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The rule that lets a Conan pattern through is in phase 2, where it
|
|
||||||
// checks tx.app_gitea itself: a hostname that does not serve gitea
|
|
||||||
// refuses the pattern.
|
|
||||||
pattern := get(
|
|
||||||
"/api/packages/alice/conan/v2/conans/search?q=tools%2F%2A",
|
|
||||||
)
|
|
||||||
apps := servingApps(t, map[string][]string{
|
|
||||||
giteaHost: {gitea}, otherHost: {wordpress},
|
|
||||||
})
|
|
||||||
wantResult(t, apps, withHost(pattern, giteaHost), coreruleset.Result{})
|
|
||||||
wantResult(t, apps, withHost(pattern, otherHost), matched(942100))
|
|
||||||
}
|
|
||||||
-160
@@ -974,163 +974,3 @@
|
|||||||
{"method": "GET", "target": "/org/acme/issues/developers?type=your_repositories&sort=recentupdate&project=ZgotmplZ&state=open&q=SELECT%20%2A%20FROM%20users%20WHERE", "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) Chrome/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", "Referer: http://example.com/org/acme/issues/developers", "Accept-Encoding: gzip, deflate", "Cookie: session=5d2e8a17c4b9f063; lang=en-US"], "body": ""}
|
{"method": "GET", "target": "/org/acme/issues/developers?type=your_repositories&sort=recentupdate&project=ZgotmplZ&state=open&q=SELECT%20%2A%20FROM%20users%20WHERE", "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) Chrome/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", "Referer: http://example.com/org/acme/issues/developers", "Accept-Encoding: gzip, deflate", "Cookie: session=5d2e8a17c4b9f063; lang=en-US"], "body": ""}
|
||||||
{"method": "GET", "target": "/org/acme/pulls/developers?type=your_repositories&sort=recentupdate&project=ZgotmplZ&state=open&q=SELECT%20%2A%20FROM%20users%20WHERE", "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) Chrome/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", "Referer: http://example.com/org/acme/pulls/developers", "Accept-Encoding: gzip, deflate", "Cookie: session=5d2e8a17c4b9f063; lang=en-US"], "body": ""}
|
{"method": "GET", "target": "/org/acme/pulls/developers?type=your_repositories&sort=recentupdate&project=ZgotmplZ&state=open&q=SELECT%20%2A%20FROM%20users%20WHERE", "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) Chrome/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", "Referer: http://example.com/org/acme/pulls/developers", "Accept-Encoding: gzip, deflate", "Cookie: session=5d2e8a17c4b9f063; lang=en-US"], "body": ""}
|
||||||
{"method": "GET", "target": "/alice/tools/milestone/1?q=SELECT%20%2A%20FROM%20users%20WHERE", "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) Chrome/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", "Referer: http://example.com/alice/tools/milestone/1", "Accept-Encoding: gzip, deflate", "Cookie: session=5d2e8a17c4b9f063; lang=en-US"], "body": ""}
|
{"method": "GET", "target": "/alice/tools/milestone/1?q=SELECT%20%2A%20FROM%20users%20WHERE", "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) Chrome/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", "Referer: http://example.com/alice/tools/milestone/1", "Accept-Encoding: gzip, deflate", "Cookie: session=5d2e8a17c4b9f063; lang=en-US"], "body": ""}
|
||||||
# The API's searches that gitea and Forgejo share, sent with curl to Forgejo
|
|
||||||
# 17.0.1. Each searches for words that name commands and files, such as python3
|
|
||||||
# and .htaccess, and for plain words. A package name can hold a slash, as npm's
|
|
||||||
# @alice/tools-cli does, so the search of packages is also sent /bin/sh. The
|
|
||||||
# search of unadopted repositories takes an owner and a name.
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/search?q=python3&limit=10", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/search?q=powershell&limit=10", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/search?q=ls%20-la&limit=10", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/search?q=.htaccess&limit=10", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/search?q=docker-compose.yml&limit=10", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/search?q=tools&limit=10", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/users/search?q=python3", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/users/search?q=powershell", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/users/search?q=ls%20-la", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/users/search?q=.htaccess", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/users/search?q=bob", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/issues/search?q=python3&type=issues&state=open", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/issues/search?q=powershell&type=issues&state=open", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/issues/search?q=ls%20-la&type=issues&state=open", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/issues/search?q=.htaccess&type=issues&state=open", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/issues/search?q=docker-compose.yml&type=issues&state=open", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/issues/search?q=build&type=issues&state=open", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/alice/tools/issues?state=open&q=python3", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/alice/tools/issues?state=open&q=powershell", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/alice/tools/issues?state=open&q=ls%20-la", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/alice/tools/issues?state=open&q=.htaccess", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/alice/tools/issues?state=open&q=docker-compose.yml", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/packages/alice?q=python3", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/packages/alice?q=powershell", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/packages/alice?q=ls%20-la", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/packages/alice?q=.htaccess", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/packages/alice?q=%2Fbin%2Fsh", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/packages/alice?q=install-script", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/packages/alice?q=%40alice%2Ftools-cli", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/topics/search?q=python3", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/topics/search?q=powershell", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/topics/search?q=ls%20-la", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/topics/search?q=.htaccess", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/topics/search?q=docker", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/orgs/acme/teams/search?q=python3", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/orgs/acme/teams/search?q=powershell", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/orgs/acme/teams/search?q=ls%20-la", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/orgs/acme/teams/search?q=.htaccess", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/orgs/acme/teams/search?q=developers", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/admin/emails/search?q=python3", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/admin/emails/search?q=powershell", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/admin/emails/search?q=ls%20-la", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/admin/emails/search?q=.htaccess", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/admin/emails/search?q=alice", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/admin/unadopted?query=python3", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/admin/unadopted?query=powershell", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/admin/unadopted?query=ls%20-la", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/admin/unadopted?query=.htaccess", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/admin/unadopted?query=alice%2Fpython3-app", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/admin/unadopted?query=alice%2Flegacy-site", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/alice/tools/milestones?state=open&name=python3", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/alice/tools/milestones?state=open&name=powershell", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/alice/tools/milestones?state=open&name=ls%20-la", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/alice/tools/milestones?state=open&name=.htaccess", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/v1/repos/alice/tools/milestones?state=open&name=v1.0", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*", "Authorization: token 772deb2aefef4c9484e8c673197b82bc880fc7ca"], "body": ""}
|
|
||||||
# The package registries' searches, sent to Forgejo 17.0.1 by each registry's
|
|
||||||
# own client: npm, composer, conan 2 and 1, cargo, dotnet and knife. curl sent
|
|
||||||
# the requests no client sends: the Chef cookbook list with a search, and
|
|
||||||
# NuGet's Packages() with the search in $filter, as NuGet's client builds it for
|
|
||||||
# a feed without Search(). curl also sent each NuGet Search() and Packages()
|
|
||||||
# search to Search()/$count and Packages()/$count, which count its results. Each
|
|
||||||
# searches for words that name commands and files, such as python3 and
|
|
||||||
# .htaccess, and for plain words. A Composer, Conan or npm name can hold a
|
|
||||||
# slash, so those searches are also sent /bin/sh.
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/npm/-/v1/search?text=python3&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5", "headers": ["user-agent: npm/10.9.9 node/v22.23.5 linux x64 workspaces/false", "npm-auth-type: web", "npm-command: search", "Accept: */*", "Accept-Encoding: gzip,deflate", "Host: example.com", "connection: keep-alive"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/npm/-/v1/search?text=powershell&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5", "headers": ["user-agent: npm/10.9.9 node/v22.23.5 linux x64 workspaces/false", "npm-auth-type: web", "npm-command: search", "Accept: */*", "Accept-Encoding: gzip,deflate", "Host: example.com", "connection: keep-alive"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/npm/-/v1/search?text=ls+-la&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5", "headers": ["user-agent: npm/10.9.9 node/v22.23.5 linux x64 workspaces/false", "npm-auth-type: web", "npm-command: search", "Accept: */*", "Accept-Encoding: gzip,deflate", "Host: example.com", "connection: keep-alive"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/npm/-/v1/search?text=.htaccess&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5", "headers": ["user-agent: npm/10.9.9 node/v22.23.5 linux x64 workspaces/false", "npm-auth-type: web", "npm-command: search", "Accept: */*", "Accept-Encoding: gzip,deflate", "Host: example.com", "connection: keep-alive"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/npm/-/v1/search?text=%2Fbin%2Fsh&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5", "headers": ["user-agent: npm/10.9.9 node/v22.23.5 linux x64 workspaces/false", "npm-auth-type: web", "npm-command: search", "Accept: */*", "Accept-Encoding: gzip,deflate", "Host: example.com", "connection: keep-alive"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/npm/-/v1/search?text=%40alice%2Ftools-cli&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5", "headers": ["user-agent: npm/10.9.9 node/v22.23.5 linux x64 workspaces/false", "npm-auth-type: web", "npm-command: search", "Accept: */*", "Accept-Encoding: gzip,deflate", "Host: example.com", "connection: keep-alive"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/npm/-/v1/search?text=docker-compose.yml&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5", "headers": ["user-agent: npm/10.9.9 node/v22.23.5 linux x64 workspaces/false", "npm-auth-type: web", "npm-command: search", "Accept: */*", "Accept-Encoding: gzip,deflate", "Host: example.com", "connection: keep-alive"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/npm/-/v1/search?text=tools&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5", "headers": ["user-agent: npm/10.9.9 node/v22.23.5 linux x64 workspaces/false", "npm-auth-type: web", "npm-command: search", "Accept: */*", "Accept-Encoding: gzip,deflate", "Host: example.com", "connection: keep-alive"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/composer/search.json?q=python3&type=", "headers": ["Host: example.com", "Accept: */*", "Accept-Encoding: deflate, gzip, br, zstd", "User-Agent: Composer/2.11.2 (Linux; 6.8.0-111-generic; PHP 8.5.12; cURL 8.23.0; cmd:search)", "Connection: keep-alive"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/composer/search.json?q=powershell&type=", "headers": ["Host: example.com", "Accept: */*", "Accept-Encoding: deflate, gzip, br, zstd", "User-Agent: Composer/2.11.2 (Linux; 6.8.0-111-generic; PHP 8.5.12; cURL 8.23.0; cmd:search)", "Connection: keep-alive"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/composer/search.json?q=ls+-la&type=", "headers": ["Host: example.com", "Accept: */*", "Accept-Encoding: deflate, gzip, br, zstd", "User-Agent: Composer/2.11.2 (Linux; 6.8.0-111-generic; PHP 8.5.12; cURL 8.23.0; cmd:search)", "Connection: keep-alive"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/composer/search.json?q=.htaccess&type=", "headers": ["Host: example.com", "Accept: */*", "Accept-Encoding: deflate, gzip, br, zstd", "User-Agent: Composer/2.11.2 (Linux; 6.8.0-111-generic; PHP 8.5.12; cURL 8.23.0; cmd:search)", "Connection: keep-alive"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/composer/search.json?q=%2Fbin%2Fsh&type=", "headers": ["Host: example.com", "Accept: */*", "Accept-Encoding: deflate, gzip, br, zstd", "User-Agent: Composer/2.11.2 (Linux; 6.8.0-111-generic; PHP 8.5.12; cURL 8.23.0; cmd:search)", "Connection: keep-alive"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/composer/search.json?q=alice%2Ftools-cli&type=", "headers": ["Host: example.com", "Accept: */*", "Accept-Encoding: deflate, gzip, br, zstd", "User-Agent: Composer/2.11.2 (Linux; 6.8.0-111-generic; PHP 8.5.12; cURL 8.23.0; cmd:search)", "Connection: keep-alive"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/composer/search.json?q=docker-compose.yml&type=", "headers": ["Host: example.com", "Accept: */*", "Accept-Encoding: deflate, gzip, br, zstd", "User-Agent: Composer/2.11.2 (Linux; 6.8.0-111-generic; PHP 8.5.12; cURL 8.23.0; cmd:search)", "Connection: keep-alive"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/composer/search.json?q=tools&type=", "headers": ["Host: example.com", "Accept: */*", "Accept-Encoding: deflate, gzip, br, zstd", "User-Agent: Composer/2.11.2 (Linux; 6.8.0-111-generic; PHP 8.5.12; cURL 8.23.0; cmd:search)", "Connection: keep-alive"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v2/conans/search?q=python3", "headers": ["Host: example.com", "User-Agent: Conan/2.34.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v2/conans/search?q=powershell", "headers": ["Host: example.com", "User-Agent: Conan/2.34.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v2/conans/search?q=ls+-la", "headers": ["Host: example.com", "User-Agent: Conan/2.34.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v2/conans/search?q=.htaccess", "headers": ["Host: example.com", "User-Agent: Conan/2.34.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v2/conans/search?q=%2Fbin%2Fsh", "headers": ["Host: example.com", "User-Agent: Conan/2.34.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v2/conans/search?q=tools%2F%2A", "headers": ["Host: example.com", "User-Agent: Conan/2.34.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v2/conans/search?q=tools%2F1.0%40alice%2Fstable", "headers": ["Host: example.com", "User-Agent: Conan/2.34.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v2/conans/search?q=docker-compose.yml", "headers": ["Host: example.com", "User-Agent: Conan/2.34.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v2/conans/search?q=tools", "headers": ["Host: example.com", "User-Agent: Conan/2.34.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v1/conans/search?q=python3", "headers": ["Host: example.com", "User-Agent: Conan/1.66.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5", "X-Client-Id: "], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v1/conans/search?q=powershell", "headers": ["Host: example.com", "User-Agent: Conan/1.66.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5", "X-Client-Id: "], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v1/conans/search?q=ls+-la", "headers": ["Host: example.com", "User-Agent: Conan/1.66.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5", "X-Client-Id: "], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v1/conans/search?q=.htaccess", "headers": ["Host: example.com", "User-Agent: Conan/1.66.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5", "X-Client-Id: "], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v1/conans/search?q=%2Fbin%2Fsh", "headers": ["Host: example.com", "User-Agent: Conan/1.66.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5", "X-Client-Id: "], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v1/conans/search?q=tools%2F%2A", "headers": ["Host: example.com", "User-Agent: Conan/1.66.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5", "X-Client-Id: "], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v1/conans/search?q=docker-compose.yml", "headers": ["Host: example.com", "User-Agent: Conan/1.66.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5", "X-Client-Id: "], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/conan/v1/conans/search?q=tools", "headers": ["Host: example.com", "User-Agent: Conan/1.66.0 (Linux 6.8.0-111-generic; Python 3.13.18; x86_64)", "Accept-Encoding: gzip, deflate", "Accept: */*", "Connection: keep-alive", "X-Client-Anonymous-Id: afce410baeaee94a86997417d92f7788a91b00c5", "X-Client-Id: "], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/cargo/api/v1/crates?q=python3&per_page=10", "headers": ["Host: example.com", "User-Agent: cargo/1.99.1 (5f94df478 2026-08-27)", "Accept-Encoding: deflate, gzip", "Upgrade: h2c", "HTTP2-Settings: AAMAAABkAAQAAQAAAAIAAAAA", "accept: application/json", "Connection: Upgrade, HTTP2-Settings"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/cargo/api/v1/crates?q=powershell&per_page=10", "headers": ["Host: example.com", "User-Agent: cargo/1.99.1 (5f94df478 2026-08-27)", "Accept-Encoding: deflate, gzip", "Upgrade: h2c", "HTTP2-Settings: AAMAAABkAAQAAQAAAAIAAAAA", "accept: application/json", "Connection: Upgrade, HTTP2-Settings"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/cargo/api/v1/crates?q=ls%20%2Dla&per_page=10", "headers": ["Host: example.com", "User-Agent: cargo/1.99.1 (5f94df478 2026-08-27)", "Accept-Encoding: deflate, gzip", "Upgrade: h2c", "HTTP2-Settings: AAMAAABkAAQAAQAAAAIAAAAA", "accept: application/json", "Connection: Upgrade, HTTP2-Settings"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/cargo/api/v1/crates?q=%2Ehtaccess&per_page=10", "headers": ["Host: example.com", "User-Agent: cargo/1.99.1 (5f94df478 2026-08-27)", "Accept-Encoding: deflate, gzip", "Upgrade: h2c", "HTTP2-Settings: AAMAAABkAAQAAQAAAAIAAAAA", "accept: application/json", "Connection: Upgrade, HTTP2-Settings"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/cargo/api/v1/crates?q=docker%2Dcompose%2Eyml&per_page=10", "headers": ["Host: example.com", "User-Agent: cargo/1.99.1 (5f94df478 2026-08-27)", "Accept-Encoding: deflate, gzip", "Upgrade: h2c", "HTTP2-Settings: AAMAAABkAAQAAQAAAAIAAAAA", "accept: application/json", "Connection: Upgrade, HTTP2-Settings"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/cargo/api/v1/crates?q=tools&per_page=10", "headers": ["Host: example.com", "User-Agent: cargo/1.99.1 (5f94df478 2026-08-27)", "Accept-Encoding: deflate, gzip", "Upgrade: h2c", "HTTP2-Settings: AAMAAABkAAQAAQAAAAIAAAAA", "accept: application/json", "Connection: Upgrade, HTTP2-Settings"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/query?q=python3&skip=0&take=20&prerelease=false&semVerLevel=2.0.0", "headers": ["Host: example.com", "X-NuGet-Session-Id: bc433449-14e0-4cb0-907b-fcca1ead302b", "User-Agent: NuGet Client V3/6.14.3 (LINUX)", "X-NuGet-Client-Version: 6.14.3", "Accept-Encoding: gzip, deflate"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/query?q=powershell&skip=0&take=20&prerelease=false&semVerLevel=2.0.0", "headers": ["Host: example.com", "X-NuGet-Session-Id: e691cf8b-1859-45b1-aa67-e0987eb7474a", "User-Agent: NuGet Client V3/6.14.3 (LINUX)", "X-NuGet-Client-Version: 6.14.3", "Accept-Encoding: gzip, deflate"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/query?q=ls%20-la&skip=0&take=20&prerelease=false&semVerLevel=2.0.0", "headers": ["Host: example.com", "X-NuGet-Session-Id: 725a8ab1-a2c3-4924-84dd-a741bd6b8c07", "User-Agent: NuGet Client V3/6.14.3 (LINUX)", "X-NuGet-Client-Version: 6.14.3", "Accept-Encoding: gzip, deflate"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/query?q=.htaccess&skip=0&take=20&prerelease=false&semVerLevel=2.0.0", "headers": ["Host: example.com", "X-NuGet-Session-Id: 5b1d333f-6583-4f78-9c23-d776ebbde5cf", "User-Agent: NuGet Client V3/6.14.3 (LINUX)", "X-NuGet-Client-Version: 6.14.3", "Accept-Encoding: gzip, deflate"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/query?q=docker-compose.yml&skip=0&take=20&prerelease=false&semVerLevel=2.0.0", "headers": ["Host: example.com", "X-NuGet-Session-Id: 23a81a25-0828-4311-8d2d-66f0d5161b6b", "User-Agent: NuGet Client V3/6.14.3 (LINUX)", "X-NuGet-Client-Version: 6.14.3", "Accept-Encoding: gzip, deflate"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/query?q=tools&skip=0&take=20&prerelease=false&semVerLevel=2.0.0", "headers": ["Host: example.com", "X-NuGet-Session-Id: ebf39a79-969d-4ac5-b5a0-5119c2d43ccc", "User-Agent: NuGet Client V3/6.14.3 (LINUX)", "X-NuGet-Client-Version: 6.14.3", "Accept-Encoding: gzip, deflate"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Search()?$filter=IsLatestVersion&searchTerm='python3'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0", "headers": ["Host: example.com", "Accept: application/atom+xml, application/xml", "X-NuGet-Session-Id: 825cbe04-63e7-414a-8394-2ecf74c68211", "User-Agent: NuGet Client V3/6.14.3 (LINUX)", "X-NuGet-Client-Version: 6.14.3", "Accept-Encoding: gzip, deflate"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Search()?$filter=IsLatestVersion&searchTerm='powershell'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0", "headers": ["Host: example.com", "Accept: application/atom+xml, application/xml", "X-NuGet-Session-Id: f61bfff6-1c26-4024-bc17-6b78dcef2edc", "User-Agent: NuGet Client V3/6.14.3 (LINUX)", "X-NuGet-Client-Version: 6.14.3", "Accept-Encoding: gzip, deflate"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Search()?$filter=IsLatestVersion&searchTerm='ls%20-la'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0", "headers": ["Host: example.com", "Accept: application/atom+xml, application/xml", "X-NuGet-Session-Id: e894f984-8abf-4a23-960a-d1a09c94eaa3", "User-Agent: NuGet Client V3/6.14.3 (LINUX)", "X-NuGet-Client-Version: 6.14.3", "Accept-Encoding: gzip, deflate"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Search()?$filter=IsLatestVersion&searchTerm='.htaccess'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0", "headers": ["Host: example.com", "Accept: application/atom+xml, application/xml", "X-NuGet-Session-Id: 33e3aa1b-14cb-4854-821a-c3eb9e5be7ee", "User-Agent: NuGet Client V3/6.14.3 (LINUX)", "X-NuGet-Client-Version: 6.14.3", "Accept-Encoding: gzip, deflate"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Search()?$filter=IsLatestVersion&searchTerm='docker-compose.yml'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0", "headers": ["Host: example.com", "Accept: application/atom+xml, application/xml", "X-NuGet-Session-Id: c478a564-7cf9-4282-9f00-bf5b7966607d", "User-Agent: NuGet Client V3/6.14.3 (LINUX)", "X-NuGet-Client-Version: 6.14.3", "Accept-Encoding: gzip, deflate"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Search()?$filter=IsLatestVersion&searchTerm='tools'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0", "headers": ["Host: example.com", "Accept: application/atom+xml, application/xml", "X-NuGet-Session-Id: 8dfa5583-4005-4134-9343-ddb4c68c125b", "User-Agent: NuGet Client V3/6.14.3 (LINUX)", "X-NuGet-Client-Version: 6.14.3", "Accept-Encoding: gzip, deflate"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/chef/api/v1/search?q=python3&items=9999999&start=0", "headers": ["Accept: application/json", "Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-Remote-Request-Id: e7210054-9f40-4a58-8228-beeab1e3bb05", "X-Chef-Version: 19.4.46", "Host: example.com", "User-Agent: Cinc Client Knife/19.4.46 (ruby-3.4.12-p148; ohai-19.1.40; x86_64-linux; +https://cinc.sh)", "Connection: close"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/chef/api/v1/search?q=powershell&items=9999999&start=0", "headers": ["Accept: application/json", "Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-Remote-Request-Id: 0f3b26a8-7ca2-4b52-a9bb-c23a25c4172c", "X-Chef-Version: 19.4.46", "Host: example.com", "User-Agent: Cinc Client Knife/19.4.46 (ruby-3.4.12-p148; ohai-19.1.40; x86_64-linux; +https://cinc.sh)", "Connection: close"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/chef/api/v1/search?q=ls%20-la&items=9999999&start=0", "headers": ["Accept: application/json", "Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-Remote-Request-Id: d0fa60d5-6948-4149-845f-e38d340cc09a", "X-Chef-Version: 19.4.46", "Host: example.com", "User-Agent: Cinc Client Knife/19.4.46 (ruby-3.4.12-p148; ohai-19.1.40; x86_64-linux; +https://cinc.sh)", "Connection: close"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/chef/api/v1/search?q=.htaccess&items=9999999&start=0", "headers": ["Accept: application/json", "Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-Remote-Request-Id: 8c22d5d2-1691-4488-89ca-a405dd7a15cc", "X-Chef-Version: 19.4.46", "Host: example.com", "User-Agent: Cinc Client Knife/19.4.46 (ruby-3.4.12-p148; ohai-19.1.40; x86_64-linux; +https://cinc.sh)", "Connection: close"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/chef/api/v1/search?q=docker-compose.yml&items=9999999&start=0", "headers": ["Accept: application/json", "Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-Remote-Request-Id: 0a1bd4b1-0009-4d1d-931d-d1c19b26dd8b", "X-Chef-Version: 19.4.46", "Host: example.com", "User-Agent: Cinc Client Knife/19.4.46 (ruby-3.4.12-p148; ohai-19.1.40; x86_64-linux; +https://cinc.sh)", "Connection: close"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/chef/api/v1/search?q=tools&items=9999999&start=0", "headers": ["Accept: application/json", "Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-Remote-Request-Id: f858e4b5-10bc-4633-a517-f165a65ac4cb", "X-Chef-Version: 19.4.46", "Host: example.com", "User-Agent: Cinc Client Knife/19.4.46 (ruby-3.4.12-p148; ohai-19.1.40; x86_64-linux; +https://cinc.sh)", "Connection: close"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/chef/api/v1/cookbooks?q=python3&items=9999999&start=0", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/json"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/chef/api/v1/cookbooks?q=powershell&items=9999999&start=0", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/json"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/chef/api/v1/cookbooks?q=ls%20-la&items=9999999&start=0", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/json"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/chef/api/v1/cookbooks?q=.htaccess&items=9999999&start=0", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/json"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/chef/api/v1/cookbooks?q=docker-compose.yml&items=9999999&start=0", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/json"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/chef/api/v1/cookbooks?q=tools&items=9999999&start=0", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/json"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()?$filter=((((Id%20ne%20null)%20and%20substringof('python3',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('python3',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20python3%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/atom+xml, application/xml"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()?$filter=((((Id%20ne%20null)%20and%20substringof('powershell',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('powershell',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20powershell%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/atom+xml, application/xml"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()?$filter=((((Id%20ne%20null)%20and%20substringof('.htaccess',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('.htaccess',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20.htaccess%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/atom+xml, application/xml"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()?$filter=((((Id%20ne%20null)%20and%20substringof('docker-compose.yml',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('docker-compose.yml',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20docker-compose.yml%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/atom+xml, application/xml"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()?$filter=((((Id%20ne%20null)%20and%20substringof('tools',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('tools',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20tools%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/atom+xml, application/xml"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()?$filter=(((((((Id%20ne%20null)%20and%20substringof('ls',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('ls',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20ls%20',tolower(Tags))))%20or%20((Id%20ne%20null)%20and%20substringof('-la',tolower(Id))))%20or%20((Description%20ne%20null)%20and%20substringof('-la',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20-la%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/atom+xml, application/xml"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()?$filter=tolower(Id)%20eq%20'python3'&$top=30", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/atom+xml, application/xml"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()?$filter=tolower(Id)%20eq%20'powershell'&$top=30", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/atom+xml, application/xml"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()?$filter=tolower(Id)%20eq%20'.htaccess'&$top=30", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/atom+xml, application/xml"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()?$filter=tolower(Id)%20eq%20'docker-compose.yml'&$top=30", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/atom+xml, application/xml"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()?$filter=tolower(Id)%20eq%20'tools'&$top=30", "headers": ["Host: example.com", "User-Agent: curl/8.17.0", "Accept: application/atom+xml, application/xml"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Search()/$count?$filter=IsLatestVersion&searchTerm='python3'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Search()/$count?$filter=IsLatestVersion&searchTerm='powershell'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Search()/$count?$filter=IsLatestVersion&searchTerm='ls%20-la'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Search()/$count?$filter=IsLatestVersion&searchTerm='.htaccess'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Search()/$count?$filter=IsLatestVersion&searchTerm='docker-compose.yml'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Search()/$count?$filter=IsLatestVersion&searchTerm='tools'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()/$count?$filter=((((Id%20ne%20null)%20and%20substringof('python3',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('python3',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20python3%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()/$count?$filter=((((Id%20ne%20null)%20and%20substringof('powershell',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('powershell',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20powershell%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()/$count?$filter=(((((((Id%20ne%20null)%20and%20substringof('ls',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('ls',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20ls%20',tolower(Tags))))%20or%20((Id%20ne%20null)%20and%20substringof('-la',tolower(Id))))%20or%20((Description%20ne%20null)%20and%20substringof('-la',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20-la%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()/$count?$filter=((((Id%20ne%20null)%20and%20substringof('.htaccess',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('.htaccess',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20.htaccess%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()/$count?$filter=((((Id%20ne%20null)%20and%20substringof('docker-compose.yml',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('docker-compose.yml',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20docker-compose.yml%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()/$count?$filter=((((Id%20ne%20null)%20and%20substringof('tools',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('tools',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20tools%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()/$count?$filter=tolower(Id)%20eq%20'python3'&$top=30", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()/$count?$filter=tolower(Id)%20eq%20'powershell'&$top=30", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()/$count?$filter=tolower(Id)%20eq%20'.htaccess'&$top=30", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()/$count?$filter=tolower(Id)%20eq%20'docker-compose.yml'&$top=30", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/api/packages/alice/nuget/Packages()/$count?$filter=tolower(Id)%20eq%20'tools'&$top=30", "headers": ["Host: example.com", "User-Agent: curl/8.18.0", "Accept: */*"], "body": ""}
|
|
||||||
@@ -762,33 +762,3 @@
|
|||||||
{"method": "POST", "target": "/ghost/api/admin/posts/", "headers": ["Host: example.com", "User-Agent: curl/8.11.1", "Accept: */*", "Authorization: Ghost eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjZhZjQ1Y2VjZDI4MWIwMDAwMWE1OTk5YiJ9.eyJpYXQiOjE3OTQ0MDA2OTQsImV4cCI6MTc5NDQwMDk5NCwiYXVkIjoiL2FkbWluLyJ9.ayqaBc1Hg4fbMZj_XbvUGlhloCnW_D5KDMN8A6uM68M", "Accept-Version: v6.76", "Content-Type: application/json", "Content-Length: 465"], "body": "{\"posts\": [{\"title\": \"Older editor format: python3 notes\", \"mobiledoc\": \"{\\\"version\\\": \\\"0.3.1\\\", \\\"atoms\\\": [], \\\"markups\\\": [[\\\"code\\\"]], \\\"cards\\\": [[\\\"code\\\", {\\\"code\\\": \\\"ls -la /var/www\\\\npython3 tidy.py .htaccess\\\", \\\"language\\\": \\\"bash\\\"}]], \\\"sections\\\": [[1, \\\"h2\\\", [[0, [], 0, \\\"Mobiledoc post about powershell\\\"]]], [1, \\\"p\\\", [[0, [], 0, \\\"Run \\\"], [0, [0], 1, \\\"ls -la\\\"], [0, [], 0, \\\" and keep .htaccess tidy.\\\"]]], [10, 0]]}\", \"status\": \"draft\"}]}"}
|
{"method": "POST", "target": "/ghost/api/admin/posts/", "headers": ["Host: example.com", "User-Agent: curl/8.11.1", "Accept: */*", "Authorization: Ghost eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjZhZjQ1Y2VjZDI4MWIwMDAwMWE1OTk5YiJ9.eyJpYXQiOjE3OTQ0MDA2OTQsImV4cCI6MTc5NDQwMDk5NCwiYXVkIjoiL2FkbWluLyJ9.ayqaBc1Hg4fbMZj_XbvUGlhloCnW_D5KDMN8A6uM68M", "Accept-Version: v6.76", "Content-Type: application/json", "Content-Length: 465"], "body": "{\"posts\": [{\"title\": \"Older editor format: python3 notes\", \"mobiledoc\": \"{\\\"version\\\": \\\"0.3.1\\\", \\\"atoms\\\": [], \\\"markups\\\": [[\\\"code\\\"]], \\\"cards\\\": [[\\\"code\\\", {\\\"code\\\": \\\"ls -la /var/www\\\\npython3 tidy.py .htaccess\\\", \\\"language\\\": \\\"bash\\\"}]], \\\"sections\\\": [[1, \\\"h2\\\", [[0, [], 0, \\\"Mobiledoc post about powershell\\\"]]], [1, \\\"p\\\", [[0, [], 0, \\\"Run \\\"], [0, [0], 1, \\\"ls -la\\\"], [0, [], 0, \\\" and keep .htaccess tidy.\\\"]]], [10, 0]]}\", \"status\": \"draft\"}]}"}
|
||||||
{"method": "POST", "target": "/ghost/api/admin/snippets/?formats=mobiledoc%2Clexical", "headers": ["Host: example.com", "Content-Length: 519", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A8kdYjooVavGjq0rNroahDCvgTG3XukhZ.ioil5%2Fg42yqQcdl%2B%2FlhvXc4HoOV6hGDetkdr5aPXvuo"], "body": "{\"snippets\":[{\"name\":\"python3 & .htaccess tip\",\"mobiledoc\":\"{}\",\"lexical\": \"{\\\"root\\\": {\\\"children\\\": [{\\\"children\\\": [{\\\"detail\\\": 0, \\\"format\\\": 0, \\\"mode\\\": \\\"normal\\\", \\\"style\\\": \\\"\\\", \\\"text\\\": \\\"Run ls -la, then python3 tidy.py .htaccess; on Windows use powershell.\\\", \\\"type\\\": \\\"extended-text\\\", \\\"version\\\": 1}], \\\"direction\\\": \\\"ltr\\\", \\\"format\\\": \\\"\\\", \\\"indent\\\": 0, \\\"type\\\": \\\"paragraph\\\", \\\"version\\\": 1}], \\\"direction\\\": \\\"ltr\\\", \\\"format\\\": \\\"\\\", \\\"indent\\\": 0, \\\"type\\\": \\\"root\\\", \\\"version\\\": 1}}\"}]}"}
|
{"method": "POST", "target": "/ghost/api/admin/snippets/?formats=mobiledoc%2Clexical", "headers": ["Host: example.com", "Content-Length: 519", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A8kdYjooVavGjq0rNroahDCvgTG3XukhZ.ioil5%2Fg42yqQcdl%2B%2FlhvXc4HoOV6hGDetkdr5aPXvuo"], "body": "{\"snippets\":[{\"name\":\"python3 & .htaccess tip\",\"mobiledoc\":\"{}\",\"lexical\": \"{\\\"root\\\": {\\\"children\\\": [{\\\"children\\\": [{\\\"detail\\\": 0, \\\"format\\\": 0, \\\"mode\\\": \\\"normal\\\", \\\"style\\\": \\\"\\\", \\\"text\\\": \\\"Run ls -la, then python3 tidy.py .htaccess; on Windows use powershell.\\\", \\\"type\\\": \\\"extended-text\\\", \\\"version\\\": 1}], \\\"direction\\\": \\\"ltr\\\", \\\"format\\\": \\\"\\\", \\\"indent\\\": 0, \\\"type\\\": \\\"paragraph\\\", \\\"version\\\": 1}], \\\"direction\\\": \\\"ltr\\\", \\\"format\\\": \\\"\\\", \\\"indent\\\": 0, \\\"type\\\": \\\"root\\\", \\\"version\\\": 1}}\"}]}"}
|
||||||
{"method": "POST", "target": "/ghost/api/admin/themes/upload/", "headers": ["Host: example.com", "Content-Length: 2138", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryzhPTWHSPscackPlN", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A8kdYjooVavGjq0rNroahDCvgTG3XukhZ.ioil5%2Fg42yqQcdl%2B%2FlhvXc4HoOV6hGDetkdr5aPXvuo"], "body": "------WebKitFormBoundaryzhPTWHSPscackPlN\r\nContent-Disposition: form-data; name=\"file\"; filename=\"notes-theme.zip\"\r\nContent-Type: application/zip\r\n\r\nUEsDBAoAAAAAAM9la10AAAAAAAAAAAAAAAAMABwAbm90ZXMtdGhlbWUvVVQJAAOmY/RqpmP0anV4CwABBOgDAAAE6AMAAFBLAwQUAAAACADPZWtdXfnn43sAAACmAAAAFQAcAG5vdGVzLXRoZW1lL2luZGV4Lmhic1VUCQADpmP0aqZj9Gp1eAsAAQToAwAABOgDAAAtjlEKAyEMRP/3FHZ7AKHfqVcpwcYqWF00C4WQu1dtP+fNZCYiFzBPCnhmVt3gjak4kWuojdBHc9TOXRWwcfKZjM/Y+30XmcZjKdXdQbw5QBMbhWmeLU8qwokzjXOLDuzIiNDHUzt4sV/ngPY/pzqK8ZUKcqplZtY/2xdQSwMECgAAAAAAz2VrXQAAAAAAAAAAAAAAABUAHABub3Rlcy10aGVtZS9wYXJ0aWFscy9VVAkAA6Zj9GqmY/RqdXgLAAEE6AMAAAToAwAAUEsDBBQAAAAIAM9la11tuH02lQAAAPgAAAAUABwAbm90ZXMtdGhlbWUvcGFnZS5oYnNVVAkAA6Zj9GqmY/RqdXgLAAEE6AMAAAToAwAAXU+7DsMgDNz7FZTMVdSd5lcQoiaxBEkFzmTx77XTdGi3e+l0x3x15gkp7Jl6vzAPr60JcqESxgwm5tDawzKr7g/Wu53ccp+YCSmDhMeDDZhMgkB7BY8lzOpgmU2rUQsE+r3m/4g1IZP6P7oPusdK64hJehpEwm39zpmXW9xWgpU0ckIdcuYmN54HDrsUsWW3tH3uXd5QSwMEFAAAAAgAz2VrXY4bR/y4AAAABwEAABgAHABub3Rlcy10aGVtZS9wYWNrYWdlLmpzb25VVAkAA6Zj9GqmY/RqdXgLAAEE6AMAAAToAwAALY/BbsIwDIbvPIWVM6uKEJdJm+BJKis1baTEjuIAG6jvjpPtZvv7/Vl+OcZE7hMcSyX9qCtZuwc3k/oScg3CjV5AE8YIncNVCvQ8CEP+ravwEZBnyPKgoivF2Bx3q//3D8M4jG6/A0e8BCa14cstq2ht+Pvr1AObbeHNfKVzShhi4xiDpzP9YMqRBi/Jkubywtew9Gg2k06ZypRxaQ8dRnOFZM2k4fl3sOk9lnlCVaptVMuNtm33BlBLAwQUAAAACADPZWtdbbh9NpUAAAD4AAAAFAAcAG5vdGVzLXRoZW1lL3Bvc3QuaGJzVVQJAAOmY/RqpmP0anV4CwABBOgDAAAE6AMAAF1Puw7DIAzc+xWUzFXUneZXEKImsQRJBc5k8e+103Rot3vpdMd8deYJKeyZer8wD6+tCXKhEsYMJubQ2sMyq+4P1rud3HKfmAkpg4THgw2YTIJAewWPJczqYJlNq1ELBPq95v+INSGT+j+6D7rHSuuISXoaRMJt/c6Zl1vcVoKVNHJCHXLmJjeeBw67FLFlt7R97l3eUEsDBBQAAAAIAM9la12WkoJXiwAAAMEAAAAXABwAbm90ZXMtdGhlbWUvZGVmYXVsdC5oYnNVVAkAA6Zj9GqmY/RqdXgLAAEE6AMAAAToAwAANY6xDsIwDET3fkXIDqwMboQEzDCwMEWmdZtKKZGIGVDkf6cO6mTdO92dYXO+nu6P28UEnqNrQI+J+BpbW8oxT0y7mDqMJGIdBMLewUyMpgv4zsSt/fCwPSweTxzJlaKur0IE9isdQ8rsNa+09jTwTP3XdBFz1jVVvirdKn8gImt4SIk1rNgtHfXhH1BLAQIeAwoAAAAAAM9la10AAAAAAAAAAAAAAAAMABgAAAAAAAAAEAD9QQAAAABub3Rlcy10aGVtZS9VVAUAA6Zj9Gp1eAsAAQToAwAABOgDAABQSwECHgMUAAAACADPZWtdXfnn43sAAACmAAAAFQAYAAAAAAABAAAAtIFGAAAAbm90ZXMtdGhlbWUvaW5kZXguaGJzVVQFAAOmY/RqdXgLAAEE6AMAAAToAwAAUEsBAh4DCgAAAAAAz2VrXQAAAAAAAAAAAAAAABUAGAAAAAAAAAAQAP1BEAEAAG5vdGVzLXRoZW1lL3BhcnRpYWxzL1VUBQADpmP0anV4CwABBOgDAAAE6AMAAFBLAQIeAxQAAAAIAM9la11tuH02lQAAAPgAAAAUABgAAAAAAAEAAAC0gV8BAABub3Rlcy10aGVtZS9wYWdlLmhic1VUBQADpmP0anV4CwABBOgDAAAE6AMAAFBLAQIeAxQAAAAIAM9la12OG0f8uAAAAAcBAAAYABgAAAAAAAEAAAC0gUICAABub3Rlcy10aGVtZS9wYWNrYWdlLmpzb25VVAUAA6Zj9Gp1eAsAAQToAwAABOgDAABQSwECHgMUAAAACADPZWtdbbh9NpUAAAD4AAAAFAAYAAAAAAABAAAAtIFMAwAAbm90ZXMtdGhlbWUvcG9zdC5oYnNVVAUAA6Zj9Gp1eAsAAQToAwAABOgDAABQSwECHgMUAAAACADPZWtdlpKCV4sAAADBAAAAFwAYAAAAAAABAAAAtIEvBAAAbm90ZXMtdGhlbWUvZGVmYXVsdC5oYnNVVAUAA6Zj9Gp1eAsAAQToAwAABOgDAABQSwUGAAAAAAcABwB3AgAACwUAAAAA\r\n------WebKitFormBoundaryzhPTWHSPscackPlN--\r\n"}
|
{"method": "POST", "target": "/ghost/api/admin/themes/upload/", "headers": ["Host: example.com", "Content-Length: 2138", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryzhPTWHSPscackPlN", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A8kdYjooVavGjq0rNroahDCvgTG3XukhZ.ioil5%2Fg42yqQcdl%2B%2FlhvXc4HoOV6hGDetkdr5aPXvuo"], "body": "------WebKitFormBoundaryzhPTWHSPscackPlN\r\nContent-Disposition: form-data; name=\"file\"; filename=\"notes-theme.zip\"\r\nContent-Type: application/zip\r\n\r\nUEsDBAoAAAAAAM9la10AAAAAAAAAAAAAAAAMABwAbm90ZXMtdGhlbWUvVVQJAAOmY/RqpmP0anV4CwABBOgDAAAE6AMAAFBLAwQUAAAACADPZWtdXfnn43sAAACmAAAAFQAcAG5vdGVzLXRoZW1lL2luZGV4Lmhic1VUCQADpmP0aqZj9Gp1eAsAAQToAwAABOgDAAAtjlEKAyEMRP/3FHZ7AKHfqVcpwcYqWF00C4WQu1dtP+fNZCYiFzBPCnhmVt3gjak4kWuojdBHc9TOXRWwcfKZjM/Y+30XmcZjKdXdQbw5QBMbhWmeLU8qwokzjXOLDuzIiNDHUzt4sV/ngPY/pzqK8ZUKcqplZtY/2xdQSwMECgAAAAAAz2VrXQAAAAAAAAAAAAAAABUAHABub3Rlcy10aGVtZS9wYXJ0aWFscy9VVAkAA6Zj9GqmY/RqdXgLAAEE6AMAAAToAwAAUEsDBBQAAAAIAM9la11tuH02lQAAAPgAAAAUABwAbm90ZXMtdGhlbWUvcGFnZS5oYnNVVAkAA6Zj9GqmY/RqdXgLAAEE6AMAAAToAwAAXU+7DsMgDNz7FZTMVdSd5lcQoiaxBEkFzmTx77XTdGi3e+l0x3x15gkp7Jl6vzAPr60JcqESxgwm5tDawzKr7g/Wu53ccp+YCSmDhMeDDZhMgkB7BY8lzOpgmU2rUQsE+r3m/4g1IZP6P7oPusdK64hJehpEwm39zpmXW9xWgpU0ckIdcuYmN54HDrsUsWW3tH3uXd5QSwMEFAAAAAgAz2VrXY4bR/y4AAAABwEAABgAHABub3Rlcy10aGVtZS9wYWNrYWdlLmpzb25VVAkAA6Zj9GqmY/RqdXgLAAEE6AMAAAToAwAALY/BbsIwDIbvPIWVM6uKEJdJm+BJKis1baTEjuIAG6jvjpPtZvv7/Vl+OcZE7hMcSyX9qCtZuwc3k/oScg3CjV5AE8YIncNVCvQ8CEP+ravwEZBnyPKgoivF2Bx3q//3D8M4jG6/A0e8BCa14cstq2ht+Pvr1AObbeHNfKVzShhi4xiDpzP9YMqRBi/Jkubywtew9Gg2k06ZypRxaQ8dRnOFZM2k4fl3sOk9lnlCVaptVMuNtm33BlBLAwQUAAAACADPZWtdbbh9NpUAAAD4AAAAFAAcAG5vdGVzLXRoZW1lL3Bvc3QuaGJzVVQJAAOmY/RqpmP0anV4CwABBOgDAAAE6AMAAF1Puw7DIAzc+xWUzFXUneZXEKImsQRJBc5k8e+103Rot3vpdMd8deYJKeyZer8wD6+tCXKhEsYMJubQ2sMyq+4P1rud3HKfmAkpg4THgw2YTIJAewWPJczqYJlNq1ELBPq95v+INSGT+j+6D7rHSuuISXoaRMJt/c6Zl1vcVoKVNHJCHXLmJjeeBw67FLFlt7R97l3eUEsDBBQAAAAIAM9la12WkoJXiwAAAMEAAAAXABwAbm90ZXMtdGhlbWUvZGVmYXVsdC5oYnNVVAkAA6Zj9GqmY/RqdXgLAAEE6AMAAAToAwAANY6xDsIwDET3fkXIDqwMboQEzDCwMEWmdZtKKZGIGVDkf6cO6mTdO92dYXO+nu6P28UEnqNrQI+J+BpbW8oxT0y7mDqMJGIdBMLewUyMpgv4zsSt/fCwPSweTxzJlaKur0IE9isdQ8rsNa+09jTwTP3XdBFz1jVVvirdKn8gImt4SIk1rNgtHfXhH1BLAQIeAwoAAAAAAM9la10AAAAAAAAAAAAAAAAMABgAAAAAAAAAEAD9QQAAAABub3Rlcy10aGVtZS9VVAUAA6Zj9Gp1eAsAAQToAwAABOgDAABQSwECHgMUAAAACADPZWtdXfnn43sAAACmAAAAFQAYAAAAAAABAAAAtIFGAAAAbm90ZXMtdGhlbWUvaW5kZXguaGJzVVQFAAOmY/RqdXgLAAEE6AMAAAToAwAAUEsBAh4DCgAAAAAAz2VrXQAAAAAAAAAAAAAAABUAGAAAAAAAAAAQAP1BEAEAAG5vdGVzLXRoZW1lL3BhcnRpYWxzL1VUBQADpmP0anV4CwABBOgDAAAE6AMAAFBLAQIeAxQAAAAIAM9la11tuH02lQAAAPgAAAAUABgAAAAAAAEAAAC0gV8BAABub3Rlcy10aGVtZS9wYWdlLmhic1VUBQADpmP0anV4CwABBOgDAAAE6AMAAFBLAQIeAxQAAAAIAM9la12OG0f8uAAAAAcBAAAYABgAAAAAAAEAAAC0gUICAABub3Rlcy10aGVtZS9wYWNrYWdlLmpzb25VVAUAA6Zj9Gp1eAsAAQToAwAABOgDAABQSwECHgMUAAAACADPZWtdbbh9NpUAAAD4AAAAFAAYAAAAAAABAAAAtIFMAwAAbm90ZXMtdGhlbWUvcG9zdC5oYnNVVAUAA6Zj9Gp1eAsAAQToAwAABOgDAABQSwECHgMUAAAACADPZWtdlpKCV4sAAADBAAAAFwAYAAAAAAABAAAAtIEvBAAAbm90ZXMtdGhlbWUvZGVmYXVsdC5oYnNVVAUAA6Zj9Gp1eAsAAQToAwAABOgDAABQSwUGAAAAAAcABwB3AgAACwUAAAAA\r\n------WebKitFormBoundaryzhPTWHSPscackPlN--\r\n"}
|
||||||
# The requests below were sent to Ghost by a script with the admin's or a
|
|
||||||
# member's headers. They hold words that name commands and files in more fields.
|
|
||||||
{"method": "POST", "target": "/ghost/api/admin/authentication/setup/", "headers": ["Host: example.com", "Content-Length: 158", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate"], "body": "{\"setup\":[{\"blogTitle\":\"Notes on python3, powershell & .htaccess\",\"name\":\"Alice ls -la Example\",\"email\":\"alice@example.com\",\"password\":\"Hv4-mQz8-Lr2x-Tp9w\"}]}"}
|
|
||||||
{"method": "POST", "target": "/ghost/api/admin/session/", "headers": ["Host: example.com", "Content-Length: 64", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate"], "body": "{\"username\":\"alice@example.com\",\"password\":\"Hv4-mQz8-Lr2x-Tp9w\"}"}
|
|
||||||
{"method": "PUT", "target": "/ghost/api/admin/users/6af54436a509340001efcee2/?include=roles", "headers": ["Host: example.com", "Content-Length: 1256", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": "{\"users\":[{\"id\":\"6af54436a509340001efcee2\",\"name\":\"Alice python3 & powershell\",\"slug\":\"alice-python3-powershell\",\"email\":\"alice@example.com\",\"profile_image\":null,\"cover_image\":null,\"bio\":\"I write about ls -la, python3, powershell and .htaccess files.\",\"website\":\"https://example.com/python3/powershell\",\"location\":\"Berlin & the python3 meetup\",\"facebook\":null,\"twitter\":null,\"threads\":null,\"bluesky\":null,\"mastodon\":null,\"tiktok\":null,\"youtube\":null,\"instagram\":null,\"linkedin\":null,\"accessibility\":\"{\\\"nightShift\\\":\\\"system\\\"}\",\"status\":\"active\",\"meta_title\":null,\"meta_description\":null,\"tour\":null,\"last_seen\":\"2026-11-12T04:44:42.000Z\",\"comment_notifications\":true,\"free_member_signup_notification\":true,\"paid_subscription_started_notification\":true,\"paid_subscription_canceled_notification\":false,\"mention_notifications\":true,\"recommendation_notifications\":true,\"milestone_notifications\":true,\"donation_notifications\":true,\"gift_subscription_notifications\":true,\"created_at\":\"2026-11-12T04:44:38.000Z\",\"updated_at\":\"2026-11-12T04:44:42.000Z\",\"roles\":[{\"id\":\"6af54436a509340001efcee7\",\"name\":\"Owner\",\"description\":\"Blog Owner\",\"created_at\":\"2026-11-12T04:44:38.000Z\",\"updated_at\":\"2026-11-12T04:44:38.000Z\"}],\"url\":\"http://example.com/author/alice/\"}]}"}
|
|
||||||
{"method": "POST", "target": "/ghost/api/admin/invites/", "headers": ["Host: example.com", "Content-Length: 130", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": "{\"invites\":[{\"email\":\"erin.htaccess@example.com\",\"role_id\":\"6af54436a509340001efcee6\",\"expires\":null,\"status\":null,\"token\":null}]}"}
|
|
||||||
{"method": "POST", "target": "/ghost/api/admin/invites/", "headers": ["Host: example.com", "Content-Length: 128", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": "{\"invites\":[{\"email\":\"python3.dev@example.com\",\"role_id\":\"6af54436a509340001efcee6\",\"expires\":null,\"status\":null,\"token\":null}]}"}
|
|
||||||
{"method": "POST", "target": "/ghost/api/admin/invites/", "headers": ["Host: example.com", "Content-Length: 132", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": "{\"invites\":[{\"email\":\"dave.powershell@example.com\",\"role_id\":\"6af54436a509340001efcee6\",\"expires\":null,\"status\":null,\"token\":null}]}"}
|
|
||||||
{"method": "GET", "target": "/ghost/api/admin/authentication/invitation/?email=erin.htaccess%40example.com", "headers": ["Host: example.com", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "app-pragma: no-cache", "Accept: application/json, text/plain, */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate"], "body": ""}
|
|
||||||
{"method": "POST", "target": "/ghost/api/admin/authentication/invitation/", "headers": ["Host: example.com", "Content-Length: 243", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate"], "body": "{\"invitation\":[{\"token\":\"MTc5NTA2MzQ4MjU5MnxlcmluLmh0YWNjZXNzQGV4YW1wbGUuY29tfDUxbkp4aWJ4SzA4NzN0eUtIQWl5YzhyWFhKTjA0aWJ1ZDExendTVHQrSU09\",\"name\":\"Erin python3 & .htaccess\",\"password\":\"Rk7-Wq2z-Np5c-Lx8v\",\"email\":\"erin.htaccess@example.com\"}]}"}
|
|
||||||
{"method": "PUT", "target": "/ghost/api/admin/tiers/6af54437a509340001efcef1/", "headers": ["Host: example.com", "Content-Length: 474", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": "{\"tiers\":[{\"id\":\"6af54437a509340001efcef1\",\"name\":\"python3 & powershell club\",\"description\":\"Weekly ls -la and .htaccess tips\",\"slug\":\"default-product\",\"active\":true,\"type\":\"paid\",\"welcome_page_url\":null,\"created_at\":\"2026-11-12T04:44:39.000Z\",\"updated_at\":\"2026-11-12T04:44:39.000Z\",\"visibility\":\"public\",\"benefits\":[\"python3 scripts\",\"powershell recipes\",\"ls -la cheat sheet\",\".htaccess examples\"],\"currency\":\"USD\",\"monthly_price\":500,\"yearly_price\":5000,\"trial_days\":0}]}"}
|
|
||||||
{"method": "POST", "target": "/ghost/api/admin/offers/", "headers": ["Host: example.com", "Content-Length: 391", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": "{\"offers\":[{\"name\":\"python3 launch\",\"code\":\"python3-launch\",\"display_title\":\"powershell & python3 deal\",\"display_description\":\"Half off for ls -la and .htaccess fans\",\"type\":\"percent\",\"cadence\":\"month\",\"amount\":50,\"duration\":\"once\",\"duration_in_months\":null,\"currency_restriction\":false,\"currency\":null,\"status\":\"active\",\"redemption_type\":\"signup\",\"tier\":{\"id\":\"6af54437a509340001efcef1\"}}]}"}
|
|
||||||
{"method": "POST", "target": "/ghost/api/admin/recommendations/", "headers": ["Host: example.com", "Content-Length: 288", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": "{\"recommendations\":[{\"title\":\"python3 & powershell weekly\",\"description\":\"Tips on ls -la and .htaccess files\",\"excerpt\":\"A newsletter about python3, powershell, ls -la and .htaccess.\",\"featured_image\":null,\"favicon\":null,\"url\":\"https://example.com/python3/\",\"one_click_subscribe\":false}]}"}
|
|
||||||
{"method": "POST", "target": "/ghost/api/admin/integrations/?include=api_keys%2Cwebhooks", "headers": ["Host: example.com", "Content-Length: 55", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": "{\"integrations\":[{\"name\":\"python3 & powershell sync\"}]}"}
|
|
||||||
{"method": "PUT", "target": "/ghost/api/admin/integrations/6af5443aa509340001efcfc6/?include=api_keys%2Cwebhooks", "headers": ["Host: example.com", "Content-Length: 128", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": "{\"integrations\":[{\"name\":\"python3 & powershell sync\",\"description\":\"Runs ls -la and tidies .htaccess files\",\"icon_image\":null}]}"}
|
|
||||||
{"method": "POST", "target": "/ghost/api/admin/webhooks/", "headers": ["Host: example.com", "Content-Length: 202", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": "{\"webhooks\":[{\"event\":\"post.published\",\"target_url\":\"https://example.com/hooks/python3\",\"name\":\"python3 & powershell hook\",\"secret\":null,\"api_version\":\"v6\",\"integration_id\":\"6af5443aa509340001efcfc6\"}]}"}
|
|
||||||
{"method": "POST", "target": "/ghost/api/admin/tags/?include=count.posts", "headers": ["Host: example.com", "Content-Length: 702", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3AWHEHmHzgM_62oH0uUMxh6TgpQCaauWmB.h%2FuJNd7nfwPSaFGmMarZNVguG8U1ZIIJsaLFE4%2BqXMA"], "body": "{\"tags\":[{\"name\":\"Shell\",\"slug\":\"shell\",\"description\":null,\"feature_image\":null,\"meta_title\":\"python3 & powershell tips\",\"meta_description\":\"Run ls -la and tidy .htaccess files with python3.\",\"canonical_url\":\"https://example.com/tag/powershell/\",\"twitter_image\":null,\"twitter_title\":\"powershell & python3\",\"twitter_description\":\"ls -la, .htaccess and more\",\"og_image\":null,\"og_title\":\"python3 & powershell\",\"og_description\":\"ls -la and .htaccess tips\",\"codeinjection_head\":\"<meta name=\\\"google-site-verification\\\" content=\\\"x\\\"><link rel=\\\"preconnect\\\" href=\\\"https://fonts.example.com\\\">\",\"codeinjection_foot\":\"<style>.tag-python3 { color: #333; }</style>\",\"accent_color\":null,\"visibility\":\"public\"}]}"}
|
|
||||||
{"method": "PUT", "target": "/ghost/api/admin/posts/6af5443ba509340001efcfd1/?formats=mobiledoc%2Clexical", "headers": ["Host: example.com", "Content-Length: 315", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": "{\"posts\":[{\"updated_at\":\"2026-11-12T04:44:43.000Z\",\"title\":\"Shell notes\",\"feature_image\":\"https://images.example.com/terminal.png\",\"feature_image_alt\":\"A terminal running ls -la on .htaccess with python3\",\"feature_image_caption\":\"python3 & powershell, photo by <a href=\\\"https://example.com/ls-la\\\">Erin</a>\"}]}"}
|
|
||||||
{"method": "POST", "target": "/ghost/api/admin/members/upload/", "headers": ["Host: example.com", "Content-Length: 990", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: multipart/form-data; boundary=----WebKitFormBoundaryq3Lm8vXc2TzR7wKd", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": "------WebKitFormBoundaryq3Lm8vXc2TzR7wKd\r\nContent-Disposition: form-data; name=\"membersfile\"; filename=\"members.csv\"\r\nContent-Type: text/csv\r\n\r\nemail,name,note,labels\r\npython3.dev@example.com,Grace python3,Asked about ls -la,powershell\r\nerin.htaccess@example.com,Erin,Tidies .htaccess files,python3\r\n\r\n------WebKitFormBoundaryq3Lm8vXc2TzR7wKd\r\nContent-Disposition: form-data; name=\"labels\"\r\n\r\npython3 import\r\n------WebKitFormBoundaryq3Lm8vXc2TzR7wKd\r\nContent-Disposition: form-data; name=\"labels\"\r\n\r\npowershell & .htaccess\r\n------WebKitFormBoundaryq3Lm8vXc2TzR7wKd\r\nContent-Disposition: form-data; name=\"mapping[email]\"\r\n\r\nemail\r\n------WebKitFormBoundaryq3Lm8vXc2TzR7wKd\r\nContent-Disposition: form-data; name=\"mapping[name]\"\r\n\r\nname\r\n------WebKitFormBoundaryq3Lm8vXc2TzR7wKd\r\nContent-Disposition: form-data; name=\"mapping[note]\"\r\n\r\nnote\r\n------WebKitFormBoundaryq3Lm8vXc2TzR7wKd\r\nContent-Disposition: form-data; name=\"mapping[labels]\"\r\n\r\nlabels\r\n------WebKitFormBoundaryq3Lm8vXc2TzR7wKd--\r\n"}
|
|
||||||
{"method": "GET", "target": "/ghost/api/admin/stats/top-content/?date_from=2026-10-13&date_to=2026-11-12&timezone=Etc%2FUTC&member_status=all&pathname=%2Fpython3-and-powershell%2F", "headers": ["Host: example.com", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "app-pragma: no-cache", "Accept: application/json, text/plain, */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/ghost/api/admin/stats/top-content/?date_from=2026-10-13&date_to=2026-11-12&timezone=Etc%2FUTC&member_status=all&source=powershell.example.com", "headers": ["Host: example.com", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "app-pragma: no-cache", "Accept: application/json, text/plain, */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/ghost/api/admin/stats/top-content/?date_from=2026-10-13&date_to=2026-11-12&timezone=Etc%2FUTC&member_status=all&utm_campaign=powershell+%26+python3", "headers": ["Host: example.com", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "app-pragma: no-cache", "Accept: application/json, text/plain, */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": ""}
|
|
||||||
{"method": "PUT", "target": "/ghost/api/admin/posts/6af5443ba509340001efcfd1/?formats=mobiledoc%2Clexical&newsletter=default-newsletter&email_segment=label%3Apowershell-users", "headers": ["Host: example.com", "Content-Length: 74", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "content-type: application/json", "app-pragma: no-cache", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": "{\"posts\":[{\"updated_at\":\"2026-11-12T04:44:43.000Z\",\"status\":\"published\"}]}"}
|
|
||||||
{"method": "GET", "target": "/ghost/api/admin/posts/?filter=status%3Apublished%2Btitle%3A%7E%27powershell+%26+python3%27&limit=25&fields=id%2Ctitle&order=published_at+DESC", "headers": ["Host: example.com", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "app-pragma: no-cache", "Accept: application/json, text/plain, */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/ghost/api/admin/posts/?filter=status%3Apublished%2Btitle%3A%7E%27ls+-la+%26+.htaccess%27&limit=25&fields=id%2Ctitle&order=published_at+DESC", "headers": ["Host: example.com", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "app-pragma: no-cache", "Accept: application/json, text/plain, */*", "Origin: http://example.com", "Referer: http://example.com/ghost/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-admin-api-session=s%3A0oed4vmdPUacOz5202Gz4DuzSj2EdRr8.yDcWOi%2FjeAs2xhgQ%2Brrm6eEUWSpZp3YslrhATMDAtCs"], "body": ""}
|
|
||||||
{"method": "POST", "target": "/members/api/comments/", "headers": ["Host: example.com", "Content-Length: 161", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "Content-Type: application/json", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-members-ssr=5174dad7-2125-4e37-bb05-2aac35f959e9; ghost-members-ssr.sig=kPwob0aJ4RTyxR4cixOd33LpDNg"], "body": "{\"comments\":[{\"post_id\":\"6af5443ba509340001efcfd1\",\"status\":\"published\",\"html\":\"<p>Thanks! I ran <code>ls -la</code> and fixed my .htaccess with python3.</p>\"}]}"}
|
|
||||||
{"method": "PUT", "target": "/members/api/comments/6af5443ba509340001efd00b/", "headers": ["Host: example.com", "Content-Length: 140", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "Content-Type: application/json", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-members-ssr=5174dad7-2125-4e37-bb05-2aac35f959e9; ghost-members-ssr.sig=kPwob0aJ4RTyxR4cixOd33LpDNg"], "body": "{\"comments\":[{\"id\":\"6af5443ba509340001efd00b\",\"html\":\"<p>Thanks! I ran ls -la, then powershell, and fixed my .htaccess with python3.</p>\"}]}"}
|
|
||||||
{"method": "POST", "target": "/members/api/comments/", "headers": ["Host: example.com", "Content-Length: 220", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "Content-Type: application/json", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-members-ssr=5174dad7-2125-4e37-bb05-2aac35f959e9; ghost-members-ssr.sig=kPwob0aJ4RTyxR4cixOd33LpDNg"], "body": "{\"comments\":[{\"post_id\":\"6af5443ba509340001efcfd1\",\"parent_id\":\"6af5443ba509340001efd00b\",\"in_reply_to_id\":\"6af5443ba509340001efd00b\",\"status\":\"published\",\"html\":\"<p>Same here: python3 & powershell both work.</p>\"}]}"}
|
|
||||||
{"method": "GET", "target": "/members/api/comments/post/6af5443ba509340001efcfd1/?limit=20&filter=created_at%3A%3C%3D2026-11-12T04%3A44%3A43.000Z&page=2&order=created_at+desc", "headers": ["Host: example.com", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-members-ssr=5174dad7-2125-4e37-bb05-2aac35f959e9; ghost-members-ssr.sig=kPwob0aJ4RTyxR4cixOd33LpDNg"], "body": ""}
|
|
||||||
{"method": "GET", "target": "/members/api/comments/6af5443ba509340001efd00b/replies/?limit=5&filter=id%3A%3E%276af5443ba509340001efd010%27", "headers": ["Host: example.com", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.8078.4 Safari/537.36", "Accept: */*", "Origin: http://example.com", "Referer: http://example.com/", "Accept-Encoding: gzip, deflate", "Cookie: ghost-members-ssr=5174dad7-2125-4e37-bb05-2aac35f959e9; ghost-members-ssr.sig=kPwob0aJ4RTyxR4cixOd33LpDNg"], "body": ""}
|
|
||||||
-161
@@ -381,167 +381,6 @@
|
|||||||
/org/acme/pulls/developers?type=your_repositories&sort=recentupdate&state=open&q=SELECT%20%2A%20FROM%20users%20WHERE&search_mode=exact
|
/org/acme/pulls/developers?type=your_repositories&sort=recentupdate&state=open&q=SELECT%20%2A%20FROM%20users%20WHERE&search_mode=exact
|
||||||
/alice/tools/milestone/1?q=SELECT%20%2A%20FROM%20users%20WHERE
|
/alice/tools/milestone/1?q=SELECT%20%2A%20FROM%20users%20WHERE
|
||||||
|
|
||||||
# The API's searches that gitea and Forgejo share, sent with curl to gitea
|
|
||||||
# 1.27.3. Each searches for words that name commands and files, such as python3
|
|
||||||
# and .htaccess, and for plain words. A package name can hold a slash, as npm's
|
|
||||||
# @alice/tools-cli does, so the search of packages is also sent /bin/sh. The
|
|
||||||
# search of unadopted repositories takes an owner and a name.
|
|
||||||
/api/v1/repos/search?q=python3&limit=10
|
|
||||||
/api/v1/repos/search?q=powershell&limit=10
|
|
||||||
/api/v1/repos/search?q=ls%20-la&limit=10
|
|
||||||
/api/v1/repos/search?q=.htaccess&limit=10
|
|
||||||
/api/v1/repos/search?q=docker-compose.yml&limit=10
|
|
||||||
/api/v1/repos/search?q=tools&limit=10
|
|
||||||
/api/v1/users/search?q=python3
|
|
||||||
/api/v1/users/search?q=powershell
|
|
||||||
/api/v1/users/search?q=ls%20-la
|
|
||||||
/api/v1/users/search?q=.htaccess
|
|
||||||
/api/v1/users/search?q=bob
|
|
||||||
/api/v1/repos/issues/search?q=python3&type=issues&state=open
|
|
||||||
/api/v1/repos/issues/search?q=powershell&type=issues&state=open
|
|
||||||
/api/v1/repos/issues/search?q=ls%20-la&type=issues&state=open
|
|
||||||
/api/v1/repos/issues/search?q=.htaccess&type=issues&state=open
|
|
||||||
/api/v1/repos/issues/search?q=docker-compose.yml&type=issues&state=open
|
|
||||||
/api/v1/repos/issues/search?q=build&type=issues&state=open
|
|
||||||
/api/v1/repos/alice/tools/issues?state=open&q=python3
|
|
||||||
/api/v1/repos/alice/tools/issues?state=open&q=powershell
|
|
||||||
/api/v1/repos/alice/tools/issues?state=open&q=ls%20-la
|
|
||||||
/api/v1/repos/alice/tools/issues?state=open&q=.htaccess
|
|
||||||
/api/v1/repos/alice/tools/issues?state=open&q=docker-compose.yml
|
|
||||||
/api/v1/packages/alice?q=python3
|
|
||||||
/api/v1/packages/alice?q=powershell
|
|
||||||
/api/v1/packages/alice?q=ls%20-la
|
|
||||||
/api/v1/packages/alice?q=.htaccess
|
|
||||||
/api/v1/packages/alice?q=%2Fbin%2Fsh
|
|
||||||
/api/v1/packages/alice?q=install-script
|
|
||||||
/api/v1/packages/alice?q=%40alice%2Ftools-cli
|
|
||||||
/api/v1/topics/search?q=python3
|
|
||||||
/api/v1/topics/search?q=powershell
|
|
||||||
/api/v1/topics/search?q=ls%20-la
|
|
||||||
/api/v1/topics/search?q=.htaccess
|
|
||||||
/api/v1/topics/search?q=docker
|
|
||||||
/api/v1/orgs/acme/teams/search?q=python3
|
|
||||||
/api/v1/orgs/acme/teams/search?q=powershell
|
|
||||||
/api/v1/orgs/acme/teams/search?q=ls%20-la
|
|
||||||
/api/v1/orgs/acme/teams/search?q=.htaccess
|
|
||||||
/api/v1/orgs/acme/teams/search?q=developers
|
|
||||||
/api/v1/admin/emails/search?q=python3
|
|
||||||
/api/v1/admin/emails/search?q=powershell
|
|
||||||
/api/v1/admin/emails/search?q=ls%20-la
|
|
||||||
/api/v1/admin/emails/search?q=.htaccess
|
|
||||||
/api/v1/admin/emails/search?q=alice
|
|
||||||
/api/v1/admin/unadopted?query=python3
|
|
||||||
/api/v1/admin/unadopted?query=powershell
|
|
||||||
/api/v1/admin/unadopted?query=ls%20-la
|
|
||||||
/api/v1/admin/unadopted?query=.htaccess
|
|
||||||
/api/v1/admin/unadopted?query=alice%2Fpython3-app
|
|
||||||
/api/v1/admin/unadopted?query=alice%2Flegacy-site
|
|
||||||
/api/v1/repos/alice/tools/milestones?state=open&name=python3
|
|
||||||
/api/v1/repos/alice/tools/milestones?state=open&name=powershell
|
|
||||||
/api/v1/repos/alice/tools/milestones?state=open&name=ls%20-la
|
|
||||||
/api/v1/repos/alice/tools/milestones?state=open&name=.htaccess
|
|
||||||
/api/v1/repos/alice/tools/milestones?state=open&name=v1.0
|
|
||||||
|
|
||||||
# The package registries' searches, sent to gitea 1.27.3 by each registry's own
|
|
||||||
# client: cargo, knife, composer, conan 1 and 2, dotnet and npm. curl sent the
|
|
||||||
# requests no client sends: the Chef cookbook list with a search, and NuGet's
|
|
||||||
# Packages() with the search in $filter, as NuGet's client builds it for a feed
|
|
||||||
# without Search(). curl also sent each NuGet Search() and Packages() search to
|
|
||||||
# Search()/$count and Packages()/$count, which count its results. Each searches
|
|
||||||
# for words that name commands and files, such as python3 and .htaccess, and
|
|
||||||
# for plain words. A Composer, Conan or npm name can hold a slash, so those
|
|
||||||
# searches are also sent /bin/sh.
|
|
||||||
/api/packages/alice/cargo/api/v1/crates?q=python3&per_page=10
|
|
||||||
/api/packages/alice/cargo/api/v1/crates?q=powershell&per_page=10
|
|
||||||
/api/packages/alice/cargo/api/v1/crates?q=ls%20%2Dla&per_page=10
|
|
||||||
/api/packages/alice/cargo/api/v1/crates?q=%2Ehtaccess&per_page=10
|
|
||||||
/api/packages/alice/cargo/api/v1/crates?q=docker%2Dcompose%2Eyml&per_page=10
|
|
||||||
/api/packages/alice/cargo/api/v1/crates?q=tools&per_page=10
|
|
||||||
/api/packages/alice/chef/api/v1/search?q=python3&items=9999999&start=0
|
|
||||||
/api/packages/alice/chef/api/v1/search?q=powershell&items=9999999&start=0
|
|
||||||
/api/packages/alice/chef/api/v1/search?q=ls%20-la&items=9999999&start=0
|
|
||||||
/api/packages/alice/chef/api/v1/search?q=.htaccess&items=9999999&start=0
|
|
||||||
/api/packages/alice/chef/api/v1/search?q=docker-compose.yml&items=9999999&start=0
|
|
||||||
/api/packages/alice/chef/api/v1/search?q=tools&items=9999999&start=0
|
|
||||||
/api/packages/alice/chef/api/v1/cookbooks?q=python3&items=9999999&start=0
|
|
||||||
/api/packages/alice/chef/api/v1/cookbooks?q=powershell&items=9999999&start=0
|
|
||||||
/api/packages/alice/chef/api/v1/cookbooks?q=ls%20-la&items=9999999&start=0
|
|
||||||
/api/packages/alice/chef/api/v1/cookbooks?q=.htaccess&items=9999999&start=0
|
|
||||||
/api/packages/alice/chef/api/v1/cookbooks?q=docker-compose.yml&items=9999999&start=0
|
|
||||||
/api/packages/alice/chef/api/v1/cookbooks?q=tools&items=9999999&start=0
|
|
||||||
/api/packages/alice/composer/search.json?q=python3&type=
|
|
||||||
/api/packages/alice/composer/search.json?q=powershell&type=
|
|
||||||
/api/packages/alice/composer/search.json?q=ls+-la&type=
|
|
||||||
/api/packages/alice/composer/search.json?q=.htaccess&type=
|
|
||||||
/api/packages/alice/composer/search.json?q=%2Fbin%2Fsh&type=
|
|
||||||
/api/packages/alice/composer/search.json?q=alice%2Ftools-cli&type=
|
|
||||||
/api/packages/alice/composer/search.json?q=docker-compose.yml&type=
|
|
||||||
/api/packages/alice/composer/search.json?q=tools&type=
|
|
||||||
/api/packages/alice/conan/v1/conans/search?q=python3
|
|
||||||
/api/packages/alice/conan/v1/conans/search?q=powershell
|
|
||||||
/api/packages/alice/conan/v1/conans/search?q=ls+-la
|
|
||||||
/api/packages/alice/conan/v1/conans/search?q=.htaccess
|
|
||||||
/api/packages/alice/conan/v1/conans/search?q=%2Fbin%2Fsh
|
|
||||||
/api/packages/alice/conan/v1/conans/search?q=tools%2F%2A
|
|
||||||
/api/packages/alice/conan/v1/conans/search?q=docker-compose.yml
|
|
||||||
/api/packages/alice/conan/v1/conans/search?q=tools
|
|
||||||
/api/packages/alice/conan/v2/conans/search?q=python3
|
|
||||||
/api/packages/alice/conan/v2/conans/search?q=powershell
|
|
||||||
/api/packages/alice/conan/v2/conans/search?q=ls+-la
|
|
||||||
/api/packages/alice/conan/v2/conans/search?q=.htaccess
|
|
||||||
/api/packages/alice/conan/v2/conans/search?q=%2Fbin%2Fsh
|
|
||||||
/api/packages/alice/conan/v2/conans/search?q=tools%2F1.0%40alice%2Fstable
|
|
||||||
/api/packages/alice/conan/v2/conans/search?q=docker-compose.yml
|
|
||||||
/api/packages/alice/conan/v2/conans/search?q=tools
|
|
||||||
/api/packages/alice/nuget/query?q=python3&skip=0&take=20&prerelease=false&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/query?q=powershell&skip=0&take=20&prerelease=false&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/query?q=ls%20-la&skip=0&take=20&prerelease=false&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/query?q=.htaccess&skip=0&take=20&prerelease=false&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/query?q=docker-compose.yml&skip=0&take=20&prerelease=false&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/query?q=tools&skip=0&take=20&prerelease=false&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Search()?$filter=IsLatestVersion&searchTerm='python3'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Search()?$filter=IsLatestVersion&searchTerm='powershell'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Search()?$filter=IsLatestVersion&searchTerm='ls%20-la'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Search()?$filter=IsLatestVersion&searchTerm='.htaccess'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Search()?$filter=IsLatestVersion&searchTerm='docker-compose.yml'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Search()?$filter=IsLatestVersion&searchTerm='tools'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Packages()?$filter=((((Id%20ne%20null)%20and%20substringof('python3',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('python3',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20python3%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Packages()?$filter=((((Id%20ne%20null)%20and%20substringof('powershell',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('powershell',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20powershell%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Packages()?$filter=(((((((Id%20ne%20null)%20and%20substringof('ls',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('ls',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20ls%20',tolower(Tags))))%20or%20((Id%20ne%20null)%20and%20substringof('-la',tolower(Id))))%20or%20((Description%20ne%20null)%20and%20substringof('-la',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20-la%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Packages()?$filter=((((Id%20ne%20null)%20and%20substringof('.htaccess',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('.htaccess',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20.htaccess%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Packages()?$filter=((((Id%20ne%20null)%20and%20substringof('docker-compose.yml',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('docker-compose.yml',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20docker-compose.yml%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Packages()?$filter=((((Id%20ne%20null)%20and%20substringof('tools',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('tools',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20tools%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Packages()?$filter=tolower(Id)%20eq%20'python3'&$top=30
|
|
||||||
/api/packages/alice/nuget/Packages()?$filter=tolower(Id)%20eq%20'powershell'&$top=30
|
|
||||||
/api/packages/alice/nuget/Packages()?$filter=tolower(Id)%20eq%20'.htaccess'&$top=30
|
|
||||||
/api/packages/alice/nuget/Packages()?$filter=tolower(Id)%20eq%20'docker-compose.yml'&$top=30
|
|
||||||
/api/packages/alice/nuget/Packages()?$filter=tolower(Id)%20eq%20'tools'&$top=30
|
|
||||||
/api/packages/alice/nuget/Search()/$count?$filter=IsLatestVersion&searchTerm='python3'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Search()/$count?$filter=IsLatestVersion&searchTerm='powershell'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Search()/$count?$filter=IsLatestVersion&searchTerm='ls%20-la'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Search()/$count?$filter=IsLatestVersion&searchTerm='.htaccess'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Search()/$count?$filter=IsLatestVersion&searchTerm='docker-compose.yml'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Search()/$count?$filter=IsLatestVersion&searchTerm='tools'&targetFramework=''&includePrerelease=false&$skip=0&$top=20&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Packages()/$count?$filter=((((Id%20ne%20null)%20and%20substringof('python3',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('python3',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20python3%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Packages()/$count?$filter=((((Id%20ne%20null)%20and%20substringof('powershell',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('powershell',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20powershell%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Packages()/$count?$filter=(((((((Id%20ne%20null)%20and%20substringof('ls',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('ls',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20ls%20',tolower(Tags))))%20or%20((Id%20ne%20null)%20and%20substringof('-la',tolower(Id))))%20or%20((Description%20ne%20null)%20and%20substringof('-la',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20-la%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Packages()/$count?$filter=((((Id%20ne%20null)%20and%20substringof('.htaccess',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('.htaccess',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20.htaccess%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Packages()/$count?$filter=((((Id%20ne%20null)%20and%20substringof('docker-compose.yml',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('docker-compose.yml',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20docker-compose.yml%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Packages()/$count?$filter=((((Id%20ne%20null)%20and%20substringof('tools',tolower(Id)))%20or%20((Description%20ne%20null)%20and%20substringof('tools',tolower(Description))))%20or%20((Tags%20ne%20null)%20and%20substringof('%20tools%20',tolower(Tags))))%20and%20IsLatestVersion&$orderby=Id&$skip=0&$top=30&semVerLevel=2.0.0
|
|
||||||
/api/packages/alice/nuget/Packages()/$count?$filter=tolower(Id)%20eq%20'python3'&$top=30
|
|
||||||
/api/packages/alice/nuget/Packages()/$count?$filter=tolower(Id)%20eq%20'powershell'&$top=30
|
|
||||||
/api/packages/alice/nuget/Packages()/$count?$filter=tolower(Id)%20eq%20'.htaccess'&$top=30
|
|
||||||
/api/packages/alice/nuget/Packages()/$count?$filter=tolower(Id)%20eq%20'docker-compose.yml'&$top=30
|
|
||||||
/api/packages/alice/nuget/Packages()/$count?$filter=tolower(Id)%20eq%20'tools'&$top=30
|
|
||||||
/api/packages/alice/npm/-/v1/search?text=python3&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5
|
|
||||||
/api/packages/alice/npm/-/v1/search?text=powershell&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5
|
|
||||||
/api/packages/alice/npm/-/v1/search?text=ls+-la&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5
|
|
||||||
/api/packages/alice/npm/-/v1/search?text=.htaccess&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5
|
|
||||||
/api/packages/alice/npm/-/v1/search?text=%2Fbin%2Fsh&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5
|
|
||||||
/api/packages/alice/npm/-/v1/search?text=%40alice%2Ftools-cli&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5
|
|
||||||
/api/packages/alice/npm/-/v1/search?text=docker-compose.yml&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5
|
|
||||||
/api/packages/alice/npm/-/v1/search?text=tools&size=20&from=0&quality=0.65&popularity=0.98&maintenance=0.5
|
|
||||||
|
|
||||||
# Pages, raw files and edit pages of files, and the API's call for their
|
# Pages, raw files and edit pages of files, and the API's call for their
|
||||||
# contents.
|
# contents.
|
||||||
/owner/repo/src/branch/main/package.json
|
/owner/repo/src/branch/main/package.json
|
||||||
|
|||||||
Reference in new issue
Block a user