Expand capture-only NetScaler NITRO simulation and crash recovery
This commit is contained in:
1 parent
7b99db9e25
commit
c65f049223
10 files changed
+2097
-19
No files matched your search
@@ -0,0 +1,72 @@
|
||||
"""Run: python3 test_nitro.py. Inert API behaviour and exact upload capture."""
|
||||
import base64
|
||||
import hashlib
|
||||
import http.client
|
||||
import json
|
||||
import pathlib
|
||||
import tempfile
|
||||
import threading
|
||||
import urllib.parse
|
||||
from types import SimpleNamespace
|
||||
from evidence import Evidence
|
||||
from honeypot import Engine, Server
|
||||
from nitro import CONFIG, RESOURCES, respond
|
||||
|
||||
|
||||
def main():
|
||||
state = SimpleNamespace()
|
||||
auth = {}
|
||||
def api(method, path, data=None, headers=None, source=state):
|
||||
return respond(source, method, urllib.parse.urlsplit('/nitro/v1/'+path),
|
||||
json.dumps(data).encode() if data is not None else b'', headers if headers is not None else auth)
|
||||
assert api('GET', 'config/nsversion')[0] == 401
|
||||
assert api('POST', 'config/login', {'login': {}})[0] == 400
|
||||
status, login, extra = api('POST', 'config/login', {'login': {'username':'probe','password':'inert'}})
|
||||
assert status == 201 and extra[0][0] == 'Set-Cookie'
|
||||
auth = {'Cookie': 'NITRO_AUTH_TOKEN='+login['sessionid']}
|
||||
assert api('GET', 'config/nsversion')[1]['nsversion'][0]['version'].startswith('NetScaler NS')
|
||||
assert len(RESOURCES['config']) > 1400 and len(RESOURCES['stat']) > 150
|
||||
for kind in RESOURCES:
|
||||
for resource in RESOURCES[kind]:assert api('GET',kind+'/'+resource)[0] == 200, resource
|
||||
for resource in CONFIG:assert resource in api('GET', 'config/'+resource)[1]
|
||||
assert api('POST', 'config/lbvserver', {'lbvserver': {'name':'new','port':80}})[0] == 201
|
||||
assert api('GET', 'config/lbvserver/new')[1]['lbvserver'][0]['port'] == 80
|
||||
assert api('PUT', 'config/lbvserver', {'lbvserver': {'name':'new','port':8080}})[0] == 200
|
||||
assert api('GET', 'config/lbvserver/new')[1]['lbvserver'][0]['port'] == 8080
|
||||
assert api('POST', 'config/lbvserver?action=disable', {'lbvserver': {'name':'new'}})[0] == 200
|
||||
assert api('GET', 'config/lbvserver/new')[1]['lbvserver'][0]['state'] == 'DISABLED'
|
||||
assert api('GET', 'config/lbvserver?filter=name:new&attrs=name')[1]['lbvserver'] == [{'name':'new'}]
|
||||
assert api('GET', 'config/lbvserver?count=yes')[1]['lbvserver'] == [{'__count':2}]
|
||||
assert api('GET', 'config/lbvserver_binding/lb_apps')[1]['lbvserver_binding'][0]['lbvserver_service_binding'][0]['servicename'] == 'svc_app_https'
|
||||
assert api('GET', 'config/lbvserver?pagesize=1&pageno=2')[1]['lbvserver'][0]['name'] == 'new'
|
||||
assert api('GET', 'config/lbvserver?pagesize=bad')[0] == 400
|
||||
assert api('GET', 'config/nsip?'+ '&'.join('x=1' for _ in range(129)))[0] == 400
|
||||
assert api('GET', 'config/not_a_resource')[0] == 404
|
||||
assert api('DELETE', 'config/lbvserver/new')[0] == 200
|
||||
assert api('GET', 'config/lbvserver/new')[1]['lbvserver'] == []
|
||||
assert api('GET', 'config/lbvserver', source=SimpleNamespace(), headers={'X-NITRO-USER':'probe','X-NITRO-PASS':'inert'})[1]['lbvserver'] == CONFIG['lbvserver']
|
||||
assert api('POST', 'config/lbvserver', {'lbvserver':{'name':'huge','blob':'x'*65520}})[0] in (400,503)
|
||||
assert api('POST', 'config/logout', {'logout':{}})[0] == 201
|
||||
assert api('GET', 'config/nsversion')[0] == 401
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
evidence = Evidence(directory)
|
||||
server = Server(('127.0.0.1',0), Engine(evidence.emit,save=evidence.save), serve_guest_files=False)
|
||||
thread=threading.Thread(target=server.serve_forever,daemon=True);thread.start()
|
||||
payload=b'\0inert-nitro-file\xff\n'
|
||||
body=json.dumps({'systemfile':{'filename':'../../inert.bin','filelocation':'/tmp','filecontent':base64.b64encode(payload).decode()}}).encode()
|
||||
try:
|
||||
connection=http.client.HTTPConnection('127.0.0.1',server.server_port,timeout=3)
|
||||
connection.request('POST','/nitro/v1/config/systemfile',body,{'Content-Type':'application/json','X-NITRO-USER':'probe','X-NITRO-PASS':'inert'})
|
||||
response=connection.getresponse();assert response.status==201;response.read();connection.close()
|
||||
digest=hashlib.sha256(payload).hexdigest()
|
||||
assert (pathlib.Path(directory)/'artifacts'/(digest+'.bin')).read_bytes()==payload
|
||||
records=[json.loads(row) for row in (pathlib.Path(directory)/'events.jsonl').read_text().splitlines()]
|
||||
upload=next(row for row in records if row['event']=='upload')
|
||||
assert upload['filename']=='../../inert.bin' and upload['evidence']['sha256']==digest
|
||||
assert any(row['event']=='request_body' and row['evidence']['sha256']==hashlib.sha256(body).hexdigest() for row in records)
|
||||
finally:
|
||||
server.shutdown();server.server_close();thread.join(3);evidence.close()
|
||||
print('PASS: resource catalog, auth, CRUD, actions, queries, source isolation and byte-exact NITRO files')
|
||||
|
||||
|
||||
if __name__=='__main__':main()
|
||||
Reference in new issue
Block a user