Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-workhorse
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-workhorse
Commits
6a2dc7c6
Commit
6a2dc7c6
authored
Jan 08, 2016
by
Jacob Vosmaer
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'uploads-check'
parents
20eea011
20f87b49
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
159 additions
and
6 deletions
+159
-6
.gitignore
.gitignore
+1
-0
.gitlab-ci.yml
.gitlab-ci.yml
+2
-4
main.go
main.go
+13
-1
main_test.go
main_test.go
+139
-0
servefile.go
servefile.go
+3
-0
upstream.go
upstream.go
+1
-1
No files found.
.gitignore
View file @
6a2dc7c6
test/data
test/data
test/scratch
test/scratch
gitlab-workhorse
gitlab-workhorse
test/public
.gitlab-ci.yml
View file @
6a2dc7c6
before_script
:
before_script
:
-
rm -rf /usr/local/go
-
test -f /.dockerinit && apt-get update -qq && apt-get install -y curl unzip bzip2
-
apt-get update -qq
-
apt-get install -y curl unzip bzip2
-
curl -O https://storage.googleapis.com/golang/go1.5.2.linux-amd64.tar.gz
-
curl -O https://storage.googleapis.com/golang/go1.5.2.linux-amd64.tar.gz
-
echo 'cae87ed095e8d94a81871281d35da7829bd1234e go1.5.2.linux-amd64.tar.gz' | shasum -c -
-
echo 'cae87ed095e8d94a81871281d35da7829bd1234e go1.5.2.linux-amd64.tar.gz' | shasum -c -
-
tar -C /usr/local -xzf go1.5.2.linux-amd64.tar.gz
-
t
est -f /.dockerinit && rm -rf /usr/local/go && t
ar -C /usr/local -xzf go1.5.2.linux-amd64.tar.gz
-
export PATH=/usr/local/go/bin:$PATH
-
export PATH=/usr/local/go/bin:$PATH
test
:
test
:
...
...
main.go
View file @
6a2dc7c6
...
@@ -50,7 +50,9 @@ const projectPattern = `^/[^/]+/[^/]+/`
...
@@ -50,7 +50,9 @@ const projectPattern = `^/[^/]+/[^/]+/`
const
gitProjectPattern
=
`^/[^/]+/[^/]+\.git/`
const
gitProjectPattern
=
`^/[^/]+/[^/]+\.git/`
const
apiPattern
=
`^/api/`
const
apiPattern
=
`^/api/`
const
projectsAPIPattern
=
`^/api/v3/projects/[^/]+/`
// A project ID in an API request is either a number or two strings 'namespace/project'
const
projectsAPIPattern
=
`^/api/v3/projects/(\d+)|([^/]+/[^/]+)/`
const
ciAPIPattern
=
`^/ci/api/`
const
ciAPIPattern
=
`^/ci/api/`
...
@@ -98,6 +100,16 @@ var httpRoutes = [...]httpRoute{
...
@@ -98,6 +100,16 @@ var httpRoutes = [...]httpRoute{
),
),
},
},
// For legacy reasons, user uploads are stored under the document root.
// To prevent anybody who knows/guesses the URL of a user-uploaded file
// from downloading it we make sure requests to /uploads/ do _not_ pass
// through handleServeFile.
httpRoute
{
""
,
regexp
.
MustCompile
(
`^/uploads/`
),
handleRailsError
(
documentRoot
,
proxyRequest
,
),
},
// Serve static files or forward the requests
// Serve static files or forward the requests
httpRoute
{
""
,
nil
,
httpRoute
{
""
,
nil
,
handleServeFile
(
documentRoot
,
CacheDisabled
,
handleServeFile
(
documentRoot
,
CacheDisabled
,
...
...
main_test.go
View file @
6a2dc7c6
...
@@ -4,6 +4,7 @@ import (
...
@@ -4,6 +4,7 @@ import (
"bytes"
"bytes"
"encoding/json"
"encoding/json"
"fmt"
"fmt"
"io"
"io/ioutil"
"io/ioutil"
"log"
"log"
"net/http"
"net/http"
...
@@ -19,6 +20,7 @@ import (
...
@@ -19,6 +20,7 @@ import (
const
scratchDir
=
"test/scratch"
const
scratchDir
=
"test/scratch"
const
testRepoRoot
=
"test/data"
const
testRepoRoot
=
"test/data"
const
testDocumentRoot
=
"test/public"
const
testRepo
=
"group/test.git"
const
testRepo
=
"group/test.git"
const
testProject
=
"group/test"
const
testProject
=
"group/test"
...
@@ -285,6 +287,143 @@ func TestDeniedXSendfileDownload(t *testing.T) {
...
@@ -285,6 +287,143 @@ func TestDeniedXSendfileDownload(t *testing.T) {
deniedXSendfileDownload
(
t
,
contentFilename
,
"foo/uploads/bar"
)
deniedXSendfileDownload
(
t
,
contentFilename
,
"foo/uploads/bar"
)
}
}
func
TestAllowedStaticFile
(
t
*
testing
.
T
)
{
content
:=
"PUBLIC"
if
err
:=
setupStaticFile
(
"static file.txt"
,
content
);
err
!=
nil
{
t
.
Fatalf
(
"create public/static file.txt: %v"
,
err
)
}
proxied
:=
false
ts
:=
testServerWithHandler
(
regexp
.
MustCompile
(
`.`
),
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
proxied
=
true
w
.
WriteHeader
(
404
)
})
defer
ts
.
Close
()
ws
:=
startWorkhorseServer
(
ts
.
URL
)
defer
ws
.
Close
()
for
_
,
resource
:=
range
[]
string
{
"/static%20file.txt"
,
"/static file.txt"
,
}
{
resp
,
err
:=
http
.
Get
(
ws
.
URL
+
resource
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
defer
resp
.
Body
.
Close
()
buf
:=
&
bytes
.
Buffer
{}
if
_
,
err
:=
io
.
Copy
(
buf
,
resp
.
Body
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
buf
.
String
()
!=
content
{
t
.
Fatalf
(
"GET %q: Expected %q, got %q"
,
resource
,
content
,
buf
.
String
())
}
if
resp
.
StatusCode
!=
200
{
t
.
Fatalf
(
"GET %q: expected 200, got %d"
,
resource
,
resp
.
StatusCode
)
}
if
proxied
{
t
.
Fatalf
(
"GET %q: should not have made it to backend"
,
resource
)
}
}
}
func
TestAllowedPublicUploadsFile
(
t
*
testing
.
T
)
{
content
:=
"PRIVATE but allowed"
if
err
:=
setupStaticFile
(
"uploads/static file.txt"
,
content
);
err
!=
nil
{
t
.
Fatalf
(
"create public/uploads/static file.txt: %v"
,
err
)
}
proxied
:=
false
ts
:=
testServerWithHandler
(
regexp
.
MustCompile
(
`.`
),
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
proxied
=
true
w
.
Header
()
.
Add
(
"X-Sendfile"
,
*
documentRoot
+
r
.
URL
.
Path
)
w
.
WriteHeader
(
200
)
})
defer
ts
.
Close
()
ws
:=
startWorkhorseServer
(
ts
.
URL
)
defer
ws
.
Close
()
for
_
,
resource
:=
range
[]
string
{
"/uploads/static%20file.txt"
,
"/uploads/static file.txt"
,
}
{
resp
,
err
:=
http
.
Get
(
ws
.
URL
+
resource
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
defer
resp
.
Body
.
Close
()
buf
:=
&
bytes
.
Buffer
{}
if
_
,
err
:=
io
.
Copy
(
buf
,
resp
.
Body
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
buf
.
String
()
!=
content
{
t
.
Fatalf
(
"GET %q: Expected %q, got %q"
,
resource
,
content
,
buf
.
String
())
}
if
resp
.
StatusCode
!=
200
{
t
.
Fatalf
(
"GET %q: expected 200, got %d"
,
resource
,
resp
.
StatusCode
)
}
if
!
proxied
{
t
.
Fatalf
(
"GET %q: never made it to backend"
,
resource
)
}
}
}
func
TestDeniedPublicUploadsFile
(
t
*
testing
.
T
)
{
content
:=
"PRIVATE"
if
err
:=
setupStaticFile
(
"uploads/static.txt"
,
content
);
err
!=
nil
{
t
.
Fatalf
(
"create public/uploads/static.txt: %v"
,
err
)
}
proxied
:=
false
ts
:=
testServerWithHandler
(
regexp
.
MustCompile
(
`.`
),
func
(
w
http
.
ResponseWriter
,
_
*
http
.
Request
)
{
proxied
=
true
w
.
WriteHeader
(
404
)
})
defer
ts
.
Close
()
ws
:=
startWorkhorseServer
(
ts
.
URL
)
defer
ws
.
Close
()
for
_
,
resource
:=
range
[]
string
{
"/uploads/static.txt"
,
"/uploads%2Fstatic.txt"
,
}
{
resp
,
err
:=
http
.
Get
(
ws
.
URL
+
resource
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
defer
resp
.
Body
.
Close
()
buf
:=
&
bytes
.
Buffer
{}
if
_
,
err
:=
io
.
Copy
(
buf
,
resp
.
Body
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
buf
.
String
()
==
content
{
t
.
Fatalf
(
"GET %q: Got private file contents which should have been blocked by upstream"
,
resource
)
}
if
resp
.
StatusCode
!=
404
{
t
.
Fatalf
(
"GET %q: expected 404, got %d"
,
resource
,
resp
.
StatusCode
)
}
if
!
proxied
{
t
.
Fatalf
(
"GET %q: never made it to backend"
,
resource
)
}
}
}
func
setupStaticFile
(
fpath
,
content
string
)
error
{
cwd
,
err
:=
os
.
Getwd
()
if
err
!=
nil
{
return
err
}
*
documentRoot
=
path
.
Join
(
cwd
,
testDocumentRoot
)
if
err
:=
os
.
MkdirAll
(
path
.
Join
(
*
documentRoot
,
path
.
Dir
(
fpath
)),
0755
);
err
!=
nil
{
return
err
}
static_file
:=
path
.
Join
(
*
documentRoot
,
fpath
)
if
err
:=
ioutil
.
WriteFile
(
static_file
,
[]
byte
(
content
),
0666
);
err
!=
nil
{
return
err
}
return
nil
}
func
prepareDownloadDir
(
t
*
testing
.
T
)
{
func
prepareDownloadDir
(
t
*
testing
.
T
)
{
if
err
:=
os
.
RemoveAll
(
scratchDir
);
err
!=
nil
{
if
err
:=
os
.
RemoveAll
(
scratchDir
);
err
!=
nil
{
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
...
...
servefile.go
View file @
6a2dc7c6
...
@@ -16,6 +16,9 @@ const (
...
@@ -16,6 +16,9 @@ const (
CacheExpireMax
CacheExpireMax
)
)
// BUG/QUIRK: If a client requests 'foo%2Fbar' and 'foo/bar' exists,
// handleServeFile will serve foo/bar instead of passing the request
// upstream.
func
handleServeFile
(
documentRoot
*
string
,
cache
CacheMode
,
notFoundHandler
serviceHandleFunc
)
serviceHandleFunc
{
func
handleServeFile
(
documentRoot
*
string
,
cache
CacheMode
,
notFoundHandler
serviceHandleFunc
)
serviceHandleFunc
{
return
func
(
w
http
.
ResponseWriter
,
r
*
gitRequest
)
{
return
func
(
w
http
.
ResponseWriter
,
r
*
gitRequest
)
{
file
:=
filepath
.
Join
(
*
documentRoot
,
r
.
relativeURIPath
)
file
:=
filepath
.
Join
(
*
documentRoot
,
r
.
relativeURIPath
)
...
...
upstream.go
View file @
6a2dc7c6
...
@@ -107,7 +107,7 @@ func (u *upstream) ServeHTTP(ow http.ResponseWriter, r *http.Request) {
...
@@ -107,7 +107,7 @@ func (u *upstream) ServeHTTP(ow http.ResponseWriter, r *http.Request) {
}
}
// Check URL Root
// Check URL Root
URIPath
:=
cleanURIPath
(
r
.
URL
.
EscapedPath
()
)
URIPath
:=
cleanURIPath
(
r
.
URL
.
Path
)
if
!
strings
.
HasPrefix
(
URIPath
,
u
.
relativeURLRoot
)
&&
URIPath
+
"/"
!=
u
.
relativeURLRoot
{
if
!
strings
.
HasPrefix
(
URIPath
,
u
.
relativeURLRoot
)
&&
URIPath
+
"/"
!=
u
.
relativeURLRoot
{
httpError
(
&
w
,
r
,
fmt
.
Sprintf
(
"Not found %q"
,
URIPath
),
http
.
StatusNotFound
)
httpError
(
&
w
,
r
,
fmt
.
Sprintf
(
"Not found %q"
,
URIPath
),
http
.
StatusNotFound
)
return
return
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment