os.path.vnormpath

B

Ben Allfree

Written to normalize web server path names based on a virtual root. I
propose that something equivalent to this be added to os.path
-------------------------------------
import os.path
import copy

def vnormpath(root,path):
"""
Normalize a path based on a virtual root.
"""
r = copy.deepcopy(root)
p = copy.deepcopy(path)

if os.path.isabs(path):
return root + path
while os.path.commonprefix([root, os.path.normpath(os.path.join(r,p))])
<> root:
r = os.path.join(r,"junk")
return os.path.normpath(os.path.join(r,p))


if __name__ == "__main__":
print vnormpath("C:\\foo\\baz",
"..\\..\\..\\..\\foo\\baz\\..\\..\\..\\frob\\glop")
 
P

Peter Otten

Ben said:
Written to normalize web server path names based on a virtual root. I
propose that something equivalent to this be added to os.path

The purpose remains unclear to me. Maybe you could expand a little.
For now, I suppose you intend vnormpath() to always return subfolders of
root.
-------------------------------------
import os.path
import copy

def vnormpath(root,path):
"""
Normalize a path based on a virtual root.
"""
r = copy.deepcopy(root)
p = copy.deepcopy(path)

Strings are "immutable", i. e. you can't change them once created. You
therefore need not (I'd rather say must not) copy them.
if os.path.isabs(path):
return root + path

This can give you funny paths on Windows like "C:/firstC:/second"; on Unix
you can get anywhere the supplier of *path* wants,
e. g. vnormpath("/home/peter", "/../ben") returns "/home/peter/../ben" which
is equivalent to "home/ben".
while os.path.commonprefix([root,
os.path.normpath(os.path.join(r,p))])
<> root:
r = os.path.join(r,"junk")
return os.path.normpath(os.path.join(r,p))

I tried to break that, too, but must admit I didn't succed so far :)
By the way said:
if __name__ == "__main__":
print vnormpath("C:\\foo\\baz",
"..\\..\\..\\..\\foo\\baz\\..\\..\\..\\frob\\glop")

The above demo is probably a good start, but try to think of a few more
use/test cases. For example, should
vnormpath("/home/ben", "/home/ben/temp") return "/home/ben/home/ben/temp" or
rather "home/ben/temp"? Also, I would compare vnormpath()'s actual against
the expected result instead of just printing it out. For production quality
code, have a look at the unittest module.

Conclusion: For the moment, be satisfied if you can fix the bugs and your
function serves *your* needs well. If you want to share the results, put it
on a web site.
For your code to make it into the python library, you have to convince
people that *they* need it badly and I dare say that you may face some
strong opposition.

Peter
 
B

Ben Allfree

Correct. vnormpath() should enforce the same rules web servers resolves a
virtual paths on the server side.

Example:

Virtual root - C:\foo\baz\wwwroot
URL - <a href="..\grop.html">

The above should never resolve to anything higher than
C:\foo\baz\wwwroot\grop.html regardless of its position in the web root
directory.

vnormpath("C:\\foo\\baz\\wwwroot", "..\\grop.html") ==
"C:\\foo\\baz\\wwwroot\\grop.html"

Likewise (here's a catch):

Virtual root - C:\foo\baz\wwwroot
URL - <a href="\..\..\baz\wwwroot\grop.html">

Should resolve to C:\foo\baz\wwwroot\baz\wwwroot\grop.html

I hope that make sense. My code is a start at solving the issue.

Peter Otten said:
Ben said:
Written to normalize web server path names based on a virtual root. I
propose that something equivalent to this be added to os.path

The purpose remains unclear to me. Maybe you could expand a little.
For now, I suppose you intend vnormpath() to always return subfolders of
root.
-------------------------------------
import os.path
import copy

def vnormpath(root,path):
"""
Normalize a path based on a virtual root.
"""
r = copy.deepcopy(root)
p = copy.deepcopy(path)

Strings are "immutable", i. e. you can't change them once created. You
therefore need not (I'd rather say must not) copy them.
if os.path.isabs(path):
return root + path

This can give you funny paths on Windows like "C:/firstC:/second"; on Unix
you can get anywhere the supplier of *path* wants,
e. g. vnormpath("/home/peter", "/../ben") returns "/home/peter/../ben" which
is equivalent to "home/ben".
while os.path.commonprefix([root,
os.path.normpath(os.path.join(r,p))])
<> root:
r = os.path.join(r,"junk")
return os.path.normpath(os.path.join(r,p))

I tried to break that, too, but must admit I didn't succed so far :)
By the way said:
if __name__ == "__main__":
print vnormpath("C:\\foo\\baz",
"..\\..\\..\\..\\foo\\baz\\..\\..\\..\\frob\\glop")

The above demo is probably a good start, but try to think of a few more
use/test cases. For example, should
vnormpath("/home/ben", "/home/ben/temp") return "/home/ben/home/ben/temp" or
rather "home/ben/temp"? Also, I would compare vnormpath()'s actual against
the expected result instead of just printing it out. For production quality
code, have a look at the unittest module.

Conclusion: For the moment, be satisfied if you can fix the bugs and your
function serves *your* needs well. If you want to share the results, put it
on a web site.
For your code to make it into the python library, you have to convince
people that *they* need it badly and I dare say that you may face some
strong opposition.

Peter
 
J

JanC

Ben Allfree said:
Virtual root - C:\foo\baz\wwwroot
URL - <a href="\..\..\baz\wwwroot\grop.html">

Should resolve to C:\foo\baz\wwwroot\baz\wwwroot\grop.html

According to RFC-2396 this should resolve to:

C:\foo\baz\wwwroot\..\..\baz\wwwroot\grop.html

The ".." above are real path segments/directory names and don't have the
special "go up 1 level" meaning.

IMHO for web servers that map URL path segments to local paths this should
result in an HTTP error (when ".." is not allowed on the local filesystem).

They also mention that in practice your solution is in use by some parsers,
so people might not be too surprised by its functioning... ;-)


<http://www.cs.tut.fi/~jkorpela/rfc/2396/full.html#C>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,169
Messages
2,570,920
Members
47,464
Latest member
Bobbylenly

Latest Threads

Top