-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
feat(fs): add safeguard to ensure all paths are within expected directory #16125
Conversation
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.
Should we warn for each of these? Ie they aren't expect yo occur so are either something malicious or will be surfacing bugs we would have otherwise missed?
If unusual or malicious, it may useful to see that in logs too. I guess that more covert bugs or edge cases with artifact updates may simply go unnoticed otherwise. |
Co-authored-by: Rhys Arkins <rhys@arkins.net>
we also should make those properties mandatory, as they never should be |
Probably in another PR? That change would affect > 20 tests and it's not really related to this safeguard but more of a general thing... |
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.
I like the concept of file traversal guarding is introduced in the code now, and further improvements can be made in separate PRs. This one looks good to me.
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.
i do not agree @zharinov we should make this as safe as possible now. I'm pretty sure it will be forgotten to do.
My point was to wait for this PR to be merged, so I'll be able to apply these safeguards to the functions of |
ok, sounds good 👍 |
function isPathInBaseDir(path: string, baseDir: string): boolean { | ||
if (!path.startsWith(upath.resolve(baseDir))) { | ||
logger.warn( | ||
{ path, baseDir }, | ||
'Preventing access to file outside the base directory' | ||
); | ||
return false; | ||
} | ||
|
||
return true; |
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.
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.
"abort further processing" = stop running on the repo?
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.
yes
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.
Sounds reasonable + further we need to ensure we don't use fs
and fs-extra
directly (i.e. migrate and add lint rule)
Changes
A preemptive check to ensure that potentially insecure relative or absolute paths are strictly contained within an expected folder. This is a safeguard to prevent potential path traversal attacks that may occur in case paths from processed repositories are used in file system operations.
node provides a guideline to prevent directory traversal. With an arbitrary path in
fileName
, the check could be simply:upath.join()
concats the two parameters and normalizes the result (see here).This means that
upath.join('/tmp/renovate', '../../etc/passwd')
becomes/etc/passwd
The only issue with
path.join()
is that this breaks when file system logic like.
or./
is used. E.g., withlocalDir = '.'
andfileName = 'asdf'
, the join result is simply asdf and thestartsWith
check would fail. This would be the case with the tests in https://github.com/renovatebot/renovate/blob/main/lib/modules/manager/npm/post-update/yarn.spec.ts(Note: Changing these tests to use '' instead of '.' would essentially be an alternative to changing all join ops to resolve)
The alternative used in this PR is
path.resolve()
, which basically enforces that all paths are relative tolocalDir
. Good examples on the differences betweenjoin
andresolve
can also be found here.Test repository: https://github.com/Churro/renovate-applyfrom-traversal/blob/master/dummy/build.gradle
-> only works so far when the commit in this PR is applied on #16030
Output:
What can be noticed is that wandering around directories using
../
still works unless you leave the base dir. Other absolute paths are rewritten bypath.resolve()
to be relative to base dir.Context
Related to #16030 (comment)
Documentation (please check one with an [x])
How I've tested my work (please tick one)
I have verified these changes via: