Start moving policy checks to service-time, rather than parse-time.
This commit is contained in:
parent
c684f7417d
commit
173b5c596e
|
@ -131,9 +131,9 @@ def _preview(is_request):
|
|||
args["pauses"] = r.preview_safe()
|
||||
|
||||
if is_request:
|
||||
r.serve(s, check=app.config["pathod"].check_policy, host="example.com")
|
||||
r.serve(app.config["pathod"].request_settings, s, check=app.config["pathod"].check_policy, host="example.com")
|
||||
else:
|
||||
r.serve(s, check=app.config["pathod"].check_policy)
|
||||
r.serve(app.config["pathod"].request_settings, s, check=app.config["pathod"].check_policy)
|
||||
|
||||
args["output"] = utils.escape_unprintables(s.getvalue())
|
||||
return render(template, False, **args)
|
||||
|
|
|
@ -25,19 +25,6 @@ class ParseException(Exception):
|
|||
return "%s at char %s"%(self.msg, self.col)
|
||||
|
||||
|
||||
def ready_actions(length, lst):
|
||||
ret = []
|
||||
for i in lst:
|
||||
itms = list(i)
|
||||
if i[0] == "r":
|
||||
itms[0] = random.randrange(length)
|
||||
elif i[0] == "a":
|
||||
itms[0] = length+1
|
||||
ret.append(tuple(itms))
|
||||
ret.sort()
|
||||
return ret
|
||||
|
||||
|
||||
def send_chunk(fp, val, blocksize, start, end):
|
||||
"""
|
||||
(start, end): Inclusive lower bound, exclusive upper bound.
|
||||
|
@ -441,6 +428,9 @@ class _Action:
|
|||
|
||||
def __repr__(self):
|
||||
return self.spec()
|
||||
|
||||
def accept(self, settings, r):
|
||||
r.actions.append(self)
|
||||
|
||||
|
||||
class PauseAt(_Action):
|
||||
|
@ -464,17 +454,14 @@ class PauseAt(_Action):
|
|||
def spec(self):
|
||||
return "p%s,%s"%(self.offset, self.seconds)
|
||||
|
||||
def accept(self, settings, r):
|
||||
r.actions.append((self.offset, "pause", self.seconds))
|
||||
def intermediate(self, settings):
|
||||
return (self.offset, "pause", self.seconds)
|
||||
|
||||
|
||||
class DisconnectAt(_Action):
|
||||
def __init__(self, offset):
|
||||
_Action.__init__(self, offset)
|
||||
|
||||
def accept(self, settings, r):
|
||||
r.actions.append((self.offset, "disconnect"))
|
||||
|
||||
@classmethod
|
||||
def expr(klass):
|
||||
e = pp.Literal("d").suppress()
|
||||
|
@ -484,6 +471,9 @@ class DisconnectAt(_Action):
|
|||
def spec(self):
|
||||
return "d%s"%self.offset
|
||||
|
||||
def intermediate(self, settings):
|
||||
return (self.offset, "disconnect")
|
||||
|
||||
|
||||
class InjectAt(_Action):
|
||||
def __init__(self, offset, value):
|
||||
|
@ -501,14 +491,12 @@ class InjectAt(_Action):
|
|||
def spec(self):
|
||||
return "i%s,%s"%(self.offset, self.value.spec())
|
||||
|
||||
def accept(self, settings, r):
|
||||
r.actions.append(
|
||||
(
|
||||
def intermediate(self, settings):
|
||||
return (
|
||||
self.offset,
|
||||
"inject",
|
||||
self.value.get_generator(settings)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class Header:
|
||||
|
@ -577,8 +565,8 @@ class Message:
|
|||
"""
|
||||
Modify this message to be safe for previews. Returns a list of elided actions.
|
||||
"""
|
||||
pauses = [i for i in self.actions if i[1] == "pause"]
|
||||
self.actions = [i for i in self.actions if i[1] != "pause"]
|
||||
pauses = [i for i in self.actions if isinstance(i, PauseAt)]
|
||||
self.actions = [i for i in self.actions if not isinstance(i, PauseAt)]
|
||||
return pauses
|
||||
|
||||
def effective_length(self, actions):
|
||||
|
@ -595,7 +583,7 @@ class Message:
|
|||
l += len(i[2])
|
||||
return l
|
||||
|
||||
def serve(self, fp, check, request_host):
|
||||
def serve(self, settings, fp, check, request_host):
|
||||
"""
|
||||
fp: The file pointer to write to.
|
||||
|
||||
|
@ -652,13 +640,15 @@ class Message:
|
|||
if self.body:
|
||||
vals.append(self.body)
|
||||
vals.reverse()
|
||||
actions = ready_actions(self.length(), self.actions)
|
||||
actions = [i.resolve_offset(self) for i in self.actions]
|
||||
actions.sort()
|
||||
actions.reverse()
|
||||
actions = [i.intermediate(settings) for i in actions]
|
||||
if check:
|
||||
ret = check(self, actions)
|
||||
if ret:
|
||||
err = PathodErrorResponse(ret)
|
||||
err.serve(fp)
|
||||
err.serve(settings, fp)
|
||||
return dict(
|
||||
disconnect = True,
|
||||
error = ret
|
||||
|
@ -767,8 +757,8 @@ class CraftedRequest(Request):
|
|||
for i in tokens:
|
||||
i.accept(settings, self)
|
||||
|
||||
def serve(self, fp, check, host):
|
||||
d = Request.serve(self, fp, check, host)
|
||||
def serve(self, settings, fp, check, host):
|
||||
d = Request.serve(self, settings, fp, check, host)
|
||||
d["spec"] = self.spec
|
||||
return d
|
||||
|
||||
|
@ -780,8 +770,8 @@ class CraftedResponse(Response):
|
|||
for i in tokens:
|
||||
i.accept(settings, self)
|
||||
|
||||
def serve(self, fp, check):
|
||||
d = Response.serve(self, fp, check, None)
|
||||
def serve(self, settings, fp, check):
|
||||
d = Response.serve(self, settings, fp, check, None)
|
||||
d["spec"] = self.spec
|
||||
return d
|
||||
|
||||
|
@ -798,8 +788,8 @@ class PathodErrorResponse(Response):
|
|||
),
|
||||
]
|
||||
|
||||
def serve(self, fp, check=None):
|
||||
d = Response.serve(self, fp, check, None)
|
||||
def serve(self, settings, fp, check=None):
|
||||
d = Response.serve(self, settings, fp, check, None)
|
||||
d["internal"] = True
|
||||
return d
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ class Pathoc(tcp.TCPClient):
|
|||
language.FileAccessDenied.
|
||||
"""
|
||||
r = language.parse_request(self.settings, spec)
|
||||
ret = r.serve(self.wfile, None, self.host)
|
||||
ret = r.serve(self.settings, self.wfile, None, self.host)
|
||||
self.wfile.flush()
|
||||
return http.read_response(self.rfile, r.method, None)
|
||||
|
||||
|
@ -68,7 +68,7 @@ class Pathoc(tcp.TCPClient):
|
|||
if showresp:
|
||||
self.rfile.start_log()
|
||||
try:
|
||||
req = r.serve(self.wfile, None, self.host)
|
||||
req = r.serve(self.settings, self.wfile, None, self.host)
|
||||
self.wfile.flush()
|
||||
resp = http.read_response(self.rfile, r.method, None)
|
||||
except http.HttpError, v:
|
||||
|
|
|
@ -18,7 +18,7 @@ class PathodHandler(tcp.BaseHandler):
|
|||
self.sni = connection.get_servername()
|
||||
|
||||
def serve_crafted(self, crafted, request_log):
|
||||
response_log = crafted.serve(self.wfile, self.server.check_policy)
|
||||
response_log = crafted.serve(self.server.request_settings, self.wfile, self.server.check_policy)
|
||||
log = dict(
|
||||
type = "crafted",
|
||||
request=request_log,
|
||||
|
@ -96,7 +96,7 @@ class PathodHandler(tcp.BaseHandler):
|
|||
return self.serve_crafted(crafted, request_log)
|
||||
elif self.server.noweb:
|
||||
crafted = language.PathodErrorResponse("Access Denied")
|
||||
crafted.serve(self.wfile, self.server.check_policy)
|
||||
crafted.serve(self.server.request_settings, self.wfile, self.server.check_policy)
|
||||
return False, dict(type = "error", msg="Access denied: web interface disabled")
|
||||
else:
|
||||
self.info("app: %s %s"%(method, path))
|
||||
|
|
|
@ -206,7 +206,7 @@ class TestMisc:
|
|||
def test_internal_response(self):
|
||||
d = cStringIO.StringIO()
|
||||
s = language.PathodErrorResponse("foo")
|
||||
s.serve(d)
|
||||
s.serve({}, d)
|
||||
|
||||
|
||||
class Test_Action:
|
||||
|
@ -229,8 +229,10 @@ class Test_Action:
|
|||
|
||||
class TestDisconnects:
|
||||
def test_parse_response(self):
|
||||
assert (0, "disconnect") in language.parse_response({}, "400:d0").actions
|
||||
assert ("r", "disconnect") in language.parse_response({}, "400:dr").actions
|
||||
a = language.parse_response({}, "400:d0").actions[0]
|
||||
assert a.spec() == "d0"
|
||||
a = language.parse_response({}, "400:dr").actions[0]
|
||||
assert a.spec() == "dr"
|
||||
|
||||
def test_at(self):
|
||||
e = language.DisconnectAt.expr()
|
||||
|
@ -253,12 +255,12 @@ class TestDisconnects:
|
|||
class TestInject:
|
||||
def test_parse_response(self):
|
||||
a = language.parse_response({}, "400:ir,@100").actions[0]
|
||||
assert a[0] == "r"
|
||||
assert a[1] == "inject"
|
||||
assert a.offset == "r"
|
||||
assert a.value.datatype == "bytes"
|
||||
assert a.value.usize == 100
|
||||
|
||||
a = language.parse_response({}, "400:ia,@100").actions[0]
|
||||
assert a[0] == "a"
|
||||
assert a[1] == "inject"
|
||||
assert a.offset == "a"
|
||||
|
||||
def test_at(self):
|
||||
e = language.InjectAt.expr()
|
||||
|
@ -273,7 +275,7 @@ class TestInject:
|
|||
def test_serve(self):
|
||||
s = cStringIO.StringIO()
|
||||
r = language.parse_response({}, "400:i0,'foo'")
|
||||
assert r.serve(s, None)
|
||||
assert r.serve({}, s, None)
|
||||
|
||||
def test_spec(self):
|
||||
e = language.InjectAt.expr()
|
||||
|
@ -281,7 +283,6 @@ class TestInject:
|
|||
assert v.spec() == 'i0,"foo"'
|
||||
|
||||
|
||||
|
||||
class TestPauses:
|
||||
def test_parse_response(self):
|
||||
e = language.PauseAt.expr()
|
||||
|
@ -300,7 +301,7 @@ class TestPauses:
|
|||
|
||||
def test_request(self):
|
||||
r = language.parse_response({}, '400:p10,10')
|
||||
assert r.actions[0] == (10, "pause", 10)
|
||||
assert r.actions[0].spec() == "p10,10"
|
||||
|
||||
def test_spec(self):
|
||||
assert language.PauseAt("r", 5).spec() == "pr,5"
|
||||
|
@ -336,7 +337,7 @@ class TestParseRequest:
|
|||
def test_render(self):
|
||||
s = cStringIO.StringIO()
|
||||
r = language.parse_request({}, "GET:'/foo'")
|
||||
assert r.serve(s, None, "foo.com")
|
||||
assert r.serve({}, s, None, "foo.com")
|
||||
|
||||
def test_str(self):
|
||||
r = language.parse_request({}, 'GET:"/foo"')
|
||||
|
@ -386,15 +387,15 @@ class TestParseResponse:
|
|||
|
||||
def test_parse_pause_before(self):
|
||||
r = language.parse_response({}, "400:p0,10")
|
||||
assert (0, "pause", 10) in r.actions
|
||||
assert r.actions[0].spec() == "p0,10"
|
||||
|
||||
def test_parse_pause_after(self):
|
||||
r = language.parse_response({}, "400:pa,10")
|
||||
assert ("a", "pause", 10) in r.actions
|
||||
assert r.actions[0].spec() == "pa,10"
|
||||
|
||||
def test_parse_pause_random(self):
|
||||
r = language.parse_response({}, "400:pr,10")
|
||||
assert ("r", "pause", 10) in r.actions
|
||||
assert r.actions[0].spec() == "pr,10"
|
||||
|
||||
def test_parse_stress(self):
|
||||
r = language.parse_response({}, "400:b@100g")
|
||||
|
@ -468,34 +469,18 @@ class TestWriteValues:
|
|||
def test_write_values_after(self):
|
||||
s = cStringIO.StringIO()
|
||||
r = language.parse_response({}, "400:da")
|
||||
r.serve(s, None)
|
||||
r.serve({}, s, None)
|
||||
|
||||
s = cStringIO.StringIO()
|
||||
r = language.parse_response({}, "400:pa,0")
|
||||
r.serve(s, None)
|
||||
r.serve({}, s, None)
|
||||
|
||||
s = cStringIO.StringIO()
|
||||
r = language.parse_response({}, "400:ia,'xx'")
|
||||
r.serve(s, None)
|
||||
r.serve({}, s, None)
|
||||
assert s.getvalue().endswith('xx')
|
||||
|
||||
|
||||
def test_ready_actions():
|
||||
x = [(0, 5)]
|
||||
assert language.ready_actions(100, x) == x
|
||||
|
||||
x = [("r", 5)]
|
||||
ret = language.ready_actions(100, x)
|
||||
assert 0 <= ret[0][0] < 100
|
||||
|
||||
x = [("a", "pause", 5)]
|
||||
ret = language.ready_actions(100, x)
|
||||
assert ret[0][0] > 100
|
||||
|
||||
x = [(1, 5), (0, 5)]
|
||||
assert language.ready_actions(100, x) == sorted(x)
|
||||
|
||||
|
||||
class TestResponse:
|
||||
def dummy_response(self):
|
||||
return language.parse_response({}, "400'msg'")
|
||||
|
@ -521,24 +506,24 @@ class TestResponse:
|
|||
r = language.parse_response({}, "400:b@100k")
|
||||
def check(req, acts):
|
||||
return "errmsg"
|
||||
assert r.serve(s, check=check)["error"] == "errmsg"
|
||||
assert r.serve({}, s, check=check)["error"] == "errmsg"
|
||||
|
||||
def test_render(self):
|
||||
s = cStringIO.StringIO()
|
||||
r = language.parse_response({}, "400'msg'")
|
||||
assert r.serve(s, None)
|
||||
assert r.serve({}, s, None)
|
||||
|
||||
def test_raw(self):
|
||||
s = cStringIO.StringIO()
|
||||
r = language.parse_response({}, "400:b'foo'")
|
||||
r.serve(s, None)
|
||||
r.serve({}, s, None)
|
||||
v = s.getvalue()
|
||||
assert "Content-Length" in v
|
||||
assert "Date" in v
|
||||
|
||||
s = cStringIO.StringIO()
|
||||
r = language.parse_response({}, "400:b'foo':r")
|
||||
r.serve(s, None)
|
||||
r.serve({}, s, None)
|
||||
v = s.getvalue()
|
||||
assert not "Content-Length" in v
|
||||
assert not "Date" in v
|
||||
|
@ -546,46 +531,48 @@ class TestResponse:
|
|||
def test_length(self):
|
||||
def testlen(x):
|
||||
s = cStringIO.StringIO()
|
||||
x.serve(s, None)
|
||||
x.serve({}, s, None)
|
||||
assert x.length() == len(s.getvalue())
|
||||
testlen(language.parse_response({}, "400'msg'"))
|
||||
testlen(language.parse_response({}, "400'msg':h'foo'='bar'"))
|
||||
testlen(language.parse_response({}, "400'msg':h'foo'='bar':b@100b"))
|
||||
|
||||
def test_effective_length(self):
|
||||
l = [None]
|
||||
def check(req, actions):
|
||||
l[0] = req.effective_length(actions)
|
||||
|
||||
def testlen(x, actions):
|
||||
s = cStringIO.StringIO()
|
||||
x.serve(s, None)
|
||||
assert x.effective_length(actions) == len(s.getvalue())
|
||||
actions = [
|
||||
x.serve({}, s, check)
|
||||
assert l[0] == len(s.getvalue())
|
||||
|
||||
]
|
||||
r = language.parse_response({}, "400'msg':b@100")
|
||||
|
||||
actions = [
|
||||
(0, "disconnect"),
|
||||
language.DisconnectAt(0)
|
||||
]
|
||||
r.actions = actions
|
||||
testlen(r, actions)
|
||||
|
||||
actions = [
|
||||
(0, "disconnect"),
|
||||
(0, "inject", "foo")
|
||||
language.DisconnectAt(0),
|
||||
language.InjectAt(0, language.ValueLiteral("foo"))
|
||||
]
|
||||
r.actions = actions
|
||||
testlen(r, actions)
|
||||
|
||||
actions = [
|
||||
(0, "inject", "foo")
|
||||
language.InjectAt(0, language.ValueLiteral("foo"))
|
||||
]
|
||||
r.actions = actions
|
||||
testlen(r, actions)
|
||||
|
||||
def test_render(self):
|
||||
r = language.parse_response({}, "400:p0,100:dr")
|
||||
assert r.actions[0][1] == "pause"
|
||||
assert r.actions[0].spec() == "p0,100"
|
||||
assert len(r.preview_safe()) == 1
|
||||
assert not r.actions[0][1] == "pause"
|
||||
assert not r.actions[0].spec().startswith("p")
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue