-
Notifications
You must be signed in to change notification settings - Fork 7
merge upstream/master into oadp-dev #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: oadp-dev
Are you sure you want to change the base?
Changes from all commits
b7dc392
c05c668
7361d91
b21b124
6232686
5a12cad
9fffee3
8ddd3d1
4b8a8d7
6c69b5c
9bd79c3
4daddec
cd5749d
7453c78
610e0b0
3213b60
4c3cb4b
328f629
392253c
660d846
4a4b0f8
67e893e
fb6aeba
bc3d75a
50125a5
032271b
0dd8905
41b01a7
41e2b1b
e6d70cf
f6fb8f6
ac11538
dab6415
7c03236
161a1bb
c75921e
a621988
e345076
9a69c49
1e92749
e05c02e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,160 @@ | ||
| # CLAUDE.md | ||
|
|
||
| Guidance for Claude when working in this repository (File Browser, `filebrowser/filebrowser`). | ||
|
|
||
| ## Repo orientation | ||
|
|
||
| - Go backend (`github.com/filebrowser/filebrowser/v2`, ecosystem `go`) + Vue frontend under `frontend/`. | ||
| - The project is in **maintenance-only mode** (see `SECURITY.md`). Prefer small, surgical, well-tested changes. | ||
| - Version scheme: `v2.63.x`. Conventional-commit messages (`fix(scope): …`, `feat: …`, `chore: …`). | ||
| - Verify with `go build ./...`, `go vet ./...`, `go test ./...`. Reuse existing test harnesses (e.g. `signToken`, `scopedUserStorage`, `handle`, `customFSUser`, `mockUserStore`). | ||
|
|
||
| --- | ||
|
|
||
| # Handling security advisories | ||
|
|
||
| Use this playbook when asked to triage, verify, fix, or manage GitHub security advisories. | ||
| All advisory state lives on GitHub and is driven with the `gh` CLI. States are | ||
| `triage → draft → published`, plus `closed`. | ||
|
|
||
| ## 1. Fetch | ||
|
|
||
| ```bash | ||
| # List by state (also: published, draft, closed) | ||
| gh api '/repos/filebrowser/filebrowser/security-advisories?state=triage&per_page=100' \ | ||
| --jq '.[] | {ghsa_id, severity, summary, state}' | ||
|
|
||
| # Full report for one advisory (read .summary and .description) | ||
| gh api /repos/filebrowser/filebrowser/security-advisories/GHSA-xxxx-xxxx-xxxx \ | ||
| --jq '.summary, "---", .description' | ||
| ``` | ||
|
|
||
| Always pull the **published** set too — you need it to dedup against. | ||
|
|
||
| ## 2. Verify each report — do NOT trust the report text | ||
|
|
||
| Read the actual source **at HEAD** and reach one verdict per advisory: | ||
|
|
||
| - **CONFIRMED** — defect exists at HEAD. Quote the exact `file:line`. | ||
| - **FIXED** — already patched (find the fix commit). | ||
| - **FALSE / NOT APPLICABLE** — claim is wrong, or targets a different project. | ||
| - **NOT EXPLOITABLE** — pattern exists but no code path can reach the precondition. | ||
| - **DUPLICATE** — of a published advisory, or of another triage advisory. | ||
|
|
||
| Common traps — check each before accepting a report: | ||
|
|
||
| - **"Incomplete fix of a prior advisory."** Read the original fix commit and confirm the *specific* | ||
| sibling code path is actually still unguarded — a prior fix may already cover a related path. | ||
| - **Wrong project.** Confirm the referenced files, symbols, and endpoints exist in this repo; reports | ||
| sometimes describe a fork. If the cited code isn't here, close as not applicable. | ||
| - **"Legacy / upgraded / imported records are affected."** Trace the field's git history and confirm that | ||
| some released version could actually produce such a record before believing it | ||
| (`git log -S'Field' -- path`, `git show <commit>`, `git tag --contains <commit>`). | ||
| - **Overlapping reports.** Two triage advisories may share one root cause — consolidate and fix once. | ||
| - **Known, intentionally-unaddressed classes.** Some areas are known and tracked but not fixed (see | ||
| `SECURITY.md`'s Known Issues); matching reports are duplicates. | ||
|
|
||
| Record, per advisory: verdict, the `file:line` evidence, exploitability preconditions | ||
| (default config? platform-specific? auth required?), and the disposition. | ||
|
|
||
| ## 3. Fix (one commit per advisory) | ||
|
|
||
| - Branch off `master` first; never commit fixes directly to `master`. | ||
| - **One commit per fix**, each referencing the GHSA in the body (`Refs GHSA-xxxx-xxxx-xxxx`). | ||
| - When several advisories share a root cause because parallel code paths diverged, **centralize** the logic | ||
| (e.g. `settings.CreateUserHome` used by signup, proxy, and hook provisioning) so they cannot drift again. | ||
| - Keep it surgical; match surrounding style. | ||
|
|
||
| ## 4. Add a regression test per fix | ||
|
|
||
| - Reuse the existing harnesses; add a focused test asserting the fixed behavior (and that the legitimate | ||
| path still works). Commit tests alongside the fixes (`test(scope): …`, `Refs GHSA-…`). | ||
| - Run `go test ./... && go vet ./...` before finishing. | ||
|
|
||
| ## 5. Severity — set a CVSS vector, don't hand-assert | ||
|
|
||
| Set a CVSS v3.1 vector; GitHub derives the score **and** severity from it (overriding the plain `severity` | ||
| field). Encode the real preconditions in the vector so the band is defensible: | ||
|
|
||
| - Requires a specific target configuration/platform (e.g. case-insensitive FS) → **AC:H**. | ||
| - Unauthenticated vs needs an account → **PR:N / PR:L**. | ||
| - Keep it consistent with the **predecessor CVE's** rating (an incomplete-fix follow-up should not outrank | ||
| its parent). Bands: `0.1–3.9` low, `4.0–6.9` medium, `7.0–8.9` high, `9.0–10.0` critical. | ||
|
|
||
| ```bash | ||
| gh api -X PATCH /repos/filebrowser/filebrowser/security-advisories/GHSA-xxxx-xxxx-xxxx \ | ||
| -f cvss_vector_string='CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H' \ | ||
| --jq '{ghsa_id, severity, score: .cvss.score, vector: .cvss.vector_string}' | ||
| ``` | ||
|
|
||
| ## 6. Clean up the title | ||
|
|
||
| Rewrite reporter titles to the concise, sentence-case, backtick-free style of the published advisories | ||
| (state the vuln; drop "Incomplete fix of…" prefixes and jargon). The title is the `summary` field: | ||
|
|
||
| ```bash | ||
| gh api -X PATCH .../security-advisories/GHSA-xxxx-xxxx-xxxx \ | ||
| -f summary='Proxy-auth auto-provisioning ignores createUserDir and grants the server root scope' | ||
| ``` | ||
|
|
||
| ## 7. Rewrite the body into the standard structure | ||
|
|
||
| Reporter reports arrive in whatever shape the reporter used. Before drafting, rewrite the | ||
| `description` into the sections below — **reusing the reporter's own wording wherever it is | ||
| accurate**, rather than paraphrasing it. Drop the greeting, the offer to help, and any claim the | ||
| verification in step 2 disproved. Keep sections in this order and omit the ones that don't apply: | ||
|
|
||
| | Section | Contents | | ||
| | --- | --- | | ||
| | `## Summary` | What the defect is, in two or three sentences. Note the version it was reported against and the range it was verified over. | | ||
| | `## Details` | Root cause, naming `file.go`, the function, and a short quote of the **pre-fix** code. | | ||
| | `## PoC` | The reporter's reproduction steps and observed result, trimmed to the essentials. | | ||
| | `## Impact` | Who can exploit it (privilege level, preconditions) and what they get. | | ||
| | `## Patches` | Fixed version plus a link to the fix commit, and one sentence on what the fix does. Mention it if the reporter re-tested and confirmed. | | ||
| | `## Workarounds` | Real mitigations, or `None. Upgrade to vX.Y.Z.` | | ||
| | `## Out of scope` | Anything in the original report deliberately **not** treated as a vulnerability, with the reasoning. Needed whenever the advisory is narrower than the report. | | ||
| | `## References` | Fix and regression-test commit links. | | ||
|
|
||
| Use `##` headings (matching the published advisories) and keep the body in the maintainer's voice — | ||
| first person ("I reproduced…") belongs only inside quoted PoC steps. | ||
|
|
||
| Send it as a file so the Markdown survives shell quoting: | ||
|
|
||
| ```bash | ||
| jq -Rs '{description: .}' desc.md \ | ||
| | gh api -X PATCH .../security-advisories/GHSA-xxxx-xxxx-xxxx --input - | ||
| ``` | ||
|
|
||
| ## 8. Set affected & patched versions | ||
|
|
||
| The package is always `{ecosystem: "go", name: "github.com/filebrowser/filebrowser/v2"}`. | ||
|
|
||
| - `vulnerable_version_range` — `<= <latest released version>` (the newest tag; find it with | ||
| `git tag --list 'v2.*' --sort=-version:refname | head -1`). | ||
| - `patched_versions` — the **next** release that will actually ship the fix. It doesn't exist just | ||
| because the branch does; it still has to be cut. **When unsure which version that will be, ask.** | ||
|
|
||
| Replace the placeholder versions below before sending: | ||
|
|
||
| ```bash | ||
| printf '%s' '{"vulnerabilities":[{"package":{"ecosystem":"go","name":"github.com/filebrowser/filebrowser/v2"},"vulnerable_version_range":"<= <latest>","patched_versions":"<next>","vulnerable_functions":[]}]}' \ | ||
| | gh api -X PATCH .../security-advisories/GHSA-xxxx-xxxx-xxxx --input - | ||
| ``` | ||
|
|
||
| ## 9. Move state / close, by disposition | ||
|
|
||
| ```bash | ||
| gh api -X PATCH .../security-advisories/GHSA-xxxx-xxxx-xxxx -f state=draft # fixed, awaiting release | ||
| gh api -X PATCH .../security-advisories/GHSA-xxxx-xxxx-xxxx -f state=closed # duplicate / N-A / not-exploitable | ||
| ``` | ||
|
|
||
| - **CONFIRMED & fixed** → metadata done → **draft**. Publish after the patched release ships. | ||
| - **DUPLICATE / NOT APPLICABLE / NOT EXPLOITABLE** → **closed**. | ||
| - **DEFERRED** (confirmed but not yet fixed) → leave in **triage** with a note. | ||
| - The REST API **cannot post advisory comments** — replies to reporters (dup notice, evidence, links to | ||
| the relevant tracking issue) must be posted manually in the advisory UI. Draft the text for the maintainer. | ||
|
|
||
| ## 10. Release & publish | ||
|
|
||
| Push the branch, open a PR, merge, tag/release the `patched_versions` you set, then publish the drafts. | ||
| Confirm outward-facing/irreversible advisory actions (close, publish) with the maintainer before doing them. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -157,15 +157,16 @@ func (a *HookAuth) SaveUser() (*users.User, error) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| u = a.GetUser(d) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| userHome, err := a.Settings.MakeUserDir(u.Username, u.Scope, a.Server.Root) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // A scope explicitly returned by the hook takes precedence over the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // automatic per-user home directory derivation. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _, explicitScope := a.Fields.Values["user.scope"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| derivedScope, err := a.Settings.CreateUserHome(u, a.Server.Root, explicitScope) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, fmt.Errorf("user: failed to mkdir user home dir: [%s]", userHome) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| u.Scope = userHome | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.Printf("user: %s, home dir: [%s].", u.Username, userHome) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.Printf("user: %s, home dir: [%s].", u.Username, u.Scope) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err = a.Users.Save(u) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err := a.Users.SaveProvisioned(u, derivedScope); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+160
to
+169
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
printf 'Files matching hook.go:\n'
fd -a 'hook\.go$' . || true
printf '\nRelevant auth/hook.go excerpt:\n'
sed -n '1,240p' auth/hook.go
printf '\nSearch CreateUserHome/SaveProvisioned/MakeUserDir definitions:\n'
rg -n "func .*CreateUserHome|func .*SaveProvisioned|func .*MakeUserDir|type .*Fields|GetString\\(" -S .Repository: migtools/filebrowser Length of output: 12609 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf 'auth/hook.go hookFields implementation:\n'
sed -n '230,310p' auth/hook.go
printf '\nsettings/dir.go CreateUserHome/MakeUserDir implementation:\n'
sed -n '1,120p' settings/dir.go
printf '\nusers/storage.go SaveProvisioned implementation:\n'
sed -n '80,170p' users/storage.go
printf '\nsettings dir tests around CreateUserHome:\n'
sed -n '1,120p' settings/dir_test.go
printf '\nfield string parsing probe for present/empty user.scope:\n'
python3 - <<'PY'
def getValues(s):
m={}
for line in s.replace('\r\n','\n').splitlines():
v=line.split('=',1)
if len(v)==2:
k=fv=v[0].strip(), v[1].strip()
m[k]=fv
return m
for s in ['user.scope=\nhook.action=auth\n','user.scope=/home/alice\nhook.action=auth\n','hook.action=auth\n']:
print(repr(s), getValues(s), "has user.scope:", "user.scope" in getValues(s).keys(), "value:", repr(getValues(s).get("user.scope","<missing>")))
PYRepository: migtools/filebrowser Length of output: 8538 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf 'auth/hook.go hookFields implementation:\n'
sed -n '230,310p' auth/hook.go
printf '\nsettings/dir.go CreateUserHome/MakeUserDir implementation:\n'
sed -n '1,120p' settings/dir.go
printf '\nusers/storage.goSaveProvisioned implementation:\n'
sed -n '80,170p' users/storage.go
printf '\nsettings dir tests around CreateUserHome:\n'
sed -n '1,120p' settings/dir_test.go
printf '\nfield string parsing probe for present/empty usser.scope:\n'
python3 - <<'PY'
def getValues(s):
m={}
for line in s.replace('\r\n','\n').splitlines():
v=line.split('=',1)
if len(v)==2:
m[v[0].strip()]=v[1].strip()
return m
for s in ['user.scope=\nhook.action=auth\n','user.scope=/home/alice\nhook.action=auth\n','hook.action=auth\n']:
print(repr(s), getValues(s), "has user.scope:", "user.scope" in getValues(s), "value:", repr(getValues(s).get("user.scope", "<missing>")))
PYRepository: migtools/filebrowser Length of output: 8393 Require a non-empty value for hook-provided explicit scopes.
🔒 Proposed fix- // A scope explicitly returned by the hook takes precedence over the
- // automatic per-user home directory derivation.
- _, explicitScope := a.Fields.Values["user.scope"]
+ // A scope explicitly returned by the hook takes precedence over the
+ //automatic per-user home directory derivation. Treat the scope as
+ // explicit only when the hook actually supplied a non-empty value —
+ // an empty value must still go through automatic per-user derivation
+ // and its collision check.
+ hookScope, hasHookScope := a.Fields.Values["user.scope"]
+ explicitScope := hasHookScope && hookScope != ""📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if p := !users.CheckPwd(a.Cred.Password, u.Password); len(a.Fields.Values) > 1 || p { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Keep suspected vulnerability reports private until triage.
Directing “non-critical” vulnerabilities to public issues can disclose exploitable details before the project confirms impact or ships a fix, and “critical” is not defined here. Route all suspected vulnerabilities through the private Security channel, or document precise criteria for cases safe to disclose publicly.
🤖 Prompt for AI Agents