racket/collects/web-server/dispatchers/filesystem-map.rkt
Jay McCarthy b843a45cd8 Fixing in an error in how paths that went outside the filesystem to
URL map were handled.

Previously, only ".." at the beginning of the URL were checked; now it
looks at the entire URL for a path that ultimately leaves the base.
2012-12-10 11:54:31 -07:00

107 lines
3.5 KiB
Racket

#lang racket/base
(require net/url
racket/list
racket/match
racket/contract)
(require web-server/private/util)
(define url->path/c
((url?) () . ->* . (values path? (listof path-piece?))))
(provide/contract
[url->path/c contract?]
[make-url->path (path-string? . -> . url->path/c)]
[make-url->valid-path (url->path/c . -> . url->path/c)]
[filter-url->path (regexp? url->path/c . -> . url->path/c)])
(define (restrict l)
(not
(negative?
(let loop ([end-in-file? #f] [depth 0] [l l])
(match l
[(list)
(if end-in-file?
(sub1 depth)
depth)]
[(list-rest (or ".." 'up) rst)
(loop #f (sub1 depth) rst)]
[(list-rest (or "" 'same) rst)
(loop #f depth rst)]
[(list-rest _ rst)
(loop #t (add1 depth) rst)])))))
(module+ test
(require rackunit)
(check-equal? (restrict (list))
#t)
(check-equal? (restrict (list 'up))
#f)
(check-equal? (restrict (list ".."))
#f)
(check-equal? (restrict (list 'same))
#t)
(check-equal? (restrict (list 'same ".."))
#f)
(check-equal? (restrict (list "foo" 'up "bar"))
#t)
(check-equal? (restrict (list "foo" 'up 'up "bar"))
#f)
(check-equal? (restrict (list 'up "bar"))
#f)
(check-equal? (restrict (list "foo" "bar" 'up "bar"))
#t)
(check-equal? (restrict (list "foo" "bar" 'up 'up 'up "bar"))
#f)
(check-equal? (restrict (list "foo" 'same "bar" 'up 'up 'up "bar"))
#f)
(check-equal? (restrict (list "foo" "" "bar" 'up 'up 'up "bar"))
#f)
(check-equal? (restrict (list "foo" "bar" 'up "bar" 'same))
#t)
(check-equal? (restrict (list "foo" "bar" 'up "bar" ""))
#t))
(define (build-path* . l)
(if (empty? l)
(build-path 'same)
(apply build-path l)))
(define ((make-url->path base) u)
(define nbase (path->complete-path base))
(define path-from-url
(map path/param-path
(url-path u)))
(unless (restrict path-from-url)
(error 'url->path "Illegal path: ~e outside base: ~e"
path-from-url
base))
(define the-path
(path->complete-path
(apply build-path* path-from-url)
nbase))
(define w/o-base (path-without-base nbase the-path))
(values the-path w/o-base))
(define ((make-url->valid-path url->path) u)
(let loop ([up (url-path u)])
#;(printf "~S\n" `(url->valid-path ,(url->string u) ,up))
(with-handlers ([exn:fail? (lambda (exn)
#;((error-display-handler) (exn-message exn) exn)
(if (empty? up)
(raise exn)
(loop (reverse (rest (reverse up))))))])
(define-values (p w/o-base)
(url->path (url-replace-path (lambda _ up) u)))
(unless (or (file-exists? p) (link-exists? p))
(raise (make-exn:fail:filesystem:exists (string->immutable-string (format "No valid path: ~a" p)) (current-continuation-marks))))
(values p w/o-base))))
(define ((filter-url->path regex url->path) u)
(define-values (p w/o-base) (url->path u))
(if (regexp-match regex (path->string p))
(values p w/o-base)
(raise (make-exn:fail:filesystem:exists (string->immutable-string (format "Does not pass filter: ~a" p))
(current-continuation-marks)))))