From 4c8abfb78d972d34ccc9be087e6b003fb107ff4d Mon Sep 17 00:00:00 2001 From: aecsocket <43144841+aecsocket@users.noreply.github.com> Date: Sun, 2 Aug 2026 17:05:41 +0100 Subject: [PATCH] adjust schema --- Cargo.lock | 9 - Cargo.toml | 1 - .../moderation/technical-review/rules.vue | 28 ++- ...36eb1dd626e77331d8278c406df952691be4c.json | 22 ++ ...67a2296efc16763cdbd5d8c295a1a7ca08021.json | 12 ++ ...7a887cebbaeab0a8c1da8b008e0c5e94fbc4e.json | 32 +++ ...f4eeff66ab4165a9f4980032e114db4dc1286.json | 26 +++ ...63ce02ca16781e32232ae0fa7a0af1973d3a4.json | 20 ++ ...b8c14fe329deac983bc2f43ba74e066f9cf8d.json | 14 ++ ...8518f1272690ce7766dbdc41ec1e98f7f1fec.json | 12 ++ ...3765fb64bd9499a796470d544466886e1a808.json | 26 --- ...78df08d25844f1019d539712b0cb686e4ae81.json | 16 ++ ...66908ea41a06ba858dc5b523aef6aae27b850.json | 15 ++ ...19dc0cdef502935dd5939a2f3ab89b0766ad2.json | 12 ++ ...58541fc78e910111c09ba3d50a12d9ca4a9f8.json | 16 ++ ...5fd5165d02ebcc0f8454554cbd870e4ddb42.json} | 16 +- ...4666378d1d24981ab8113badded16fb63413.json} | 16 +- ...1a31da1885cc22bb61be6853950ba52be9aa7.json | 15 ++ ...a93c7016b2a6f18aa4f22ea57f604d82c1da.json} | 16 +- ...d2402f52fea71e27b08e7926fcc2a9e62c0f3.json | 20 ++ ...2c36b9db3770508be4acbb6b441ed8006d9c7.json | 12 ++ ...bd875675779a173fb2cd41ba365c1acca96f9.json | 14 ++ ...afedb074492b4ec7f2457c14113f5fd13aa02.json | 18 ++ ...e5c93783c7641b019fdb698a1ec0be1393606.json | 17 ++ ...0d934b9afc6dccc5fc77454fbe45365bdf429.json | 20 ++ ...86e8917cfab9679d5064074681663ba467e41.json | 22 ++ .../internal/moderation/tech_review/rules.rs | 14 +- .../moderation/tech_review/rules_scan.rs | 193 +++++++++++++++--- .../api-client/src/modules/labrinth/types.ts | 14 +- packages/delphi-cel-experiment/Cargo.toml | 10 - packages/delphi-cel-experiment/src/main.rs | 174 ---------------- 31 files changed, 577 insertions(+), 275 deletions(-) create mode 100644 apps/labrinth/.sqlx/query-0f3d943e4fc48a94363b77c8a7d36eb1dd626e77331d8278c406df952691be4c.json create mode 100644 apps/labrinth/.sqlx/query-0ffebf149d50149d0c128478e6267a2296efc16763cdbd5d8c295a1a7ca08021.json create mode 100644 apps/labrinth/.sqlx/query-182be4110f45b6be6a9174200a07a887cebbaeab0a8c1da8b008e0c5e94fbc4e.json create mode 100644 apps/labrinth/.sqlx/query-1adbd24d815107e13bc1440c7a8f4eeff66ab4165a9f4980032e114db4dc1286.json create mode 100644 apps/labrinth/.sqlx/query-20cff8fdf7971e91c9d473b9a4663ce02ca16781e32232ae0fa7a0af1973d3a4.json create mode 100644 apps/labrinth/.sqlx/query-329c009635ad2a4c214da2f4651b8c14fe329deac983bc2f43ba74e066f9cf8d.json create mode 100644 apps/labrinth/.sqlx/query-38ace0abad724d1111b79a72c7c8518f1272690ce7766dbdc41ec1e98f7f1fec.json delete mode 100644 apps/labrinth/.sqlx/query-3f1aaeed6e394294fa32da7e7f83765fb64bd9499a796470d544466886e1a808.json create mode 100644 apps/labrinth/.sqlx/query-5cb65218a2a4a7343130d838f8a78df08d25844f1019d539712b0cb686e4ae81.json create mode 100644 apps/labrinth/.sqlx/query-6678cd4c51771cfaae2be8021ba66908ea41a06ba858dc5b523aef6aae27b850.json create mode 100644 apps/labrinth/.sqlx/query-66868d0b35229abffa3f668eaef19dc0cdef502935dd5939a2f3ab89b0766ad2.json create mode 100644 apps/labrinth/.sqlx/query-69a1cb4b7f1115a990d1fc4805d58541fc78e910111c09ba3d50a12d9ca4a9f8.json rename apps/labrinth/.sqlx/{query-868d19328921b9a3bd56fa9354e5e75d09c76a3eb3ed9c89b05b9aab13505bf9.json => query-7e7e2e9f67158b94eb62834cbfd65fd5165d02ebcc0f8454554cbd870e4ddb42.json} (51%) rename apps/labrinth/.sqlx/{query-2fb03e1de120cf921146b6dbd35691117412407c4225f966c013fdfef21a82fb.json => query-8537362d9c11e437ada9ce97bb604666378d1d24981ab8113badded16fb63413.json} (51%) create mode 100644 apps/labrinth/.sqlx/query-8ca7f46618bd72cfd5fe1f246d41a31da1885cc22bb61be6853950ba52be9aa7.json rename apps/labrinth/.sqlx/{query-1f318b0dda5aaf58b9e403f87fcacfcd223bd67f6d5ba95868ace4373a98c67d.json => query-b536d7870ba77ace55e309938c3ba93c7016b2a6f18aa4f22ea57f604d82c1da.json} (50%) create mode 100644 apps/labrinth/.sqlx/query-b92b5bb7d179c4fcdbc45600ccfd2402f52fea71e27b08e7926fcc2a9e62c0f3.json create mode 100644 apps/labrinth/.sqlx/query-c65295f61b18f7be4fdf30b3e2c2c36b9db3770508be4acbb6b441ed8006d9c7.json create mode 100644 apps/labrinth/.sqlx/query-cb626a36deffd73e67de2dc4789bd875675779a173fb2cd41ba365c1acca96f9.json create mode 100644 apps/labrinth/.sqlx/query-cd5ccd618fb3cc41646a6de86f9afedb074492b4ec7f2457c14113f5fd13aa02.json create mode 100644 apps/labrinth/.sqlx/query-cec4240c7c848988b3dfd13e3f8e5c93783c7641b019fdb698a1ec0be1393606.json create mode 100644 apps/labrinth/.sqlx/query-fabba52b34e14196b6305671c060d934b9afc6dccc5fc77454fbe45365bdf429.json create mode 100644 apps/labrinth/.sqlx/query-fd5c773a61d35bcd71503ec4d5f86e8917cfab9679d5064074681663ba467e41.json delete mode 100644 packages/delphi-cel-experiment/Cargo.toml delete mode 100644 packages/delphi-cel-experiment/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index feb4429c6e..becdde6fa4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2909,15 +2909,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204" -[[package]] -name = "delphi-cel-experiment" -version = "0.1.0" -dependencies = [ - "cel", - "serde", - "serde_json", -] - [[package]] name = "der" version = "0.7.10" diff --git a/Cargo.toml b/Cargo.toml index f6952a2d5b..916ad271a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ members = [ "packages/app-lib", "packages/ariadne", "packages/daedalus", - "packages/delphi-cel-experiment", "packages/labrinth-derive", "packages/modrinth-content-management", "packages/modrinth-log", diff --git a/apps/frontend/src/pages/moderation/technical-review/rules.vue b/apps/frontend/src/pages/moderation/technical-review/rules.vue index fbc86e94c5..ef158e1b01 100644 --- a/apps/frontend/src/pages/moderation/technical-review/rules.vue +++ b/apps/frontend/src/pages/moderation/technical-review/rules.vue @@ -461,10 +461,16 @@ const TEST_INPUTS: Labrinth.TechReview.Internal.RuleInput[] = [ sha512: 'fedcba9876543210', }, }, - scope: { - project_id: 'example-project', - version_id: 'example-version', - file_id: 'example-file', + project: { + id: 'example-project', + types: ['mod'], + }, + version: { + id: 'example-version', + loaders: ['fabric'], + }, + file: { + id: 'example-file', }, }, { @@ -489,10 +495,16 @@ const TEST_INPUTS: Labrinth.TechReview.Internal.RuleInput[] = [ sha512: '0123456789abcdef', }, }, - scope: { - project_id: 'telemetry-project', - version_id: 'telemetry-version', - file_id: 'telemetry-file', + project: { + id: 'telemetry-project', + types: ['mod'], + }, + version: { + id: 'telemetry-version', + loaders: ['neoforge'], + }, + file: { + id: 'telemetry-file', }, }, ] diff --git a/apps/labrinth/.sqlx/query-0f3d943e4fc48a94363b77c8a7d36eb1dd626e77331d8278c406df952691be4c.json b/apps/labrinth/.sqlx/query-0f3d943e4fc48a94363b77c8a7d36eb1dd626e77331d8278c406df952691be4c.json new file mode 100644 index 0000000000..4bc87e73a5 --- /dev/null +++ b/apps/labrinth/.sqlx/query-0f3d943e4fc48a94363b77c8a7d36eb1dd626e77331d8278c406df952691be4c.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COUNT(*) FROM payouts_values_notifications WHERE notified = FALSE AND user_id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + null + ] + }, + "hash": "0f3d943e4fc48a94363b77c8a7d36eb1dd626e77331d8278c406df952691be4c" +} diff --git a/apps/labrinth/.sqlx/query-0ffebf149d50149d0c128478e6267a2296efc16763cdbd5d8c295a1a7ca08021.json b/apps/labrinth/.sqlx/query-0ffebf149d50149d0c128478e6267a2296efc16763cdbd5d8c295a1a7ca08021.json new file mode 100644 index 0000000000..c8a82d60ef --- /dev/null +++ b/apps/labrinth/.sqlx/query-0ffebf149d50149d0c128478e6267a2296efc16763cdbd5d8c295a1a7ca08021.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO users (id, username, email, role)\n VALUES\n (1000, 'userland', 'userland@modrinth.com', 'developer'),\n (1001, 'useful', 'useful@modrinth.com', 'developer'),\n (1002, 'Useless', 'useless@modrinth.com', 'developer')\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "0ffebf149d50149d0c128478e6267a2296efc16763cdbd5d8c295a1a7ca08021" +} diff --git a/apps/labrinth/.sqlx/query-182be4110f45b6be6a9174200a07a887cebbaeab0a8c1da8b008e0c5e94fbc4e.json b/apps/labrinth/.sqlx/query-182be4110f45b6be6a9174200a07a887cebbaeab0a8c1da8b008e0c5e94fbc4e.json new file mode 100644 index 0000000000..5e057dc995 --- /dev/null +++ b/apps/labrinth/.sqlx/query-182be4110f45b6be6a9174200a07a887cebbaeab0a8c1da8b008e0c5e94fbc4e.json @@ -0,0 +1,32 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT id AS \"id!: DelphiRuleId\", name, rule\n FROM delphi_rules\n WHERE NOT delete_on_next_revision\n ORDER BY priority DESC, id\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id!: DelphiRuleId", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "rule", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "182be4110f45b6be6a9174200a07a887cebbaeab0a8c1da8b008e0c5e94fbc4e" +} diff --git a/apps/labrinth/.sqlx/query-1adbd24d815107e13bc1440c7a8f4eeff66ab4165a9f4980032e114db4dc1286.json b/apps/labrinth/.sqlx/query-1adbd24d815107e13bc1440c7a8f4eeff66ab4165a9f4980032e114db4dc1286.json new file mode 100644 index 0000000000..921f7f92d9 --- /dev/null +++ b/apps/labrinth/.sqlx/query-1adbd24d815107e13bc1440c7a8f4eeff66ab4165a9f4980032e114db4dc1286.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n id,\n status AS \"status: PayoutStatus\"\n FROM payouts\n ORDER BY id\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "status: PayoutStatus", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false + ] + }, + "hash": "1adbd24d815107e13bc1440c7a8f4eeff66ab4165a9f4980032e114db4dc1286" +} diff --git a/apps/labrinth/.sqlx/query-20cff8fdf7971e91c9d473b9a4663ce02ca16781e32232ae0fa7a0af1973d3a4.json b/apps/labrinth/.sqlx/query-20cff8fdf7971e91c9d473b9a4663ce02ca16781e32232ae0fa7a0af1973d3a4.json new file mode 100644 index 0000000000..3c99ff3fed --- /dev/null +++ b/apps/labrinth/.sqlx/query-20cff8fdf7971e91c9d473b9a4663ce02ca16781e32232ae0fa7a0af1973d3a4.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COUNT(*) FROM payouts_values_notifications WHERE notified = FALSE", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "20cff8fdf7971e91c9d473b9a4663ce02ca16781e32232ae0fa7a0af1973d3a4" +} diff --git a/apps/labrinth/.sqlx/query-329c009635ad2a4c214da2f4651b8c14fe329deac983bc2f43ba74e066f9cf8d.json b/apps/labrinth/.sqlx/query-329c009635ad2a4c214da2f4651b8c14fe329deac983bc2f43ba74e066f9cf8d.json new file mode 100644 index 0000000000..2731d395ff --- /dev/null +++ b/apps/labrinth/.sqlx/query-329c009635ad2a4c214da2f4651b8c14fe329deac983bc2f43ba74e066f9cf8d.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE mods SET status = 'processing' WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [] + }, + "hash": "329c009635ad2a4c214da2f4651b8c14fe329deac983bc2f43ba74e066f9cf8d" +} diff --git a/apps/labrinth/.sqlx/query-38ace0abad724d1111b79a72c7c8518f1272690ce7766dbdc41ec1e98f7f1fec.json b/apps/labrinth/.sqlx/query-38ace0abad724d1111b79a72c7c8518f1272690ce7766dbdc41ec1e98f7f1fec.json new file mode 100644 index 0000000000..815e7795ec --- /dev/null +++ b/apps/labrinth/.sqlx/query-38ace0abad724d1111b79a72c7c8518f1272690ce7766dbdc41ec1e98f7f1fec.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO users (id, username, email, role)\n VALUES (2100, 'prefix_under_score', 'prefix_under_score@modrinth.com', 'developer')\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "38ace0abad724d1111b79a72c7c8518f1272690ce7766dbdc41ec1e98f7f1fec" +} diff --git a/apps/labrinth/.sqlx/query-3f1aaeed6e394294fa32da7e7f83765fb64bd9499a796470d544466886e1a808.json b/apps/labrinth/.sqlx/query-3f1aaeed6e394294fa32da7e7f83765fb64bd9499a796470d544466886e1a808.json deleted file mode 100644 index bf96272e15..0000000000 --- a/apps/labrinth/.sqlx/query-3f1aaeed6e394294fa32da7e7f83765fb64bd9499a796470d544466886e1a808.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT id AS \"id!: DelphiRuleId\", rule\n FROM delphi_rules\n WHERE NOT delete_on_next_revision\n ORDER BY priority DESC, id\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id!: DelphiRuleId", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "rule", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false - ] - }, - "hash": "3f1aaeed6e394294fa32da7e7f83765fb64bd9499a796470d544466886e1a808" -} diff --git a/apps/labrinth/.sqlx/query-5cb65218a2a4a7343130d838f8a78df08d25844f1019d539712b0cb686e4ae81.json b/apps/labrinth/.sqlx/query-5cb65218a2a4a7343130d838f8a78df08d25844f1019d539712b0cb686e4ae81.json new file mode 100644 index 0000000000..55ba902620 --- /dev/null +++ b/apps/labrinth/.sqlx/query-5cb65218a2a4a7343130d838f8a78df08d25844f1019d539712b0cb686e4ae81.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO users (id, username, email, role)\n VALUES ($1, $2, $3, 'developer')\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Varchar", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "5cb65218a2a4a7343130d838f8a78df08d25844f1019d539712b0cb686e4ae81" +} diff --git a/apps/labrinth/.sqlx/query-6678cd4c51771cfaae2be8021ba66908ea41a06ba858dc5b523aef6aae27b850.json b/apps/labrinth/.sqlx/query-6678cd4c51771cfaae2be8021ba66908ea41a06ba858dc5b523aef6aae27b850.json new file mode 100644 index 0000000000..b4c2e5a56e --- /dev/null +++ b/apps/labrinth/.sqlx/query-6678cd4c51771cfaae2be8021ba66908ea41a06ba858dc5b523aef6aae27b850.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO payouts_values_notifications (date_available, user_id, notified)\n VALUES ($1, $2, FALSE)\n ON CONFLICT (date_available, user_id) DO NOTHING", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Timestamptz", + "Int8" + ] + }, + "nullable": [] + }, + "hash": "6678cd4c51771cfaae2be8021ba66908ea41a06ba858dc5b523aef6aae27b850" +} diff --git a/apps/labrinth/.sqlx/query-66868d0b35229abffa3f668eaef19dc0cdef502935dd5939a2f3ab89b0766ad2.json b/apps/labrinth/.sqlx/query-66868d0b35229abffa3f668eaef19dc0cdef502935dd5939a2f3ab89b0766ad2.json new file mode 100644 index 0000000000..5d509fdf07 --- /dev/null +++ b/apps/labrinth/.sqlx/query-66868d0b35229abffa3f668eaef19dc0cdef502935dd5939a2f3ab89b0766ad2.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "CREATE SCHEMA public;", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "66868d0b35229abffa3f668eaef19dc0cdef502935dd5939a2f3ab89b0766ad2" +} diff --git a/apps/labrinth/.sqlx/query-69a1cb4b7f1115a990d1fc4805d58541fc78e910111c09ba3d50a12d9ca4a9f8.json b/apps/labrinth/.sqlx/query-69a1cb4b7f1115a990d1fc4805d58541fc78e910111c09ba3d50a12d9ca4a9f8.json new file mode 100644 index 0000000000..fc7d2ac98d --- /dev/null +++ b/apps/labrinth/.sqlx/query-69a1cb4b7f1115a990d1fc4805d58541fc78e910111c09ba3d50a12d9ca4a9f8.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO payouts_values (user_id, mod_id, amount, created, date_available)\n VALUES ($1, NULL, $2, NOW(), $3)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Numeric", + "Timestamptz" + ] + }, + "nullable": [] + }, + "hash": "69a1cb4b7f1115a990d1fc4805d58541fc78e910111c09ba3d50a12d9ca4a9f8" +} diff --git a/apps/labrinth/.sqlx/query-868d19328921b9a3bd56fa9354e5e75d09c76a3eb3ed9c89b05b9aab13505bf9.json b/apps/labrinth/.sqlx/query-7e7e2e9f67158b94eb62834cbfd65fd5165d02ebcc0f8454554cbd870e4ddb42.json similarity index 51% rename from apps/labrinth/.sqlx/query-868d19328921b9a3bd56fa9354e5e75d09c76a3eb3ed9c89b05b9aab13505bf9.json rename to apps/labrinth/.sqlx/query-7e7e2e9f67158b94eb62834cbfd65fd5165d02ebcc0f8454554cbd870e4ddb42.json index 8fc73132b8..14a9377c81 100644 --- a/apps/labrinth/.sqlx/query-868d19328921b9a3bd56fa9354e5e75d09c76a3eb3ed9c89b05b9aab13505bf9.json +++ b/apps/labrinth/.sqlx/query-7e7e2e9f67158b94eb62834cbfd65fd5165d02ebcc0f8454554cbd870e4ddb42.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n detail.id,\n detail.key,\n issue.issue_type,\n detail.severity AS \"severity: DelphiSeverity\",\n detail.jar,\n detail.file_path,\n detail.data AS \"data: Json>\",\n report.delphi_version,\n file.size AS \"size?\",\n file.id AS \"file_id?\",\n version.id AS \"version_id?\",\n version.mod_id AS \"project_id?\",\n COALESCE(file_hashes.hashes, '{}'::jsonb)\n AS \"hashes!: Json>\"\n FROM delphi_report_issue_details detail\n INNER JOIN delphi_report_issues issue ON issue.id = detail.issue_id\n INNER JOIN delphi_reports report ON report.id = issue.report_id\n LEFT JOIN files file ON file.id = report.file_id\n LEFT JOIN versions version ON version.id = file.version_id\n LEFT JOIN LATERAL (\n SELECT\n jsonb_object_agg(algorithm, encode(hash, 'hex')) AS hashes\n FROM hashes\n WHERE hashes.file_id = file.id\n ) file_hashes ON TRUE\n WHERE detail.id = ANY($1::bigint[])\n ORDER BY detail.id\n ", + "query": "\n SELECT\n detail.id,\n detail.key,\n issue.issue_type,\n detail.severity AS \"severity: DelphiSeverity\",\n detail.jar,\n detail.file_path,\n detail.data AS \"data: Json>\",\n report.delphi_version,\n file.size AS \"size?\",\n file.id AS \"file_id?\",\n version.id AS \"version_id?\",\n version.mod_id AS \"project_id?\",\n COALESCE(version_metadata.project_types, ARRAY[]::text[])\n AS \"project_types!: Vec\",\n COALESCE(version_metadata.loaders, ARRAY[]::text[])\n AS \"loaders!: Vec\",\n COALESCE(file_hashes.hashes, '{}'::jsonb)\n AS \"hashes!: Json>\"\n FROM delphi_report_issue_details detail\n INNER JOIN delphi_report_issues issue ON issue.id = detail.issue_id\n INNER JOIN delphi_reports report ON report.id = issue.report_id\n LEFT JOIN files file ON file.id = report.file_id\n LEFT JOIN versions version ON version.id = file.version_id\n LEFT JOIN LATERAL (\n SELECT\n ARRAY_AGG(\n DISTINCT project_type.name::text\n ORDER BY project_type.name::text\n ) FILTER (WHERE project_type.name IS NOT NULL)\n AS project_types,\n ARRAY_AGG(\n DISTINCT loader.loader\n ORDER BY loader.loader\n ) AS loaders\n FROM loaders_versions loader_version\n INNER JOIN loaders loader\n ON loader.id = loader_version.loader_id\n LEFT JOIN loaders_project_types loader_project_type\n ON loader_project_type.joining_loader_id = loader_version.loader_id\n LEFT JOIN project_types project_type\n ON project_type.id = loader_project_type.joining_project_type_id\n WHERE loader_version.version_id = version.id\n ) version_metadata ON TRUE\n LEFT JOIN LATERAL (\n SELECT\n jsonb_object_agg(algorithm, encode(hash, 'hex')) AS hashes\n FROM hashes\n WHERE hashes.file_id = file.id\n ) file_hashes ON TRUE\n WHERE detail.id = ANY($1::bigint[])\n ORDER BY detail.id\n ", "describe": { "columns": [ { @@ -79,6 +79,16 @@ }, { "ordinal": 12, + "name": "project_types!: Vec", + "type_info": "TextArray" + }, + { + "ordinal": 13, + "name": "loaders!: Vec", + "type_info": "VarcharArray" + }, + { + "ordinal": 14, "name": "hashes!: Json>", "type_info": "Jsonb" } @@ -101,8 +111,10 @@ true, true, true, + null, + null, null ] }, - "hash": "868d19328921b9a3bd56fa9354e5e75d09c76a3eb3ed9c89b05b9aab13505bf9" + "hash": "7e7e2e9f67158b94eb62834cbfd65fd5165d02ebcc0f8454554cbd870e4ddb42" } diff --git a/apps/labrinth/.sqlx/query-2fb03e1de120cf921146b6dbd35691117412407c4225f966c013fdfef21a82fb.json b/apps/labrinth/.sqlx/query-8537362d9c11e437ada9ce97bb604666378d1d24981ab8113badded16fb63413.json similarity index 51% rename from apps/labrinth/.sqlx/query-2fb03e1de120cf921146b6dbd35691117412407c4225f966c013fdfef21a82fb.json rename to apps/labrinth/.sqlx/query-8537362d9c11e437ada9ce97bb604666378d1d24981ab8113badded16fb63413.json index 3e24779308..1102be54f7 100644 --- a/apps/labrinth/.sqlx/query-2fb03e1de120cf921146b6dbd35691117412407c4225f966c013fdfef21a82fb.json +++ b/apps/labrinth/.sqlx/query-8537362d9c11e437ada9ce97bb604666378d1d24981ab8113badded16fb63413.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n detail.id,\n detail.key,\n issue.issue_type,\n detail.severity AS \"severity: DelphiSeverity\",\n detail.jar,\n detail.file_path,\n detail.data AS \"data: Json>\",\n report.delphi_version,\n file.size AS \"size?\",\n file.id AS \"file_id?\",\n version.id AS \"version_id?\",\n version.mod_id AS \"project_id?\",\n COALESCE(file_hashes.hashes, '{}'::jsonb)\n AS \"hashes!: Json>\"\n FROM delphi_report_issue_details detail\n INNER JOIN delphi_report_issues issue ON issue.id = detail.issue_id\n INNER JOIN delphi_reports report ON report.id = issue.report_id\n LEFT JOIN files file ON file.id = report.file_id\n LEFT JOIN versions version ON version.id = file.version_id\n LEFT JOIN (\n SELECT\n file_id,\n jsonb_object_agg(algorithm, encode(hash, 'hex')) AS hashes\n FROM hashes\n GROUP BY file_id\n ) file_hashes ON file_hashes.file_id = file.id\n ORDER BY detail.id\n ", + "query": "\n SELECT\n detail.id,\n detail.key,\n issue.issue_type,\n detail.severity AS \"severity: DelphiSeverity\",\n detail.jar,\n detail.file_path,\n detail.data AS \"data: Json>\",\n report.delphi_version,\n file.size AS \"size?\",\n file.id AS \"file_id?\",\n version.id AS \"version_id?\",\n version.mod_id AS \"project_id?\",\n COALESCE(version_metadata.project_types, ARRAY[]::text[])\n AS \"project_types!: Vec\",\n COALESCE(version_metadata.loaders, ARRAY[]::text[])\n AS \"loaders!: Vec\",\n COALESCE(file_hashes.hashes, '{}'::jsonb)\n AS \"hashes!: Json>\"\n FROM delphi_report_issue_details detail\n INNER JOIN delphi_report_issues issue ON issue.id = detail.issue_id\n INNER JOIN delphi_reports report ON report.id = issue.report_id\n LEFT JOIN files file ON file.id = report.file_id\n LEFT JOIN versions version ON version.id = file.version_id\n LEFT JOIN (\n SELECT\n loader_version.version_id,\n ARRAY_AGG(\n DISTINCT project_type.name::text\n ORDER BY project_type.name::text\n ) FILTER (WHERE project_type.name IS NOT NULL)\n AS project_types,\n ARRAY_AGG(\n DISTINCT loader.loader\n ORDER BY loader.loader\n ) AS loaders\n FROM loaders_versions loader_version\n INNER JOIN loaders loader\n ON loader.id = loader_version.loader_id\n LEFT JOIN loaders_project_types loader_project_type\n ON loader_project_type.joining_loader_id = loader_version.loader_id\n LEFT JOIN project_types project_type\n ON project_type.id = loader_project_type.joining_project_type_id\n GROUP BY loader_version.version_id\n ) version_metadata\n ON version_metadata.version_id = version.id\n LEFT JOIN (\n SELECT\n file_id,\n jsonb_object_agg(algorithm, encode(hash, 'hex')) AS hashes\n FROM hashes\n GROUP BY file_id\n ) file_hashes ON file_hashes.file_id = file.id\n ORDER BY detail.id\n ", "describe": { "columns": [ { @@ -79,6 +79,16 @@ }, { "ordinal": 12, + "name": "project_types!: Vec", + "type_info": "TextArray" + }, + { + "ordinal": 13, + "name": "loaders!: Vec", + "type_info": "VarcharArray" + }, + { + "ordinal": 14, "name": "hashes!: Json>", "type_info": "Jsonb" } @@ -99,8 +109,10 @@ false, false, false, + null, + null, null ] }, - "hash": "2fb03e1de120cf921146b6dbd35691117412407c4225f966c013fdfef21a82fb" + "hash": "8537362d9c11e437ada9ce97bb604666378d1d24981ab8113badded16fb63413" } diff --git a/apps/labrinth/.sqlx/query-8ca7f46618bd72cfd5fe1f246d41a31da1885cc22bb61be6853950ba52be9aa7.json b/apps/labrinth/.sqlx/query-8ca7f46618bd72cfd5fe1f246d41a31da1885cc22bb61be6853950ba52be9aa7.json new file mode 100644 index 0000000000..444f57bf3b --- /dev/null +++ b/apps/labrinth/.sqlx/query-8ca7f46618bd72cfd5fe1f246d41a31da1885cc22bb61be6853950ba52be9aa7.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE moderation_locks SET locked_at = NOW() - ($1::bigint * INTERVAL '1 minute') - INTERVAL '1 second' WHERE project_id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [] + }, + "hash": "8ca7f46618bd72cfd5fe1f246d41a31da1885cc22bb61be6853950ba52be9aa7" +} diff --git a/apps/labrinth/.sqlx/query-1f318b0dda5aaf58b9e403f87fcacfcd223bd67f6d5ba95868ace4373a98c67d.json b/apps/labrinth/.sqlx/query-b536d7870ba77ace55e309938c3ba93c7016b2a6f18aa4f22ea57f604d82c1da.json similarity index 50% rename from apps/labrinth/.sqlx/query-1f318b0dda5aaf58b9e403f87fcacfcd223bd67f6d5ba95868ace4373a98c67d.json rename to apps/labrinth/.sqlx/query-b536d7870ba77ace55e309938c3ba93c7016b2a6f18aa4f22ea57f604d82c1da.json index 66ae4f6a27..1d7013755a 100644 --- a/apps/labrinth/.sqlx/query-1f318b0dda5aaf58b9e403f87fcacfcd223bd67f6d5ba95868ace4373a98c67d.json +++ b/apps/labrinth/.sqlx/query-b536d7870ba77ace55e309938c3ba93c7016b2a6f18aa4f22ea57f604d82c1da.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n detail.key,\n issue.issue_type,\n detail.severity AS \"severity: DelphiSeverity\",\n detail.jar,\n detail.file_path,\n detail.data AS \"data: Json>\",\n report.delphi_version,\n file.size AS \"size?\",\n file.id AS \"file_id?\",\n version.id AS \"version_id?\",\n version.mod_id AS \"project_id?\",\n COALESCE(file_hashes.hashes, '{}'::jsonb)\n AS \"hashes!: Json>\"\n FROM delphi_report_issue_details detail\n INNER JOIN delphi_report_issues issue ON issue.id = detail.issue_id\n INNER JOIN delphi_reports report ON report.id = issue.report_id\n LEFT JOIN files file ON file.id = report.file_id\n LEFT JOIN versions version ON version.id = file.version_id\n LEFT JOIN LATERAL (\n SELECT\n jsonb_object_agg(algorithm, encode(hash, 'hex')) AS hashes\n FROM hashes\n WHERE hashes.file_id = file.id\n ) file_hashes ON TRUE\n WHERE detail.id = $1\n ", + "query": "\n SELECT\n detail.key,\n issue.issue_type,\n detail.severity AS \"severity: DelphiSeverity\",\n detail.jar,\n detail.file_path,\n detail.data AS \"data: Json>\",\n report.delphi_version,\n file.size AS \"size?\",\n file.id AS \"file_id?\",\n version.id AS \"version_id?\",\n version.mod_id AS \"project_id?\",\n COALESCE(version_metadata.project_types, ARRAY[]::text[])\n AS \"project_types!: Vec\",\n COALESCE(version_metadata.loaders, ARRAY[]::text[])\n AS \"loaders!: Vec\",\n COALESCE(file_hashes.hashes, '{}'::jsonb)\n AS \"hashes!: Json>\"\n FROM delphi_report_issue_details detail\n INNER JOIN delphi_report_issues issue ON issue.id = detail.issue_id\n INNER JOIN delphi_reports report ON report.id = issue.report_id\n LEFT JOIN files file ON file.id = report.file_id\n LEFT JOIN versions version ON version.id = file.version_id\n LEFT JOIN LATERAL (\n SELECT\n ARRAY_AGG(\n DISTINCT project_type.name::text\n ORDER BY project_type.name::text\n ) FILTER (WHERE project_type.name IS NOT NULL)\n AS project_types,\n ARRAY_AGG(\n DISTINCT loader.loader\n ORDER BY loader.loader\n ) AS loaders\n FROM loaders_versions loader_version\n INNER JOIN loaders loader\n ON loader.id = loader_version.loader_id\n LEFT JOIN loaders_project_types loader_project_type\n ON loader_project_type.joining_loader_id = loader_version.loader_id\n LEFT JOIN project_types project_type\n ON project_type.id = loader_project_type.joining_project_type_id\n WHERE loader_version.version_id = version.id\n ) version_metadata ON TRUE\n LEFT JOIN LATERAL (\n SELECT\n jsonb_object_agg(algorithm, encode(hash, 'hex')) AS hashes\n FROM hashes\n WHERE hashes.file_id = file.id\n ) file_hashes ON TRUE\n WHERE detail.id = $1\n ", "describe": { "columns": [ { @@ -74,6 +74,16 @@ }, { "ordinal": 11, + "name": "project_types!: Vec", + "type_info": "TextArray" + }, + { + "ordinal": 12, + "name": "loaders!: Vec", + "type_info": "VarcharArray" + }, + { + "ordinal": 13, "name": "hashes!: Json>", "type_info": "Jsonb" } @@ -95,8 +105,10 @@ false, false, false, + null, + null, null ] }, - "hash": "1f318b0dda5aaf58b9e403f87fcacfcd223bd67f6d5ba95868ace4373a98c67d" + "hash": "b536d7870ba77ace55e309938c3ba93c7016b2a6f18aa4f22ea57f604d82c1da" } diff --git a/apps/labrinth/.sqlx/query-b92b5bb7d179c4fcdbc45600ccfd2402f52fea71e27b08e7926fcc2a9e62c0f3.json b/apps/labrinth/.sqlx/query-b92b5bb7d179c4fcdbc45600ccfd2402f52fea71e27b08e7926fcc2a9e62c0f3.json new file mode 100644 index 0000000000..89bd8147dc --- /dev/null +++ b/apps/labrinth/.sqlx/query-b92b5bb7d179c4fcdbc45600ccfd2402f52fea71e27b08e7926fcc2a9e62c0f3.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT status AS \"status: PayoutStatus\" FROM payouts WHERE id = 1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "status: PayoutStatus", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false + ] + }, + "hash": "b92b5bb7d179c4fcdbc45600ccfd2402f52fea71e27b08e7926fcc2a9e62c0f3" +} diff --git a/apps/labrinth/.sqlx/query-c65295f61b18f7be4fdf30b3e2c2c36b9db3770508be4acbb6b441ed8006d9c7.json b/apps/labrinth/.sqlx/query-c65295f61b18f7be4fdf30b3e2c2c36b9db3770508be4acbb6b441ed8006d9c7.json new file mode 100644 index 0000000000..f245ea1c8a --- /dev/null +++ b/apps/labrinth/.sqlx/query-c65295f61b18f7be4fdf30b3e2c2c36b9db3770508be4acbb6b441ed8006d9c7.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "DROP SCHEMA public CASCADE;", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "c65295f61b18f7be4fdf30b3e2c2c36b9db3770508be4acbb6b441ed8006d9c7" +} diff --git a/apps/labrinth/.sqlx/query-cb626a36deffd73e67de2dc4789bd875675779a173fb2cd41ba365c1acca96f9.json b/apps/labrinth/.sqlx/query-cb626a36deffd73e67de2dc4789bd875675779a173fb2cd41ba365c1acca96f9.json new file mode 100644 index 0000000000..39215aaedc --- /dev/null +++ b/apps/labrinth/.sqlx/query-cb626a36deffd73e67de2dc4789bd875675779a173fb2cd41ba365c1acca96f9.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE users SET email_verified = TRUE WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [] + }, + "hash": "cb626a36deffd73e67de2dc4789bd875675779a173fb2cd41ba365c1acca96f9" +} diff --git a/apps/labrinth/.sqlx/query-cd5ccd618fb3cc41646a6de86f9afedb074492b4ec7f2457c14113f5fd13aa02.json b/apps/labrinth/.sqlx/query-cd5ccd618fb3cc41646a6de86f9afedb074492b4ec7f2457c14113f5fd13aa02.json new file mode 100644 index 0000000000..469c30168a --- /dev/null +++ b/apps/labrinth/.sqlx/query-cd5ccd618fb3cc41646a6de86f9afedb074492b4ec7f2457c14113f5fd13aa02.json @@ -0,0 +1,18 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO payouts (id, method, platform_id, status, user_id, amount, created)\n VALUES ($1, $2, $3, $4, $5, 10.0, NOW())\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Text", + "Text", + "Varchar", + "Int8" + ] + }, + "nullable": [] + }, + "hash": "cd5ccd618fb3cc41646a6de86f9afedb074492b4ec7f2457c14113f5fd13aa02" +} diff --git a/apps/labrinth/.sqlx/query-cec4240c7c848988b3dfd13e3f8e5c93783c7641b019fdb698a1ec0be1393606.json b/apps/labrinth/.sqlx/query-cec4240c7c848988b3dfd13e3f8e5c93783c7641b019fdb698a1ec0be1393606.json new file mode 100644 index 0000000000..52e020ebf2 --- /dev/null +++ b/apps/labrinth/.sqlx/query-cec4240c7c848988b3dfd13e3f8e5c93783c7641b019fdb698a1ec0be1393606.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO payouts (id, method, platform_id, status, user_id, amount, created)\n VALUES ($1, $2, NULL, $3, $4, 10.00, NOW())\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Text", + "Varchar", + "Int8" + ] + }, + "nullable": [] + }, + "hash": "cec4240c7c848988b3dfd13e3f8e5c93783c7641b019fdb698a1ec0be1393606" +} diff --git a/apps/labrinth/.sqlx/query-fabba52b34e14196b6305671c060d934b9afc6dccc5fc77454fbe45365bdf429.json b/apps/labrinth/.sqlx/query-fabba52b34e14196b6305671c060d934b9afc6dccc5fc77454fbe45365bdf429.json new file mode 100644 index 0000000000..006563f450 --- /dev/null +++ b/apps/labrinth/.sqlx/query-fabba52b34e14196b6305671c060d934b9afc6dccc5fc77454fbe45365bdf429.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT pg_advisory_unlock(1)", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "pg_advisory_unlock", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "fabba52b34e14196b6305671c060d934b9afc6dccc5fc77454fbe45365bdf429" +} diff --git a/apps/labrinth/.sqlx/query-fd5c773a61d35bcd71503ec4d5f86e8917cfab9679d5064074681663ba467e41.json b/apps/labrinth/.sqlx/query-fd5c773a61d35bcd71503ec4d5f86e8917cfab9679d5064074681663ba467e41.json new file mode 100644 index 0000000000..d3e3520bcc --- /dev/null +++ b/apps/labrinth/.sqlx/query-fd5c773a61d35bcd71503ec4d5f86e8917cfab9679d5064074681663ba467e41.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COUNT(*) FROM notifications WHERE user_id = $1 AND body->>'type' = 'payout_available'", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + null + ] + }, + "hash": "fd5c773a61d35bcd71503ec4d5f86e8917cfab9679d5064074681663ba467e41" +} diff --git a/apps/labrinth/src/routes/internal/moderation/tech_review/rules.rs b/apps/labrinth/src/routes/internal/moderation/tech_review/rules.rs index 0021847bd9..a721937b1e 100644 --- a/apps/labrinth/src/routes/internal/moderation/tech_review/rules.rs +++ b/apps/labrinth/src/routes/internal/moderation/tech_review/rules.rs @@ -158,8 +158,9 @@ pub async fn test_rule( })?; let rule = request.rule; + let expression = rule.clone(); let program = - tokio::task::spawn_blocking(move || cel::Program::compile(&rule)) + tokio::task::spawn_blocking(move || cel::Program::compile(&expression)) .await .wrap_internal_err("failed to join cel compilation task")? .map_err(|error| { @@ -168,12 +169,11 @@ pub async fn test_rule( let mut effects = Vec::with_capacity(request.inputs.len()); for (index, input) in request.inputs.iter().enumerate() { - let effect = super::rules_scan::evaluate_rule(&program, input) - .map_err(|error| { - ApiError::Request(eyre!( - "failed to evaluate test input {index}: {error}" - )) - })?; + let evaluation = + super::rules_scan::evaluate_rule(&program, &rule, input); + let effect = evaluation.wrap_request_err(format!( + "failed to evaluate test input {index}" + ))?; effects.push(effect); } diff --git a/apps/labrinth/src/routes/internal/moderation/tech_review/rules_scan.rs b/apps/labrinth/src/routes/internal/moderation/tech_review/rules_scan.rs index ed39d82193..a0317f383c 100644 --- a/apps/labrinth/src/routes/internal/moderation/tech_review/rules_scan.rs +++ b/apps/labrinth/src/routes/internal/moderation/tech_review/rules_scan.rs @@ -58,7 +58,9 @@ pub struct RuleInput { pub trace: RuleTrace, pub scan: RuleScan, pub artifact: RuleArtifact, - pub scope: RuleScope, + pub project: RuleProject, + pub version: RuleVersion, + pub file: RuleFile, } #[derive(Deserialize, Serialize, utoipa::ToSchema)] @@ -83,14 +85,26 @@ pub struct RuleArtifact { } #[derive(Deserialize, Serialize, utoipa::ToSchema)] -pub struct RuleScope { - pub project_id: Option, - pub version_id: Option, - pub file_id: Option, +pub struct RuleProject { + pub id: Option, + pub types: Vec, +} + +#[derive(Deserialize, Serialize, utoipa::ToSchema)] +pub struct RuleVersion { + pub id: Option, + pub loaders: Vec, +} + +#[derive(Deserialize, Serialize, utoipa::ToSchema)] +pub struct RuleFile { + pub id: Option, } struct CompiledRule { id: DelphiRuleId, + name: String, + expression: String, program: cel::Program, } @@ -197,6 +211,10 @@ pub async fn get_detail_rule_input( file.id AS "file_id?", version.id AS "version_id?", version.mod_id AS "project_id?", + COALESCE(version_metadata.project_types, ARRAY[]::text[]) + AS "project_types!: Vec", + COALESCE(version_metadata.loaders, ARRAY[]::text[]) + AS "loaders!: Vec", COALESCE(file_hashes.hashes, '{}'::jsonb) AS "hashes!: Json>" FROM delphi_report_issue_details detail @@ -204,6 +222,26 @@ pub async fn get_detail_rule_input( INNER JOIN delphi_reports report ON report.id = issue.report_id LEFT JOIN files file ON file.id = report.file_id LEFT JOIN versions version ON version.id = file.version_id + LEFT JOIN LATERAL ( + SELECT + ARRAY_AGG( + DISTINCT project_type.name::text + ORDER BY project_type.name::text + ) FILTER (WHERE project_type.name IS NOT NULL) + AS project_types, + ARRAY_AGG( + DISTINCT loader.loader + ORDER BY loader.loader + ) AS loaders + FROM loaders_versions loader_version + INNER JOIN loaders loader + ON loader.id = loader_version.loader_id + LEFT JOIN loaders_project_types loader_project_type + ON loader_project_type.joining_loader_id = loader_version.loader_id + LEFT JOIN project_types project_type + ON project_type.id = loader_project_type.joining_project_type_id + WHERE loader_version.version_id = version.id + ) version_metadata ON TRUE LEFT JOIN LATERAL ( SELECT jsonb_object_agg(algorithm, encode(hash, 'hex')) AS hashes @@ -237,10 +275,16 @@ pub async fn get_detail_rule_input( size: detail.size, hashes: detail.hashes.0, }, - scope: RuleScope { - project_id: detail.project_id.map(|id| to_base62(id as u64)), - version_id: detail.version_id.map(|id| to_base62(id as u64)), - file_id: detail.file_id.map(|id| to_base62(id as u64)), + project: RuleProject { + id: detail.project_id.map(|id| to_base62(id as u64)), + types: detail.project_types, + }, + version: RuleVersion { + id: detail.version_id.map(|id| to_base62(id as u64)), + loaders: detail.loaders, + }, + file: RuleFile { + id: detail.file_id.map(|id| to_base62(id as u64)), }, })) } @@ -331,7 +375,7 @@ pub async fn scan_rules( } Err(error) => { tracing::error!(error = ?error, "delphi rule scan failed"); - let message = error.to_string(); + let message = format!("{error:#}"); let event = RuleScanErrorEvent { message: &message }; if let Ok(data) = serde_json::to_string(&event) { let _ = sender.send(Bytes::from(format!( @@ -402,6 +446,10 @@ async fn run_scan( file.id AS "file_id?", version.id AS "version_id?", version.mod_id AS "project_id?", + COALESCE(version_metadata.project_types, ARRAY[]::text[]) + AS "project_types!: Vec", + COALESCE(version_metadata.loaders, ARRAY[]::text[]) + AS "loaders!: Vec", COALESCE(file_hashes.hashes, '{}'::jsonb) AS "hashes!: Json>" FROM delphi_report_issue_details detail @@ -409,6 +457,28 @@ async fn run_scan( INNER JOIN delphi_reports report ON report.id = issue.report_id LEFT JOIN files file ON file.id = report.file_id LEFT JOIN versions version ON version.id = file.version_id + LEFT JOIN ( + SELECT + loader_version.version_id, + ARRAY_AGG( + DISTINCT project_type.name::text + ORDER BY project_type.name::text + ) FILTER (WHERE project_type.name IS NOT NULL) + AS project_types, + ARRAY_AGG( + DISTINCT loader.loader + ORDER BY loader.loader + ) AS loaders + FROM loaders_versions loader_version + INNER JOIN loaders loader + ON loader.id = loader_version.loader_id + LEFT JOIN loaders_project_types loader_project_type + ON loader_project_type.joining_loader_id = loader_version.loader_id + LEFT JOIN project_types project_type + ON project_type.id = loader_project_type.joining_project_type_id + GROUP BY loader_version.version_id + ) version_metadata + ON version_metadata.version_id = version.id LEFT JOIN ( SELECT file_id, @@ -458,19 +528,29 @@ async fn run_scan( size: detail.size, hashes: detail.hashes.0, }, - scope: RuleScope { - project_id: detail.project_id.map(|id| to_base62(id as u64)), - version_id: detail.version_id.map(|id| to_base62(id as u64)), - file_id: detail.file_id.map(|id| to_base62(id as u64)), + project: RuleProject { + id: detail.project_id.map(|id| to_base62(id as u64)), + types: detail.project_types, + }, + version: RuleVersion { + id: detail.version_id.map(|id| to_base62(id as u64)), + loaders: detail.loaders, + }, + file: RuleFile { + id: detail.file_id.map(|id| to_base62(id as u64)), }, }; for rule in &rules { - let effect = evaluate_rule(&rule.program, &input) + let effect = evaluate_rule( + &rule.program, + &rule.expression, + &input, + ) .wrap_err_with(|| { format!( - "failed to evaluate delphi rule {} for detail {detail_id}", - rule.id.0 + "failed to evaluate delphi rule '{}' for detail {detail_id}", + rule.name ) })?; if let Some(effect) = effect { @@ -642,6 +722,10 @@ pub(crate) async fn materialize_current_rule_effects( file.id AS "file_id?", version.id AS "version_id?", version.mod_id AS "project_id?", + COALESCE(version_metadata.project_types, ARRAY[]::text[]) + AS "project_types!: Vec", + COALESCE(version_metadata.loaders, ARRAY[]::text[]) + AS "loaders!: Vec", COALESCE(file_hashes.hashes, '{}'::jsonb) AS "hashes!: Json>" FROM delphi_report_issue_details detail @@ -649,6 +733,26 @@ pub(crate) async fn materialize_current_rule_effects( INNER JOIN delphi_reports report ON report.id = issue.report_id LEFT JOIN files file ON file.id = report.file_id LEFT JOIN versions version ON version.id = file.version_id + LEFT JOIN LATERAL ( + SELECT + ARRAY_AGG( + DISTINCT project_type.name::text + ORDER BY project_type.name::text + ) FILTER (WHERE project_type.name IS NOT NULL) + AS project_types, + ARRAY_AGG( + DISTINCT loader.loader + ORDER BY loader.loader + ) AS loaders + FROM loaders_versions loader_version + INNER JOIN loaders loader + ON loader.id = loader_version.loader_id + LEFT JOIN loaders_project_types loader_project_type + ON loader_project_type.joining_loader_id = loader_version.loader_id + LEFT JOIN project_types project_type + ON project_type.id = loader_project_type.joining_project_type_id + WHERE loader_version.version_id = version.id + ) version_metadata ON TRUE LEFT JOIN LATERAL ( SELECT jsonb_object_agg(algorithm, encode(hash, 'hex')) AS hashes @@ -683,19 +787,25 @@ pub(crate) async fn materialize_current_rule_effects( size: detail.size, hashes: detail.hashes.0, }, - scope: RuleScope { - project_id: detail.project_id.map(|id| to_base62(id as u64)), - version_id: detail.version_id.map(|id| to_base62(id as u64)), - file_id: detail.file_id.map(|id| to_base62(id as u64)), + project: RuleProject { + id: detail.project_id.map(|id| to_base62(id as u64)), + types: detail.project_types, + }, + version: RuleVersion { + id: detail.version_id.map(|id| to_base62(id as u64)), + loaders: detail.loaders, + }, + file: RuleFile { + id: detail.file_id.map(|id| to_base62(id as u64)), }, }; for rule in &rules { - let effect = - evaluate_rule(&rule.program, &input).wrap_err_with(|| { + let effect = evaluate_rule(&rule.program, &rule.expression, &input) + .wrap_err_with(|| { format!( - "failed to evaluate delphi rule {} for detail {}", - rule.id.0, detail.id + "failed to evaluate delphi rule '{}' for detail {}", + rule.name, detail.id ) })?; if let Some(effect) = effect { @@ -717,7 +827,7 @@ async fn fetch_compiled_rules( ) -> Result> { let rules = sqlx::query!( r#" - SELECT id AS "id!: DelphiRuleId", rule + SELECT id AS "id!: DelphiRuleId", name, rule FROM delphi_rules WHERE NOT delete_on_next_revision ORDER BY priority DESC, id @@ -734,12 +844,14 @@ async fn fetch_compiled_rules( let program = cel::Program::compile(&rule.rule).map_err(|error| { eyre!( - "failed to compile delphi rule {}: {error}", - rule.id.0 + "failed to compile delphi rule '{}': {error}", + rule.name ) })?; Ok(CompiledRule { id: rule.id, + name: rule.name, + expression: rule.rule, program, }) }) @@ -799,6 +911,21 @@ async fn insert_materialized_effects( } pub(super) fn evaluate_rule( + program: &cel::Program, + expression: &str, + input: &RuleInput, +) -> Result> { + evaluate_rule_inner(program, input) + .wrap_err_with(|| { + let input = serde_json::to_string(input).unwrap_or_else(|error| { + format!("") + }); + format!("CEL input: {input}") + }) + .wrap_err_with(|| format!("CEL expression: {expression}")) +} + +fn evaluate_rule_inner( program: &cel::Program, input: &RuleInput, ) -> Result> { @@ -816,8 +943,14 @@ pub(super) fn evaluate_rule( .add_variable("artifact", &input.artifact) .wrap_err("failed to add `artifact` to cel context")?; context - .add_variable("scope", &input.scope) - .wrap_err("failed to add `scope` to cel context")?; + .add_variable("project", &input.project) + .wrap_err("failed to add `project` to cel context")?; + context + .add_variable("version", &input.version) + .wrap_err("failed to add `version` to cel context")?; + context + .add_variable("file", &input.file) + .wrap_err("failed to add `file` to cel context")?; let value = program .execute(&context) diff --git a/packages/api-client/src/modules/labrinth/types.ts b/packages/api-client/src/modules/labrinth/types.ts index 67bd85bce7..a943512a30 100644 --- a/packages/api-client/src/modules/labrinth/types.ts +++ b/packages/api-client/src/modules/labrinth/types.ts @@ -2335,10 +2335,16 @@ export namespace Labrinth { size: number | null hashes: Record } - scope: { - project_id: string | null - version_id: string | null - file_id: string | null + project: { + id: string | null + types: string[] + } + version: { + id: string | null + loaders: string[] + } + file: { + id: string | null } } diff --git a/packages/delphi-cel-experiment/Cargo.toml b/packages/delphi-cel-experiment/Cargo.toml deleted file mode 100644 index 34ea4460b0..0000000000 --- a/packages/delphi-cel-experiment/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "delphi-cel-experiment" -version = "0.1.0" -edition.workspace = true -publish = false - -[dependencies] -cel = { workspace = true } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } diff --git a/packages/delphi-cel-experiment/src/main.rs b/packages/delphi-cel-experiment/src/main.rs deleted file mode 100644 index 8657e94a1c..0000000000 --- a/packages/delphi-cel-experiment/src/main.rs +++ /dev/null @@ -1,174 +0,0 @@ -use std::{collections::BTreeMap, error::Error, io}; - -use cel::{Context, Program}; -use serde::{Deserialize, Serialize}; - -const EFFECT_RULE: &str = r#" - trace.issue_type == "OBFUSCATED_NAMES" - && trace.severity == "high" - && "confidence" in trace.data - && trace.data.confidence >= 0.9 - ? { - "severity": "low" - } - : null -"#; - -#[derive(Debug, Serialize)] -struct RuleInput { - schema_version: u32, - trace: TraceInput, - scan: ScanInput, - artifact: ArtifactInput, - scope: ScopeInput, -} - -#[derive(Debug, Serialize)] -struct TraceInput { - key: String, - issue_type: String, - severity: DelphiSeverity, - jar: Option, - file_path: String, - data: BTreeMap, -} - -#[derive(Debug, Serialize)] -struct ScanInput { - delphi_version: i32, -} - -#[derive(Debug, Serialize)] -struct ArtifactInput { - size: u32, - hashes: BTreeMap, -} - -#[derive(Debug, Serialize)] -struct ScopeInput { - project_id: String, - version_id: String, - file_id: String, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -enum DelphiSeverity { - Hidden, - Low, - Medium, - High, - Severe, - Malware, -} - -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -struct RuleEffect { - severity: DelphiSeverity, -} - -fn main() -> Result<(), Box> { - let input = example_input(); - println!( - "CEL input context:\n{}", - serde_json::to_string_pretty(&input)? - ); - - let effect = evaluate_rule(EFFECT_RULE, &input)?; - println!("\nCEL map decoded as RuleEffect:\n{effect:#?}"); - - Ok(()) -} - -fn example_input() -> RuleInput { - RuleInput { - schema_version: 1, - trace: TraceInput { - key: "known-safe:obfuscated-bootstrap".to_string(), - issue_type: "OBFUSCATED_NAMES".to_string(), - severity: DelphiSeverity::High, - jar: Some("META-INF/jars/embedded.jar".to_string()), - file_path: "com/example/Bootstrap.class".to_string(), - data: BTreeMap::from([ - ("confidence".to_string(), serde_json::json!(0.97)), - ("symbol_count".to_string(), serde_json::json!(42)), - ]), - }, - scan: ScanInput { delphi_version: 17 }, - artifact: ArtifactInput { - size: 412_892, - hashes: BTreeMap::from([ - ("sha1".to_string(), "0123456789abcdef".to_string()), - ("sha512".to_string(), "fedcba9876543210".to_string()), - ]), - }, - scope: ScopeInput { - project_id: "AANobbMI".to_string(), - version_id: "IIJJKKLL".to_string(), - file_id: "XXYYZZ00".to_string(), - }, - } -} - -fn evaluate_rule( - expression: &str, - input: &RuleInput, -) -> Result, Box> { - let mut context = Context::default(); - context.add_variable("schema_version", input.schema_version)?; - context.add_variable("trace", &input.trace)?; - context.add_variable("scan", &input.scan)?; - context.add_variable("artifact", &input.artifact)?; - context.add_variable("scope", &input.scope)?; - - let value = Program::compile(expression)?.execute(&context)?; - let json = value - .json() - .map_err(|error| invalid_data(error.to_string()))?; - match json { - serde_json::Value::Null => Ok(None), - value => Ok(Some(serde_json::from_value(value)?)), - } -} - -fn invalid_data(message: impl Into) -> io::Error { - io::Error::new(io::ErrorKind::InvalidData, message.into()) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn decodes_a_map_as_an_effect() { - let effect = evaluate_rule(EFFECT_RULE, &example_input()) - .expect("rule should evaluate"); - - assert_eq!( - effect, - Some(RuleEffect { - severity: DelphiSeverity::Low, - }) - ); - } - - #[test] - fn returns_none_when_a_rule_does_not_match() { - let mut input = example_input(); - input.trace.issue_type = "NETWORK_ACCESS".to_string(); - - let effect = - evaluate_rule(EFFECT_RULE, &input).expect("rule should evaluate"); - - assert_eq!(effect, None); - } - - #[test] - fn rejects_unknown_map_fields() { - let error = evaluate_rule(r#"{"unknown": true}"#, &example_input()) - .expect_err("unknown fields should be rejected"); - - assert!(error.to_string().contains("unknown field")); - } -}