{"id":"CVE-2026-62683","kind":"vulnerability","aliases":["CVE-2026-62683","GHSA-pp88-jhwj-5qh5"],"related":[],"title":"File Browser: Trailing-slash directory deletion leaves public shares active for recreated paths","description":"## Summary\n\nFile Browser v2.63.15 can leave a public directory share behind when the shared directory is deleted through a path with a trailing slash. The share cleanup path calls `DeleteWithPathPrefix(file.Path, userID)`, but the Bolt backend performs the database prefix query with the unnormalized argument before trimming the slash for boundary checks. For a stored share at `/a`, deleting `/a/` queries for paths prefixed by `/a/`, so the exact `/a` share is never loaded and never deleted.\n\nIf a directory with the same path is later recreated, the stale public share becomes active again and exposes the new directory contents through the old public URL.\n\n## Impact\n\nAn authenticated user with share, download, and delete permissions can create a valid public share, delete the shared path with a trailing slash, and leave the public link in storage. The link is dormant while the path does not exist, but it exposes future content if the same path is recreated later. The final data exposure is unauthenticated through the public share URL.\n\nThis bypasses the intended lifecycle cleanup around public shares and is adjacent to the recent public-share path-existence and prefix-boundary fixes.\n\n## Reproduction\n\nThe following test was run against v2.63.15, commit `be23ab3a15bf957928ecfed88de5ab67850c1b9c`.\n\n1. Create a scoped user with share, download, and delete permissions.\n2. Create `/a/old.txt` in that user's scope.\n3. Store a valid public share row with `Hash: \"h\"`, `UserID: 1`, and `Path: \"/a\"`.\n4. Send an authenticated delete request for `/a/` through the resource delete handler.\n5. Confirm the directory is deleted.\n6. Confirm share hash `h` still exists in storage.\n7. Recreate `/a/future.txt`.\n8. Request the public share handler at `h/` and observe that the unauthenticated response includes `future.txt`.\n\nCommand used for the local proof:\n\n```bash\ngo test ./http -run TestValidationTrailingSlashDeleteLeavesExactDirectoryShare -count=1 -v\n```\n\nObserved result:\n\n```text\n=== RUN   TestValidationTrailingSlashDeleteLeavesExactDirectoryShare\n--- PASS: TestValidationTrailingSlashDeleteLeavesExactDirectoryShare (0.03s)\nPASS\nok  github.com/filebrowser/filebrowser/v2/http  0.044s\n```\n\n## Root cause\n\n`storage/bolt/share.go` queries the share index before normalizing the path:\n\n```go\nfunc (s shareBackend) DeleteWithPathPrefix(pathPrefix string, userID uint) error {\n    var links []share.Link\n    if err := s.db.Prefix(\"Path\", pathPrefix, &links); err != nil {\n        ...\n    }\n\n    prefix := strings.TrimRight(pathPrefix, \"/\")\n\n    for _, link := range links {\n        if link.UserID != userID {\n            continue\n        }\n\n        if link.Path != prefix && !strings.HasPrefix(link.Path, prefix+\"/\") {\n            continue\n        }\n        ...\n    }\n}\n```\n\nFor `pathPrefix == \"/a/\"`, the database prefix query can return descendants such as `/a/child`, but it does not return the exact share path `/a`. The later `TrimRight` cannot delete the exact share because that row was not loaded from storage.\n\n`http/resource.go` passes the request-derived path into this cleanup before removing the resource:\n\n```go\nfile, err := files.NewFileInfo(... Path: r.URL.Path ...)\n...\nerr = d.store.Share.DeleteWithPathPrefix(file.Path, d.user.ID)\n...\nreturn d.user.Fs.RemoveAll(r.URL.Path)\n```\n\nA request for `/a/` therefore removes the directory but misses the exact `/a` share row.\n\n## Remediation\n\nNormalize the path before querying the share index, and cover both the exact normalized path and its descendants. Add regression tests where a stored share path `/a` is removed by `DeleteWithPathPrefix(\"/a/\", userID)` and where `DELETE /api/resources/a/` deletes the exact public share row.\n\nFixed in version 2.63.17.","summary":"Deleting a shared directory through a path with a trailing slash leaves its public share link stored in the database. If a directory with the same path is later recreated, the old public URL serves the new contents without authentication.","severity":"low","cvss":3.1,"epss":0.00315,"exploited":false,"exploited_at":null,"products":[{"name":"github.com/filebrowser/filebrowser/v2","fixed":["2.63.17"],"affected":"< 2.63.17","ecosystem":"go"}],"ecosystems":["go"],"tags":["cwe-863","file-browser","go","package","self-hosted"],"not_affected_if":["no-public-shares","no-share-permission","version-2.63.17-or-later"],"how_to_check":"- Check the File Browser version with `filebrowser version` or the image tag; affected builds are below `2.63.17`.\n- Review existing public shares in the admin UI or share list for links whose directory was deleted and later recreated.","fix":"- File Browser: move to `2.63.17`.","status":"open","published_at":"2026-07-15T16:16:51+00:00","first_seen_at":"2026-11-13T23:12:39+00:00","triaged_at":"2026-11-13T23:14:00+00:00","updated_at":"2026-11-13T23:35:11+00:00","category":"package","confidence":0.85,"triage":{"at":"2026-11-13T23:14:00Z","model":"claude-haiku-5-5","output":{"fix":"- File Browser: move to `2.63.17`.","reach":"internet","title":"File Browser: trailing-slash delete leaves public share live for recreated paths","impact":"medium","summary":"Deleting a shared directory through a path with a trailing slash leaves its public share link stored in the database. If a directory with the same path is later recreated, the old public URL serves the new contents without authentication.","audience":"businesses","category":"package","nicknames":[],"who_cares":["file-browser","self-hosted","go"],"confidence":0.85,"fix_status":"available","needs_auth":"yes","worst_case":"unauthenticated users read files in a recreated shared directory","analyst_note":"","how_to_check":"- Check the File Browser version with `filebrowser version` or the image tag; affected builds are below `2.63.17`.\n- Review existing public shares in the admin UI or share list for links whose directory was deleted and later recreated.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["version-2.63.17-or-later","no-public-shares","no-share-permission"],"who_is_affected":"Deployments of File Browser v2 below 2.63.17 where users have share, download and delete permissions. The exposure needs an authenticated user who creates a public share, deletes the shared directory with a trailing slash, and later recreates the same path; the share is then reachable without login. Instances where users cannot create public shares are not exposed.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"no","severity_if_unknown":"low"},"cost_usd":0.000864,"input_tokens":5977,"output_tokens":533,"severity_before":"low"},"analyst_status":null,"analyst_note":null,"search":"'-2026':16A,24A '-62683':17A,25A '-863':30B '/a':136C,138C,144C,148C,317C,325C,448C,468C,515C,524C,554C,559C '/a/child':458C '/a/future.txt':348C '/a/old.txt':297C '/api/resources/a':564C '/filebrowser/filebrowser/v2':28A '/filebrowser/filebrowser/v2/http':393C '/http':374C '0.03':387C '0.044':394C '1':284C,314C,378C '2':295C '2.63.17':574C '3':303C '4':318C '5':331C '5qh5':22A '6':337C '7':346C '8':349C 'activ':11A,171C 'add':546C 'adjac':259C 'argument':123C 'around':254C 'authent':78B,186C,321C 'backend':114C 'bash':371C 'be23ab3a15bf957928ecfed88de5ab67850c1b9c':283C 'becom':170C 'behind':89C 'bolt':113C 'boundari':129C,272C 'browser':2A,33B,81C 'bypass':249C 'call':107C 'cannot':472C 'caus':397C 'check':130C 'cleanup':105C,253C,494C 'command':365C 'commit':282C 'confirm':332C,338C 'content':76B,178C,229C 'continu':438C,445C 'count':377C 'cover':537C 'creat':195C,285C,296C 'cve':15A,23A 'cwe':29B 'd.store.share.deletewithpathprefix':506C 'd.user.fs.removeall':510C 'd.user.id':508C 'data':239C 'databas':58B,117C,450C 'delet':7A,39B,95C,137C,155C,192C,200C,293C,322C,329C,336C,473C,563C,565C 'deletewithpathprefix':108C,411C,558C 'deriv':490C 'descend':455C,545C 'directori':6A,42B,61B,87C,93C,158C,177C,334C,519C 'dormant':218C 'download':190C,291C 'err':421C,426C,501C,505C 'error':416C 'exact':147C,465C,475C,523C,540C,567C 'exist':224C,268C,343C 'expos':174C,227C 'exposur':240C 'file':1A,32B,80C,500C 'file-brows':31B 'file.path':109C,507C 'files.newfileinfo':502C 'final':238C 'fix':273C,571C 'follow':276C 'func':408C 'futur':228C 'future.txt':364C 'ghsa':19A 'ghsa-pp88-jhwj-5qh5':18A 'github.com':27A,392C 'github.com/filebrowser/filebrowser/v2':26A 'github.com/filebrowser/filebrowser/v2/http':391C 'go':34B,372C,407C,499C 'h':312C,341C,356C 'handler':330C,354C 'hash':311C,340C 'host':38B 'http/resource.go':485C 'impact':184C 'includ':363C 'index':402C,535C 'intend':251C 'jhwj':21A 'later':67B,164C,236C,470C 'leav':8A,50B,84C,209C 'lifecycl':252C 'link':54B,212C,216C,418C,425C,432C,434C 'link.path':440C,443C 'link.userid':436C 'load':152C,482C 'local':369C 'miss':521C 'never':151C,154C 'new':75B,176C 'nil':427C 'normal':404C,528C,541C 'observ':358C,380C 'ok':390C 'old':70B,181C 'packag':35B 'pass':385C,389C,486C 'path':14A,45B,65B,98C,106C,141C,162C,203C,221C,233C,267C,316C,406C,423C,467C,491C,503C,530C,542C,553C 'path-exist':266C 'pathprefix':412C,424C,430C,447C 'perform':115C 'permiss':193C,294C 'pp88':20A 'prefix':118C,142C,271C,428C,441C,444C,451C 'prefix-boundari':270C 'proof':370C 'public':9A,52B,71B,86C,168C,182C,198C,211C,245C,255C,264C,307C,352C,568C 'public-shar':263C 'queri':119C,139C,399C,452C,532C 'r.url.path':504C,511C 'rang':433C 'recent':262C 'recreat':13A,68B,165C,235C,347C 'regress':547C 'remedi':527C 'remov':496C,517C,556C 'reproduct':274C 'request':323C,350C,489C,513C 'request-deriv':488C 'resourc':328C,498C 'respons':362C 'result':381C 'return':454C,463C,509C 'root':396C 'row':309C,479C,526C,570C 'run':279C,375C,383C 's.db.prefix':422C 'scope':287C,302C 'self':37B 'self-host':36B 'send':319C 'serv':73B 'share':10A,41B,53B,88C,92C,104C,134C,149C,169C,189C,199C,202C,246C,256C,265C,290C,308C,339C,353C,401C,466C,476C,525C,534C,552C,569C 'share.link':419C 'sharebackend':410C 'slash':5A,49B,102C,127C,207C 'stale':167C 'still':342C 'storag':214C,345C,484C 'storage/bolt/share.go':398C 'store':55B,133C,304C,551C 'string':413C 'strings.hasprefix':442C 'strings.trimright':429C 'summari':79C 'test':277C,373C,548C 'testvalidationtrailingslashdeleteleavesexactdirectoryshar':376C,384C,386C 'text':382C 'therefor':516C 'trail':4A,48B,101C,206C 'trailing-slash':3A 'trim':125C 'trimright':471C 'uint':415C 'unauthent':242C,361C 'unnorm':122C 'url':72B,183C,247C 'use':366C 'user':187C,288C,300C 'userid':110C,313C,414C,437C,560C 'v':379C 'v2.63.15':82C,281C 'valid':197C,306C 'var':417C 'version':573C 'without':77B","nicknames":[],"mitigations":[],"who_affected":"Deployments of File Browser v2 below 2.63.17 where users have share, download and delete permissions. The exposure needs an authenticated user who creates a public share, deletes the shared directory with a trailing slash, and later recreates the same path; the share is then reachable without login. Instances where users cannot create public shares are not exposed.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"businesses","plain_action":null,"pkeys":["githubcomfilebrowserfilebrowserv2"],"ptokens":["com","filebrowser","github","v2"],"labels":["cwe863","filebrowser","go","low","package","selfhosted"],"campaign_type":null,"search_a":"'-2026':16A,24A '-62683':17A,25A '/filebrowser/filebrowser/v2':28A '5qh5':22A 'activ':11A 'browser':2A 'cve':15A,23A 'delet':7A 'directori':6A 'file':1A 'ghsa':19A 'ghsa-pp88-jhwj-5qh5':18A 'github.com':27A 'github.com/filebrowser/filebrowser/v2':26A 'jhwj':21A 'leav':8A 'path':14A 'pp88':20A 'public':9A 'recreat':13A 'share':10A 'slash':5A 'trail':4A 'trailing-slash':3A","search_b":"'-863':30B 'authent':78B 'browser':33B 'content':76B 'cwe':29B 'databas':58B 'delet':39B 'directori':42B,61B 'file':32B 'file-brows':31B 'go':34B 'host':38B 'later':67B 'leav':50B 'link':54B 'new':75B 'old':70B 'packag':35B 'path':45B,65B 'public':52B,71B 'recreat':68B 'self':37B 'self-host':36B 'serv':73B 'share':41B,53B 'slash':49B 'store':55B 'trail':48B 'url':72B 'without':77B","search_c":"'/a':136C,138C,144C,148C,317C,325C,448C,468C,515C,524C,554C,559C '/a/child':458C '/a/future.txt':348C '/a/old.txt':297C '/api/resources/a':564C '/filebrowser/filebrowser/v2/http':393C '/http':374C '0.03':387C '0.044':394C '1':284C,314C,378C '2':295C '2.63.17':574C '3':303C '4':318C '5':331C '6':337C '7':346C '8':349C 'activ':171C 'add':546C 'adjac':259C 'argument':123C 'around':254C 'authent':186C,321C 'backend':114C 'bash':371C 'be23ab3a15bf957928ecfed88de5ab67850c1b9c':283C 'becom':170C 'behind':89C 'bolt':113C 'boundari':129C,272C 'browser':81C 'bypass':249C 'call':107C 'cannot':472C 'caus':397C 'check':130C 'cleanup':105C,253C,494C 'command':365C 'commit':282C 'confirm':332C,338C 'content':178C,229C 'continu':438C,445C 'count':377C 'cover':537C 'creat':195C,285C,296C 'd.store.share.deletewithpathprefix':506C 'd.user.fs.removeall':510C 'd.user.id':508C 'data':239C 'databas':117C,450C 'delet':95C,137C,155C,192C,200C,293C,322C,329C,336C,473C,563C,565C 'deletewithpathprefix':108C,411C,558C 'deriv':490C 'descend':455C,545C 'directori':87C,93C,158C,177C,334C,519C 'dormant':218C 'download':190C,291C 'err':421C,426C,501C,505C 'error':416C 'exact':147C,465C,475C,523C,540C,567C 'exist':224C,268C,343C 'expos':174C,227C 'exposur':240C 'file':80C,500C 'file.path':109C,507C 'files.newfileinfo':502C 'final':238C 'fix':273C,571C 'follow':276C 'func':408C 'futur':228C 'future.txt':364C 'github.com':392C 'github.com/filebrowser/filebrowser/v2/http':391C 'go':372C,407C,499C 'h':312C,341C,356C 'handler':330C,354C 'hash':311C,340C 'http/resource.go':485C 'impact':184C 'includ':363C 'index':402C,535C 'intend':251C 'later':164C,236C,470C 'leav':84C,209C 'lifecycl':252C 'link':212C,216C,418C,425C,432C,434C 'link.path':440C,443C 'link.userid':436C 'load':152C,482C 'local':369C 'miss':521C 'never':151C,154C 'new':176C 'nil':427C 'normal':404C,528C,541C 'observ':358C,380C 'ok':390C 'old':181C 'pass':385C,389C,486C 'path':98C,106C,141C,162C,203C,221C,233C,267C,316C,406C,423C,467C,491C,503C,530C,542C,553C 'path-exist':266C 'pathprefix':412C,424C,430C,447C 'perform':115C 'permiss':193C,294C 'prefix':118C,142C,271C,428C,441C,444C,451C 'prefix-boundari':270C 'proof':370C 'public':86C,168C,182C,198C,211C,245C,255C,264C,307C,352C,568C 'public-shar':263C 'queri':119C,139C,399C,452C,532C 'r.url.path':504C,511C 'rang':433C 'recent':262C 'recreat':165C,235C,347C 'regress':547C 'remedi':527C 'remov':496C,517C,556C 'reproduct':274C 'request':323C,350C,489C,513C 'request-deriv':488C 'resourc':328C,498C 'respons':362C 'result':381C 'return':454C,463C,509C 'root':396C 'row':309C,479C,526C,570C 'run':279C,375C,383C 's.db.prefix':422C 'scope':287C,302C 'send':319C 'share':88C,92C,104C,134C,149C,169C,189C,199C,202C,246C,256C,265C,290C,308C,339C,353C,401C,466C,476C,525C,534C,552C,569C 'share.link':419C 'sharebackend':410C 'slash':102C,127C,207C 'stale':167C 'still':342C 'storag':214C,345C,484C 'storage/bolt/share.go':398C 'store':133C,304C,551C 'string':413C 'strings.hasprefix':442C 'strings.trimright':429C 'summari':79C 'test':277C,373C,548C 'testvalidationtrailingslashdeleteleavesexactdirectoryshar':376C,384C,386C 'text':382C 'therefor':516C 'trail':101C,206C 'trim':125C 'trimright':471C 'uint':415C 'unauthent':242C,361C 'unnorm':122C 'url':183C,247C 'use':366C 'user':187C,288C,300C 'userid':110C,313C,414C,437C,560C 'v':379C 'v2.63.15':82C,281C 'valid':197C,306C 'var':417C 'version':573C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-06-28T06:01:43+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-pp88-jhwj-5qh5","url":"https://github.com/advisories/GHSA-pp88-jhwj-5qh5","severity":"low","published_at":"2026-11-13T23:12:06+00:00","modified_at":"2026-11-13T23:12:07+00:00","first_seen_at":"2026-11-13T23:12:39+00:00","fetched_at":"2026-11-13T23:22:53+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-62683","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-62683","severity":null,"published_at":"2026-07-15T16:16:51+00:00","modified_at":"2026-07-15T19:18:38+00:00","first_seen_at":"2026-11-13T23:35:11+00:00","fetched_at":"2026-11-13T23:35:11+00:00","note":null}],"documents":[{"id":150836,"url":"https://api.github.com/repos/filebrowser/filebrowser/security-advisories/GHSA-pp88-jhwj-5qh5","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":150837,"url":"https://github.com/filebrowser/filebrowser/security/advisories/GHSA-pp88-jhwj-5qh5","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":"2026-06-28T06:01:43+00:00","relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":150838,"url":"https://github.com/filebrowser/filebrowser/commit/f30fca636c1af9ef401e9a82ff60391cb3db97e1","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":150839,"url":"https://github.com/filebrowser/filebrowser/releases/tag/v2.63.17","title":"Release: github.com","kind":"release","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"}],"disclosures":[],"exploit_sources":[{"type":"epss","score":0.00315,"url":"https://api.first.org/data/v1/epss?cve=CVE-2026-62683"}],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}