Allow nesting of pathod response specs in pathoc specs

This opens the door to really neat, repeatable, client-side driven
fuzzing, especially of proxies.
This commit is contained in:
Aldo Cortesi 2014-10-26 10:50:32 +13:00
parent c00ae41486
commit fc1fc80469
3 changed files with 73 additions and 5 deletions

View File

@ -1,3 +1,4 @@
from __future__ import print_function
import operator import operator
import string import string
import random import random
@ -6,7 +7,6 @@ import os
import time import time
import copy import copy
import abc import abc
from email.utils import formatdate
import contrib.pyparsing as pp import contrib.pyparsing as pp
from netlib import http_status, tcp, http_uastrings from netlib import http_status, tcp, http_uastrings
@ -527,6 +527,43 @@ class Body(_Component):
return Body(self.value.freeze(settings)) return Body(self.value.freeze(settings))
class PathodSpec(_Token):
def __init__(self, value):
self.value = value
try:
self.parsed = Response(
Response.expr().parseString(
value.val,
parseAll=True
)
)
except pp.ParseException, v:
raise ParseException(v.msg, v.line, v.col)
@classmethod
def expr(klass):
e = pp.Literal("s").suppress()
e = e + ValueLiteral.expr()
return e.setParseAction(lambda x: klass(*x))
def values(self, settings):
return [
self.value.get_generator(settings),
]
def quote(self, s):
quotechar = s[0]
s = s[1:-1]
s = s.replace(quotechar, "\\" + quotechar)
return quotechar + s + quotechar
def spec(self):
return "s%s"%(self.quote(self.value.spec()))
def freeze(self, settings):
return PathodSpec(ValueLiteral(self.parsed.freeze(settings).spec()))
class Path(_Component): class Path(_Component):
def __init__(self, value): def __init__(self, value):
if isinstance(value, basestring): if isinstance(value, basestring):
@ -934,7 +971,8 @@ class Request(_Message):
InjectAt, InjectAt,
ShortcutContentType, ShortcutContentType,
ShortcutUserAgent, ShortcutUserAgent,
Raw Raw,
PathodSpec,
) )
logattrs = ["method", "path", "body"] logattrs = ["method", "path", "body"]
@ -946,10 +984,16 @@ class Request(_Message):
def path(self): def path(self):
return self._get_token(Path) return self._get_token(Path)
@property
def pathodspec(self):
return self._get_token(PathodSpec)
def preamble(self, settings): def preamble(self, settings):
v = self.method.values(settings) v = self.method.values(settings)
v.append(" ") v.append(" ")
v.extend(self.path.values(settings)) v.extend(self.path.values(settings))
if self.pathodspec:
v.append(self.pathodspec.parsed.spec())
v.append(" ") v.append(" ")
v.append(self.version) v.append(self.version)
return v return v

View File

@ -223,6 +223,29 @@ class TestMisc:
s = v.spec() s = v.spec()
assert s == e.parseString(s)[0].spec() assert s == e.parseString(s)[0].spec()
def test_pathodspec(self):
e = language.PathodSpec.expr()
v = e.parseString("s'200'")[0]
assert v.value.val == "200"
tutils.raises(
language.ParseException,
e.parseString,
"s'foo'"
)
v = e.parseString('s"200:b@1"')[0]
assert "@1" in v.spec()
f = v.freeze({})
assert "@1" not in f.spec()
r = parse_request('GET:"/foo":s"200"')
assert "200" in r.preamble({})
f = r.freeze({})
assert parse_request(f.spec())
def test_code(self): def test_code(self):
e = language.Code.expr() e = language.Code.expr()
v = e.parseString("200")[0] v = e.parseString("200")[0]
@ -661,14 +684,12 @@ class TestResponse:
language.serve(r, s, {}) language.serve(r, s, {})
v = s.getvalue() v = s.getvalue()
assert "Content-Length" in v assert "Content-Length" in v
assert "Date" in v
s = cStringIO.StringIO() s = cStringIO.StringIO()
r = language.parse_response("400:b'foo':r") r = language.parse_response("400:b'foo':r")
language.serve(r, s, {}) language.serve(r, s, {})
v = s.getvalue() v = s.getvalue()
assert not "Content-Length" in v assert not "Content-Length" in v
assert not "Date" in v
def test_length(self): def test_length(self):
def testlen(x): def testlen(x):

View File

@ -59,8 +59,11 @@ class _TestDaemon:
c.settimeout(timeout) c.settimeout(timeout)
s = cStringIO.StringIO() s = cStringIO.StringIO()
for i in requests: for i in requests:
r = language.parse_requests(i)[0]
if explain:
r = r.freeze({})
c.print_request( c.print_request(
language.parse_requests(i)[0], r,
showreq = showreq, showreq = showreq,
showresp = showresp, showresp = showresp,
explain = explain, explain = explain,