def test_defaults(self): tm = TM("my test tm", description="aa", isOrdered=True) internet = Boundary("Internet") cloud = Boundary("Cloud") user = Actor("User", inBoundary=internet) server = Server("Server") db = Datastore("DB", inBoundary=cloud) db.type = DatastoreType.SQL func = Datastore("Lambda function", inBoundary=cloud) request = Dataflow(user, server, "request") response = Dataflow(server, user, "response", isResponse=True) user_query = Dataflow(user, db, "user query") server_query = Dataflow(server, db, "server query") func_query = Dataflow(func, db, "func query") default_target = ["Actor", "Boundary", "Dataflow", "Datastore", "Server"] testCases = [ {"target": server, "condition": "target.oneOf(Server, Datastore)"}, {"target": server, "condition": "not target.oneOf(Actor, Dataflow)"}, {"target": request, "condition": "target.crosses(Boundary)"}, {"target": user_query, "condition": "target.crosses(Boundary)"}, {"target": server_query, "condition": "target.crosses(Boundary)"}, {"target": func_query, "condition": "not target.crosses(Boundary)"}, {"target": func_query, "condition": "not target.enters(Boundary)"}, {"target": func_query, "condition": "not target.exits(Boundary)"}, {"target": request, "condition": "not target.enters(Boundary)"}, {"target": request, "condition": "target.exits(Boundary)"}, {"target": response, "condition": "target.enters(Boundary)"}, {"target": response, "condition": "not target.exits(Boundary)"}, {"target": user, "condition": "target.inside(Boundary)"}, {"target": func, "condition": "not any(target.inputs)"}, { "target": server, "condition": "any(f.sink.oneOf(Datastore) and f.sink.type == DatastoreType.SQL " "for f in target.outputs)", }, ] self.assertTrue(tm.check()) for case in testCases: t = Threat(SID="", target=default_target, condition=case["condition"]) self.assertTrue( t.apply(case["target"]), "Failed to match {} against {}".format( case["target"], case["condition"], ), )
def test_responses(self): tm = TM("my test tm", description="aa", isOrdered=True) user = Actor("User") web = Server("Web Server") db = Datastore("SQL Database") http_req = Dataflow(user, web, "http req") insert = Dataflow(web, db, "insert data") query = Dataflow(web, db, "query") query_resp = Dataflow(db, web, "query results", responseTo=query) http_resp = Dataflow(web, user, "http resp") http_resp.responseTo = http_req self.assertTrue(tm.check()) self.assertEqual(http_req.response, http_resp) self.assertIs(http_resp.isResponse, True) self.assertIs(query_resp.isResponse, True) self.assertEqual(query_resp.responseTo, query) self.assertEqual(query.response, query_resp) self.assertIsNone(insert.response) self.assertIs(insert.isResponse, False)
def makeDataflow(dataflow_json, componentObj, boundaryObj): name = dataflow_json.pop('name') if dataflow_json['source'] is not '' and dataflow_json['sink'] is not '': src = componentObj[dataflow_json.pop('source')] sink = componentObj[dataflow_json.pop('sink')] else: return None dataflow = Dataflow(src, sink, name) for k,v in dataflow_json.items(): if k == 'Options': for option, v_ in v.items(): replaceAttribute(dataflow, option, v_) else: if k == 'inBoundary': continue elif k == 'inBoundary' and v is not '': dataflow.inBoundary = boundaryObj[v] continue replaceAttribute(dataflow, k, v) return dataflow
user = Actor("App-y-Tenant") app = Server("Mobile App") buy_api = Server("Buy<br/>API-y") buy_api.inBoundary = Web rent_api = Server("Rent<br/>API-y") rent_api.inBoundary = Web alert_api = Server("Alert<br/>API-y") alert_api.inBoundary = Web cloud = Server("Phone Provider Cloud") cloud.inBoundary = external_web alert_api_to_cloud = Dataflow(alert_api, cloud, "push") cloud_to_app = Dataflow(cloud, app, " ") db_b = Datastore("Oracle Table B") db_b.inBoundary = Web buy_api_to_db = Dataflow(buy_api, db_b, " ") db_r = Datastore("Oracle Table R") db_r.inBoundary = Web rent_api_to_db = Dataflow(rent_api, db_r, " ") db_t = Datastore("Oracle Table Tenants") db_t.inBoundary = Web rent_api_to_db_t = Dataflow(rent_api, db_t, " ") buy_api_to_db_t = Dataflow(buy_api, db_t, " ") alert_api_to_db_t = Dataflow(alert_api, db_t, " ")
def test_defaults(self): tm = TM("TM") user = Actor("User", data="HTTP", authenticatesDestination=True) server = Server("Server", port=443, protocol="HTTPS", isEncrypted=True, data="JSON") db = Datastore( "PostgreSQL", isSQL=True, port=5432, protocol="PostgreSQL", isEncrypted=False, data="SQL resp", ) worker = Process("Task queue worker") req_get = Dataflow(user, server, "HTTP GET") server_query = Dataflow(server, db, "Query", data="SQL") result = Dataflow(db, server, "Results", isResponse=True) resp_get = Dataflow(server, user, "HTTP Response", isResponse=True) req_post = Dataflow(user, server, "HTTP POST", data="JSON") resp_post = Dataflow(server, user, "HTTP Response", isResponse=True) worker_query = Dataflow(worker, db, "Query", data="SQL") Dataflow(db, worker, "Results", isResponse=True) cookie = Data("Auth Cookie", carriedBy=[req_get, req_post]) self.assertTrue(tm.check()) self.assertEqual(req_get.srcPort, -1) self.assertEqual(req_get.dstPort, server.port) self.assertEqual(req_get.isEncrypted, server.isEncrypted) self.assertEqual(req_get.authenticatesDestination, user.authenticatesDestination) self.assertEqual(req_get.protocol, server.protocol) self.assertTrue(user.data.issubset(req_get.data)) self.assertEqual(server_query.srcPort, -1) self.assertEqual(server_query.dstPort, db.port) self.assertEqual(server_query.isEncrypted, db.isEncrypted) self.assertEqual(server_query.authenticatesDestination, server.authenticatesDestination) self.assertEqual(server_query.protocol, db.protocol) self.assertTrue(server.data.issubset(server_query.data)) self.assertEqual(result.srcPort, db.port) self.assertEqual(result.dstPort, -1) self.assertEqual(result.isEncrypted, db.isEncrypted) self.assertEqual(result.authenticatesDestination, False) self.assertEqual(result.protocol, db.protocol) self.assertTrue(db.data.issubset(result.data)) self.assertEqual(resp_get.srcPort, server.port) self.assertEqual(resp_get.dstPort, -1) self.assertEqual(resp_get.isEncrypted, server.isEncrypted) self.assertEqual(resp_get.authenticatesDestination, False) self.assertEqual(resp_get.protocol, server.protocol) self.assertTrue(server.data.issubset(resp_get.data)) self.assertEqual(req_post.srcPort, -1) self.assertEqual(req_post.dstPort, server.port) self.assertEqual(req_post.isEncrypted, server.isEncrypted) self.assertEqual(req_post.authenticatesDestination, user.authenticatesDestination) self.assertEqual(req_post.protocol, server.protocol) self.assertTrue(user.data.issubset(req_post.data)) self.assertEqual(resp_post.srcPort, server.port) self.assertEqual(resp_post.dstPort, -1) self.assertEqual(resp_post.isEncrypted, server.isEncrypted) self.assertEqual(resp_post.authenticatesDestination, False) self.assertEqual(resp_post.protocol, server.protocol) self.assertTrue(server.data.issubset(resp_post.data)) self.assertListEqual(server.inputs, [req_get, req_post]) self.assertListEqual(server.outputs, [server_query]) self.assertListEqual(worker.inputs, []) self.assertListEqual(worker.outputs, [worker_query]) self.assertListEqual(cookie.carriedBy, [req_get, req_post]) self.assertSetEqual(set(cookie.processedBy), set([user, server])) self.assertIn(cookie, req_get.data) self.assertSetEqual(set([d.name for d in req_post.data]), set([cookie.name, "HTTP", "JSON"]))
web.sanitizesInput = False web.encodesOutput = True web.authorizesSource = False db = Datastore("SQL Database") db.OS = "CentOS" db.isHardened = False db.inBoundary = server_db db.isSQL = True db.inScope = True my_lambda = Lambda("AWS Lambda") my_lambda.hasAccessControl = True my_lambda.inBoundary = vpc user_to_web = Dataflow(user, web, "User enters comments (*)") user_to_web.protocol = "HTTP" user_to_web.dstPort = 80 user_to_web.data = 'Comments in HTML or Markdown' user_to_web.note = "This is a simple web app\nthat stores and retrieves user comments." web_to_db = Dataflow(web, db, "Insert query with comments") web_to_db.protocol = "MySQL" web_to_db.dstPort = 3306 web_to_db.data = 'MySQL insert statement, all literals' web_to_db.note = "Web server inserts user comments\ninto it's SQL query and stores them in the DB." db_to_web = Dataflow(db, web, "Retrieve comments") db_to_web.protocol = "MySQL" db_to_web.dstPort = 80 db_to_web.data = 'Web server retrieves comments from DB'
def test_defaults(self): tm = TM("TM") user = Actor("User", data="HTTP") server = Server("Server", port=443, protocol="HTTPS", isEncrypted=True, data="JSON") db = Datastore( "PostgreSQL", isSQL=True, port=5432, protocol="PostgreSQL", isEncrypted=False, data="SQL resp", ) req_get = Dataflow(user, server, "HTTP GET") query = Dataflow(server, db, "Query", data="SQL") result = Dataflow(db, server, "Results", isResponse=True) resp_get = Dataflow(server, user, "HTTP Response", isResponse=True) req_post = Dataflow(user, server, "HTTP POST", data="JSON") resp_post = Dataflow(server, user, "HTTP Response", isResponse=True) tm.check() self.assertEqual(req_get.srcPort, -1) self.assertEqual(req_get.dstPort, server.port) self.assertEqual(req_get.isEncrypted, server.isEncrypted) self.assertEqual(req_get.protocol, server.protocol) self.assertEqual(req_get.data, user.data) self.assertEqual(query.srcPort, -1) self.assertEqual(query.dstPort, db.port) self.assertEqual(query.isEncrypted, db.isEncrypted) self.assertEqual(query.protocol, db.protocol) self.assertNotEqual(query.data, server.data) self.assertEqual(result.srcPort, db.port) self.assertEqual(result.dstPort, -1) self.assertEqual(result.isEncrypted, db.isEncrypted) self.assertEqual(result.protocol, db.protocol) self.assertEqual(result.data, db.data) self.assertEqual(resp_get.srcPort, server.port) self.assertEqual(resp_get.dstPort, -1) self.assertEqual(resp_get.isEncrypted, server.isEncrypted) self.assertEqual(resp_get.protocol, server.protocol) self.assertEqual(resp_get.data, server.data) self.assertEqual(req_post.srcPort, -1) self.assertEqual(req_post.dstPort, server.port) self.assertEqual(req_post.isEncrypted, server.isEncrypted) self.assertEqual(req_post.protocol, server.protocol) self.assertNotEqual(req_post.data, user.data) self.assertEqual(resp_post.srcPort, server.port) self.assertEqual(resp_post.dstPort, -1) self.assertEqual(resp_post.isEncrypted, server.isEncrypted) self.assertEqual(resp_post.protocol, server.protocol) self.assertEqual(resp_post.data, server.data)
service.inBoundary = hosted_services aws_services = [s3_bucket] for service in aws_services: service.inBoundary = aws mongo_services = [identity_db, token_blacklist_db, resources_db] for service in mongo_services: service.inBoundary = mongo_atlas sendgrid.inBoundary = external_services #TODO # aws.inBoundary = external_services # mongo_atlas.inBoundary = external_services # Dataflows user_browser = Dataflow(web_user, browser, "User Accesing Webapp") browser_user = Dataflow(browser, web_user, "Responses to user") browser_user.responseTo = user_browser browser_webserver = Dataflow(browser, react_webapp, "User Accesing Webapp") webserver_browser = Dataflow(react_webapp, browser, "Responses to user") webserver_browser.responseTo = browser_webserver for entity in [browser, mobile_client, direct_api]: to_webserver = Dataflow(entity, nginx_backend_server, "{} acessing api".format(entity.name)) from_webserver = Dataflow(nginx_backend_server, entity, "Responses to {}".format(entity.name)) from_webserver.responseTo = to_webserver # web_user
def test_defaults(self): internet = Boundary("Internet") cloud = Boundary("Cloud") user = Actor("User", inBoundary=internet) server = Server("Server") db = Datastore("DB", inBoundary=cloud) func = Datastore("Lambda function", inBoundary=cloud) request = Dataflow(user, server, "request") response = Dataflow(server, user, "response") user_query = Dataflow(user, db, "user query") server_query = Dataflow(server, db, "server query") func_query = Dataflow(func, db, "func query") default = { "SID": "", "description": "", "condition": "", "target": ["Actor", "Boundary", "Dataflow", "Datastore", "Server"], "details": "", "severity": "", "mitigations": "", "example": "", "references": "", } testCases = [ { "target": server, "condition": "target.oneOf(Server, Datastore)" }, { "target": server, "condition": "not target.oneOf(Actor, Dataflow)" }, { "target": request, "condition": "target.crosses(Boundary)" }, { "target": user_query, "condition": "target.crosses(Boundary)" }, { "target": server_query, "condition": "target.crosses(Boundary)" }, { "target": func_query, "condition": "not target.crosses(Boundary)" }, { "target": func_query, "condition": "not target.enters(Boundary)" }, { "target": func_query, "condition": "not target.exits(Boundary)" }, { "target": request, "condition": "not target.enters(Boundary)" }, { "target": request, "condition": "target.exits(Boundary)" }, { "target": response, "condition": "target.enters(Boundary)" }, { "target": response, "condition": "not target.exits(Boundary)" }, { "target": user, "condition": "target.inside(Boundary)" }, ] for case in testCases: t = Threat({**default, **{"condition": case["condition"]}}) self.assertTrue( t.apply(case["target"]), "Failed to match {} against {}".format(case["target"], case["condition"]), )
kubeproxy.inBoundary = worker pods.inBoundary = contain scheduler.inBoundary = mcomps controllers.inBoundary = mcomps pods.inBoundary = contain iptables.inBoundary = worker miu.inBoundary = apisrv ia.inBoundary = contain ea.inBoundary = inet admin.inBoundary = apisrv dev.inBoundary = inet eu.inBoundary = inet # Dataflows apiserver2etcd = Dataflow(apiserver, etcd, "All kube-apiserver data") apiserver2etcd.isEncrypted = True apiserver2etcd.protocol = "HTTPS" apiserver2kubelet = Dataflow(apiserver, kubelet, "kubelet Health, Status, &c.") apiserver2kubelet.isEncrypted = False apiserver2kubelet.protocol = "HTTP" apiserver2kubeproxy = Dataflow(apiserver, kubeproxy, "kube-proxy Health, Status, &c.") apiserver2kubeproxy.isEncrypted = False apiserver2kubeproxy.protocol = "HTTP" apiserver2scheduler = Dataflow(apiserver, scheduler, "kube-scheduler Health, Status, &c.")
allAuth.inBoundary = internet phoneCloud = Server("Phone<br/>Provider<br/>Cloud") firensurfCloud = Server("Fire n' Surf .gov") dbB = Datastore("Oracle Table B") dbB.inBoundary = internet dbR = Datastore("Oracle Table R") dbR.inBoundary = internet dbT = Datastore("Oracle Table T") dbT.inBoundary = internet user_to_app = Dataflow(user, app, "use") app_to_buyapi = Dataflow(app, buyApi, "HTTPS<br/>JSON") app_to_phonecloud = Dataflow(app, phoneCloud, " ") app_to_rentapi = Dataflow(app, rentApi, "HTTPS<br/>JSON") app_to_authapi = Dataflow(app, authApi, "HTTPS<br/>JSON") app_to_dbt = Dataflow(authApi, dbT, "Token-y") allauth_to_dbt = Dataflow(allAuth, dbT, " ") buyapi_to_dbt = Dataflow(buyApi, dbT, " ") buyapi_to_market = Dataflow(buyApi, market, " ") rentapi_to_dbr = Dataflow(rentApi, dbR, " ") rentapi_to_dbb = Dataflow(buyApi, dbB, " ") rentapi_to_market = Dataflow(rentApi, market, " ") alert_to_phonecloud = Dataflow(alertApi, phoneCloud, "push") alert_to_firensurf = Dataflow(alertApi, firensurfCloud, "Kafka<br/>HTTPS") firensurf_to_alert = Dataflow(firensurfCloud, alertApi, "push") buyapi_to_phonecloud = Dataflow(buyApi, phoneCloud, " ")
user = Actor("User") user.inBoundary = User_Web web = Server("Web Server") web.OS = "CloudOS" web.isHardened = True db = Datastore("SQL Database (*)") db.OS = "CentOS" db.isHardened = False db.inBoundary = Web_DB db.isSql = True db.inScope = False user_to_web = Dataflow(user, web, "User enters comments (*)") user_to_web.protocol = "HTTP" user_to_web.dstPort = 80 user_to_web.data = 'Comments in HTML or Markdown' user_to_web.order = 1 user_to_web.note = "This is a note\nmulti-line" web_to_user = Dataflow(web, user, "Comments saved (*)") web_to_user.protocol = "HTTP" web_to_user.data = 'Ack of saving or error message, in JSON' web_to_user.order = 2 web_to_db = Dataflow(web, db, "Insert query with comments") web_to_db.protocol = "MySQL" web_to_db.dstPort = 3306 web_to_db.data = 'MySQL insert statement, all literals'
db_search = Datastore("Restaurants") db_search.inBoundary = DB db_search.inScope = True db_search.authenticatesSource = True db_rating = Datastore("Ratings") db_rating.inBoundary = DB db_rating.inScope = False db_reservations = Datastore("Reservations") db_reservations.inBoundary = DB db_reservations.inScope = False # Define flows search_user_to_api = Dataflow(user, api_search, "User enters search") search_user_to_api.isEncrypted = True #JIRA TEST-0001 search_user_to_api.order =1 search_api_to_db = Dataflow(api_search, db_search, "Search critirea") search_api_to_db.order = 2 search_db_to_api = Dataflow(db_search, api_search, "Results") search_db_to_api.order = 3 search_api_to_user = Dataflow(api_search, user, "Results") search_api_to_user.order = 4 reservation_user_to_api = Dataflow(user, api_reservation, "User enters reservation") reservation_user_to_api.isEncrypted = True reservation_user_to_api.order =5 reservation_api_to_db = Dataflow(api_reservation, db_reservations, "Check availability") reservation_api_to_db.order = 6 reservation_db_to_api = Dataflow(db_reservations, api_reservation, "Returns availability")
web.OS = "CloudOS" web.isHardened = True db = Datastore("SQL Database (*)") db.OS = "CentOS" db.isHardened = False db.inBoundary = Web_DB db.isSql = True db.inScope = False web. my_lambda = Lambda("cleanDBevery6hours") my_lambda.hasAccessControl = True my_lambda.inBoundary = Web_DB my_lambda_to_db = Dataflow(my_lambda, db, "(λ)Periodically cleans DB") my_lambda_to_db.protocol = "SQL" my_lambda_to_db.dstPort = 3306 user_to_web = Dataflow(user, web, "User enters comments (*)") user_to_web.protocol = "HTTP" user_to_web.dstPort = 80 user_to_web.data = 'Comments in HTML or Markdown' user_to_web.order = 1 web_to_user = Dataflow(web, user, "Comments saved (*)") web_to_user.protocol = "HTTP" web_to_user.data = 'Ack of saving or error message, in JSON' web_to_user.order = 2 web_to_db = Dataflow(web, db, "Insert query with comments")
def test_defaults(self): internet = Boundary("Internet") cloud = Boundary("Cloud") user = Actor("User", inBoundary=internet) server = Server("Server") db = Datastore("DB", inBoundary=cloud) func = Datastore("Lambda function", inBoundary=cloud) request = Dataflow(user, server, "request") response = Dataflow(server, user, "response") user_query = Dataflow(user, db, "user query") server_query = Dataflow(server, db, "server query") func_query = Dataflow(func, db, "func query") default_target = [ "Actor", "Boundary", "Dataflow", "Datastore", "Server" ] testCases = [ { "target": server, "condition": "target.oneOf(Server, Datastore)" }, { "target": server, "condition": "not target.oneOf(Actor, Dataflow)" }, { "target": request, "condition": "target.crosses(Boundary)" }, { "target": user_query, "condition": "target.crosses(Boundary)" }, { "target": server_query, "condition": "target.crosses(Boundary)" }, { "target": func_query, "condition": "not target.crosses(Boundary)" }, { "target": func_query, "condition": "not target.enters(Boundary)" }, { "target": func_query, "condition": "not target.exits(Boundary)" }, { "target": request, "condition": "not target.enters(Boundary)" }, { "target": request, "condition": "target.exits(Boundary)" }, { "target": response, "condition": "target.enters(Boundary)" }, { "target": response, "condition": "not target.exits(Boundary)" }, { "target": user, "condition": "target.inside(Boundary)" }, ] for case in testCases: t = Threat(SID="", target=default_target, condition=case["condition"]) self.assertTrue( t.apply(case["target"]), "Failed to match {} against {}".format(case["target"], case["condition"]), )
from pytm.pytm import TM, Boundary, Server, Actor, Datastore, Dataflow, SetOfProcesses tm = TM("Generic CMS example") tm.description = "This is a sample threat model for the Threat Model Cookbook." internet = Boundary("Internet") user = Actor("Generic/Privilege User") webserver = Server("Web Server") webserver.inBoundary = internet user_to_webserver = Dataflow(user, webserver, "HTTPS") db = Datastore("db") db.inBoundary = internet db_to_webserver = Dataflow(webserver, db, " ") adminuser = Actor(" admin ") admin_to_webserver = Dataflow(adminuser, db, "unsecure<br/>mysql<br/>connection") cdn = SetOfProcesses("CDN network") user_to_cdn = Dataflow(user, cdn, "HTTP") webserver_to_cdn = Dataflow(webserver, cdn, "Push to Bucket") tm.process()