From 0bd5f487aeec0ac42744b6172534379fa51a67b6 Mon Sep 17 00:00:00 2001 From: elyousfi Date: Wed, 12 Jun 2024 15:37:08 +0100 Subject: [PATCH] Adapt create assets mutations to format of yaml file. --- src/ostorlab/serve_app/oxo.py | 62 ++++----- src/ostorlab/serve_app/types.py | 37 ++---- tests/serve_app/oxo_test.py | 216 +++++++++++++------------------- 3 files changed, 131 insertions(+), 184 deletions(-) diff --git a/src/ostorlab/serve_app/oxo.py b/src/ostorlab/serve_app/oxo.py index dda6d3ffd..bd026ba46 100644 --- a/src/ostorlab/serve_app/oxo.py +++ b/src/ostorlab/serve_app/oxo.py @@ -307,37 +307,43 @@ def mutate( errors.append(error_message) continue if asset.android_store is not None: - new_asset = models.AndroidStore.create( - package_name=asset.android_store.package_name, - application_name=asset.android_store.application_name, - ) - created_assets.append(new_asset) + for asset_android_store in asset.android_store: + new_asset = models.AndroidStore.create( + package_name=asset_android_store.package_name, + application_name=asset_android_store.application_name, + ) + created_assets.append(new_asset) if asset.android_file is not None: - content = asset.android_file.file.read() - android_file_path = ( - config_manager.upload_path / f"android_{str(uuid.uuid4())}" - ) - android_file_path.write_bytes(content) - new_asset = models.AndroidFile.create( - package_name=asset.android_file.package_name, - path=str(android_file_path), - ) - created_assets.append(new_asset) + for asset_android_file in asset.android_file: + content = asset_android_file.file.read() + android_file_path = ( + config_manager.upload_path / f"android_{str(uuid.uuid4())}" + ) + android_file_path.write_bytes(content) + new_asset = models.AndroidFile.create( + package_name=asset_android_file.package_name, + path=str(android_file_path), + ) + created_assets.append(new_asset) if asset.ios_store is not None: - new_asset = models.IosStore.create( - bundle_id=asset.ios_store.bundle_id, - application_name=asset.ios_store.application_name, - ) - created_assets.append(new_asset) + for asset_ios_store in asset.ios_store: + new_asset = models.IosStore.create( + bundle_id=asset_ios_store.bundle_id, + application_name=asset_ios_store.application_name, + ) + created_assets.append(new_asset) if asset.ios_file is not None: - content = asset.ios_file.file.read() - ios_file_path = config_manager.upload_path / f"ios_{str(uuid.uuid4())}" - ios_file_path.write_bytes(content) - new_asset = models.IosFile.create( - bundle_id=asset.ios_file.bundle_id, - path=str(ios_file_path), - ) - created_assets.append(new_asset) + for asset_ios_file in asset.ios_file: + content = asset_ios_file.file.read() + ios_file_path = ( + config_manager.upload_path / f"ios_{str(uuid.uuid4())}" + ) + ios_file_path.write_bytes(content) + new_asset = models.IosFile.create( + bundle_id=asset_ios_file.bundle_id, + path=str(ios_file_path), + ) + created_assets.append(new_asset) if asset.link is not None: new_asset = models.Urls.create(links=asset.link) created_assets.append(new_asset) diff --git a/src/ostorlab/serve_app/types.py b/src/ostorlab/serve_app/types.py index 95061175f..8df9b6412 100644 --- a/src/ostorlab/serve_app/types.py +++ b/src/ostorlab/serve_app/types.py @@ -187,22 +187,7 @@ def resolve_vulnerabilities( return OxoVulnerabilitiesType(vulnerabilities=vulnerabilities) -class AssetScansMixin: - scans = graphene.List( - lambda: OxoScanType, last_only=graphene.Boolean(required=False) - ) - - def resolve_scans(self, info): - with models.Database() as session: - asset = session.query(models.Asset).get(self.id) - scan = session.query(models.Scan).get(asset.scan_id) - - return [scan] - - -class OxoAndroidStoreAssetType( - graphene_sqlalchemy.SQLAlchemyObjectType, AssetScansMixin -): +class OxoAndroidStoreAssetType(graphene_sqlalchemy.SQLAlchemyObjectType): class Meta: model = models.AndroidStore only_fields = ("id", "package_name", "application_name") @@ -221,7 +206,7 @@ class OxoAndroidStoreAssetInputType(graphene.InputObjectType): application_name = graphene.String() -class OxoIOSStoreAssetType(graphene_sqlalchemy.SQLAlchemyObjectType, AssetScansMixin): +class OxoIOSStoreAssetType(graphene_sqlalchemy.SQLAlchemyObjectType): class Meta: model = models.IosStore only_fields = ("id", "bundle_id", "application_name") @@ -240,9 +225,7 @@ class OxoIOSStoreAssetInputType(graphene.InputObjectType): application_name = graphene.String() -class OxoAndroidFileAssetType( - graphene_sqlalchemy.SQLAlchemyObjectType, AssetScansMixin -): +class OxoAndroidFileAssetType(graphene_sqlalchemy.SQLAlchemyObjectType): class Meta: model = models.AndroidFile only_fields = ("id", "package_name", "path") @@ -261,7 +244,7 @@ class OxoAndroidFileAssetInputType(graphene.InputObjectType): package_name = graphene.String() -class OxoIOSFileAssetType(graphene_sqlalchemy.SQLAlchemyObjectType, AssetScansMixin): +class OxoIOSFileAssetType(graphene_sqlalchemy.SQLAlchemyObjectType): class Meta: model = models.IosFile only_fields = ("id", "bundle_id", "path") @@ -286,7 +269,7 @@ class Meta: only_fields = ("url", "method") -class OxoUrlsAssetType(graphene_sqlalchemy.SQLAlchemyObjectType, AssetScansMixin): +class OxoUrlsAssetType(graphene_sqlalchemy.SQLAlchemyObjectType): links = graphene.List(OxoLinkAssetType, required=False) class Meta: @@ -307,7 +290,7 @@ class Meta: only_fields = ("host", "mask") -class OxoNetworkAssetType(graphene_sqlalchemy.SQLAlchemyObjectType, AssetScansMixin): +class OxoNetworkAssetType(graphene_sqlalchemy.SQLAlchemyObjectType): networks = graphene.List(OxoIPRangeAssetType, required=False) class Meta: @@ -735,10 +718,10 @@ class OxoLinkInputType(graphene.InputObjectType): class OxoAssetInputType(graphene.InputObjectType): - android_file = OxoAndroidFileAssetInputType() - ios_file = OxoIOSFileAssetInputType() - android_store = OxoAndroidStoreAssetInputType() - ios_store = OxoIOSStoreAssetInputType() + android_file = graphene.List(OxoAndroidFileAssetInputType) + ios_file = graphene.List(OxoIOSFileAssetInputType) + android_store = graphene.List(OxoAndroidStoreAssetInputType) + ios_store = graphene.List(OxoIOSStoreAssetInputType) link = graphene.List(OxoLinkInputType) ip = graphene.List(OxoIPRangeInputType) diff --git a/tests/serve_app/oxo_test.py b/tests/serve_app/oxo_test.py index 048f69801..8a7450fa2 100644 --- a/tests/serve_app/oxo_test.py +++ b/tests/serve_app/oxo_test.py @@ -957,9 +957,9 @@ def testCreateAsset_androidStore_createsNewAsset( createAssets(assets: $assets) { assets { ... on OxoAndroidStoreAssetType { - id - packageName - applicationName + id + applicationName + packageName } } } @@ -973,10 +973,16 @@ def testCreateAsset_androidStore_createsNewAsset( "variables": { "assets": [ { - "androidStore": { - "applicationName": "fake_app", - "packageName": "a.b.c", - } + "androidStore": [ + { + "applicationName": "fake_app1", + "packageName": "a.b.c", + }, + { + "applicationName": "fake_app2", + "packageName": "d.e.f", + }, + ] } ] }, @@ -984,15 +990,23 @@ def testCreateAsset_androidStore_createsNewAsset( ) assert resp.status_code == 200, resp.get_json() - asset_data = resp.get_json()["data"]["createAssets"]["assets"][0] - assert asset_data["id"] is not None - assert asset_data["packageName"] == "a.b.c" - assert asset_data["applicationName"] == "fake_app" + asset1 = resp.get_json()["data"]["createAssets"]["assets"][0] + asset2 = resp.get_json()["data"]["createAssets"]["assets"][1] + assert asset1["id"] is not None + assert asset1["packageName"] == "a.b.c" + assert asset1["applicationName"] == "fake_app1" + assert asset2["id"] is not None + assert asset2["packageName"] == "d.e.f" + assert asset2["applicationName"] == "fake_app2" with models.Database() as session: - assert session.query(models.AndroidStore).count() == 1 + assert session.query(models.AndroidStore).count() == 2 assert session.query(models.AndroidStore).all()[0].package_name == "a.b.c" assert ( - session.query(models.AndroidStore).all()[0].application_name == "fake_app" + session.query(models.AndroidStore).all()[0].application_name == "fake_app1" + ) + assert session.query(models.AndroidStore).all()[1].package_name == "d.e.f" + assert ( + session.query(models.AndroidStore).all()[1].application_name == "fake_app2" ) @@ -1009,10 +1023,6 @@ def testCreateAsset_iOSStore_createsNewAsset( id bundleId applicationName - scans { - id - title - } } } } @@ -1026,10 +1036,16 @@ def testCreateAsset_iOSStore_createsNewAsset( "variables": { "assets": [ { - "iosStore": { - "applicationName": "fake_app", - "bundleId": "a.b.c", - } + "iosStore": [ + { + "applicationName": "fake_app1", + "bundleId": "a.b.c", + }, + { + "applicationName": "fake_app2", + "bundleId": "d.e.f", + }, + ] } ] }, @@ -1037,14 +1053,20 @@ def testCreateAsset_iOSStore_createsNewAsset( ) assert resp.status_code == 200, resp.get_json() - asset_data = resp.get_json()["data"]["createAssets"]["assets"][0] - assert asset_data["id"] is not None - assert asset_data["bundleId"] == "a.b.c" - assert asset_data["applicationName"] == "fake_app" + asset1 = resp.get_json()["data"]["createAssets"]["assets"][0] + asset2 = resp.get_json()["data"]["createAssets"]["assets"][1] + assert asset1["id"] is not None + assert asset1["bundleId"] == "a.b.c" + assert asset1["applicationName"] == "fake_app1" + assert asset2["id"] is not None + assert asset2["bundleId"] == "d.e.f" + assert asset2["applicationName"] == "fake_app2" with models.Database() as session: - assert session.query(models.IosStore).count() == 1 + assert session.query(models.IosStore).count() == 2 assert session.query(models.IosStore).all()[0].bundle_id == "a.b.c" - assert session.query(models.IosStore).all()[0].application_name == "fake_app" + assert session.query(models.IosStore).all()[0].application_name == "fake_app1" + assert session.query(models.IosStore).all()[1].bundle_id == "d.e.f" + assert session.query(models.IosStore).all()[1].application_name == "fake_app2" def testCreateAsset_url_createsNewAsset( @@ -1184,17 +1206,19 @@ def testCreateAsset_androidFile_createsNewAsset( "variables": { "assets": [ { - "androidFile": { - "file": None, - "packageName": "a.b.c", - } + "androidFile": [ + { + "file": None, + "packageName": "a.b.c", + } + ] } ] }, } ), "0": apk_path.open("rb"), - "map": json.dumps({"0": ["variables.assets.0.androidFile.file"]}), + "map": json.dumps({"0": ["variables.assets.0.androidFile.0.file"]}), } resp = authenticated_flask_client.post( @@ -1251,17 +1275,19 @@ def testCreateAsset_iOSFile_createsNewAsset( "variables": { "assets": [ { - "iosFile": { - "file": None, - "bundleId": "a.b.c", - } + "iosFile": [ + { + "file": None, + "bundleId": "a.b.c", + } + ] } ] }, } ), "0": apk_path.open("rb"), - "map": json.dumps({"0": ["variables.assets.0.iosFile.file"]}), + "map": json.dumps({"0": ["variables.assets.0.iosFile.0.file"]}), } resp = authenticated_flask_client.post( @@ -1322,16 +1348,20 @@ def testCreateAsset_whenMultipleAssets_shouldCreateAll( "variables": { "assets": [ { - "androidStore": { - "applicationName": "fake_app", - "packageName": "a.b.c", - } + "androidStore": [ + { + "applicationName": "fake_app", + "packageName": "a.b.c", + } + ] }, { - "iosStore": { - "applicationName": "fake_app", - "bundleId": "a.b.c", - } + "iosStore": [ + { + "applicationName": "fake_app", + "bundleId": "a.b.c", + } + ] }, ] }, @@ -1388,14 +1418,18 @@ def testCreateAsset_whenMultipleTargetsForSameAsset_shouldReturnError( "variables": { "assets": [ { - "androidStore": { - "applicationName": "fake_app", - "packageName": "a.b.c", - }, - "iosStore": { - "applicationName": "fake_app", - "bundleId": "a.b.c", - }, + "androidStore": [ + { + "applicationName": "fake_app", + "packageName": "a.b.c", + } + ], + "iosStore": [ + { + "applicationName": "fake_app", + "bundleId": "a.b.c", + } + ], } ] }, @@ -1496,58 +1530,6 @@ def testQueryScan_whenAsset_shouldReturnScanAndAssetInformation( assert scan_data["assets"][0]["applicationName"] == asset.application_name -def testQueryAsset_whenHasScan_shouldReturnScanInformationFromAssetObject( - authenticated_flask_client: testing.FlaskClient, - mocker: plugin.MockerFixture, - db_engine_path: str, -) -> None: - """Ensure we can query the specific scan information from its asset.""" - mocker.patch.object(models, "ENGINE_URL", db_engine_path) - with models.Database() as session: - scan = models.Scan( - title="iOS Scan", - progress=models.ScanProgress.NOT_STARTED, - ) - session.add(scan) - session.commit() - asset = models.AndroidStore( - package_name="a.b.c", application_name="fake_app", scan_id=scan.id - ) - session.add(asset) - session.commit() - - query = """ - query Scans($scanIds: [Int!]) { - scans(scanIds: $scanIds) { - scans { - id - assets { - ... on OxoAndroidStoreAssetType { - id - packageName - applicationName - scans { - id - title - } - } - } - } - } - } - """ - - response = authenticated_flask_client.post( - "/graphql", - json={"query": query, "variables": {"scanIds": [scan.id]}}, - ) - - assert response.status_code == 200, response.get_json() - asset_data = response.get_json()["data"]["scans"]["scans"][0]["assets"][0] - assert asset_data["scans"][0]["id"] == str(scan.id) - assert asset_data["scans"][0]["title"] == "iOS Scan" - - def testQueryAssets_whenScanHasMultipleAssets_shouldReturnAllAssets( authenticated_flask_client: testing.FlaskClient, multiple_assets_scan: models.Scan ) -> None: @@ -1908,10 +1890,6 @@ def testRunScanMutation_whenNetworkAsset_shouldRunScan( host mask } - scans { - id - title - } } } } @@ -1993,10 +1971,6 @@ def testRunScanMutation_whenUrl_shouldRunScan( url method } - scans { - id - title - } } } } @@ -2076,10 +2050,6 @@ def testRunScanMutation_whenAndroidFile_shouldRunScan( id path packageName - scans { - id - title - } } } } @@ -2153,10 +2123,6 @@ def testRunScanMutation_whenIosFile_shouldRunScan( id path bundleId - scans { - id - title - } } } } @@ -2230,10 +2196,6 @@ def testRunScanMutation_whenAndroidStore_shouldRunScan( id packageName applicationName - scans { - id - title - } } } } @@ -2307,10 +2269,6 @@ def testRunScanMutation_whenIosStore_shouldRunScan( id bundleId applicationName - scans { - id - title - } } } }