diff --git a/CALCKEY.md b/CALCKEY.md index 619155238..bb28e5bbf 100644 --- a/CALCKEY.md +++ b/CALCKEY.md @@ -6,19 +6,13 @@ ## Planned - Stucture - - [DragonflyDB](https://dragonflydb.io/) support as a Redis alternative - - Optionally use [ScyllaDB](https://www.scylladb.com/open-source-nosql-database/) for storing notes - Rewrite backend in Rust and [Rocket](https://rocket.rs/) - - Use [Magic RegExP](https://regexp.dev/) for RegEx 🦄 - Function - User "choices" (recommended users) and featured hashtags like Mastodon and Soapbox - Join Reason system like Mastodon/Pleroma - Option to publicize server blocks - More antenna options - Groups -- Form - - Lookup/details for post/file/server - - [Rat mode?](https://stop.voring.me/notes/933fx97bmd) ## Work in progress @@ -30,6 +24,7 @@ - Timeline filters - Events - Fully revamp non-logged-in screen +- Optionally use [ScyllaDB](https://www.scylladb.com/open-source-nosql-database/) for storing notes ## Implemented @@ -122,6 +117,7 @@ - Let moderators see moderation nodes - Non-mangled unicode emojis - Skin tone selection support +- [DragonflyDB](https://dragonflydb.io/) support as a Redis alternative ## Implemented (remote) diff --git a/package.json b/package.json index a30d85b3d..d4460c878 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "calckey", - "version": "14.0.0-dev78", + "version": "14.0.0-dev79", "codename": "aqua", "repository": { "type": "git", @@ -57,7 +57,7 @@ "gulp-replace": "1.1.4", "gulp-terser": "2.1.0", "install-peers": "^1.0.4", - "rome": "^12.1.3", + "rome": "^v12.1.3-nightly.f65b0d9", "start-server-and-test": "1.15.2", "typescript": "5.1.6" } diff --git a/packages/backend/migration/1678426061773-tweak-varchar-length.js b/packages/backend/migration/1678426061773-tweak-varchar-length.js index 883374599..00ddcaebe 100644 --- a/packages/backend/migration/1678426061773-tweak-varchar-length.js +++ b/packages/backend/migration/1678426061773-tweak-varchar-length.js @@ -1,10 +1,16 @@ export class tweakVarcharLength1678426061773 { - name = 'tweakVarcharLength1678426061773' + name = "tweakVarcharLength1678426061773"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "smtpUser" TYPE character varying(1024)`, undefined); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "smtpPass" TYPE character varying(1024)`, undefined); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "smtpUser" TYPE character varying(1024)`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "smtpPass" TYPE character varying(1024)`, + undefined, + ); + } - async down(queryRunner) {} + async down(queryRunner) {} } diff --git a/packages/backend/native-utils/package.json b/packages/backend/native-utils/package.json index 385330d77..962b4bc4c 100644 --- a/packages/backend/native-utils/package.json +++ b/packages/backend/native-utils/package.json @@ -43,6 +43,7 @@ "universal": "napi universal", "version": "napi version", "format": "cargo fmt --all", + "lint": "cargo clippy --fix", "cargo:test": "pnpm run cargo:unit && pnpm run cargo:integration", "cargo:unit": "cargo test unit_test && cargo test -F napi unit_test", "cargo:integration": "cargo test -F noarray int_test -- --test-threads=1" diff --git a/packages/backend/native-utils/src/model/repository/antenna.rs b/packages/backend/native-utils/src/model/repository/antenna.rs index 7c614b954..2b761173e 100644 --- a/packages/backend/native-utils/src/model/repository/antenna.rs +++ b/packages/backend/native-utils/src/model/repository/antenna.rs @@ -46,7 +46,7 @@ impl Repository for antenna::Model { src: self.src.try_into()?, user_list_id: self.user_list_id, user_group_id, - users: self.users.into(), + users: self.users, instances: self.instances.into(), case_sensitive: self.case_sensitive, notify: self.notify, diff --git a/packages/backend/native-utils/src/model/schema/antenna.rs b/packages/backend/native-utils/src/model/schema/antenna.rs index 4ec1e0794..da2c3061b 100644 --- a/packages/backend/native-utils/src/model/schema/antenna.rs +++ b/packages/backend/native-utils/src/model/schema/antenna.rs @@ -58,7 +58,7 @@ impl TryFrom for super::AntennaSrc { // ---- TODO: could be macro impl Schema for super::Antenna {} -pub static VALIDATOR: Lazy = Lazy::new(|| super::Antenna::validator()); +pub static VALIDATOR: Lazy = Lazy::new(super::Antenna::validator); // ---- cfg_if! { diff --git a/packages/backend/native-utils/src/model/schema/app.rs b/packages/backend/native-utils/src/model/schema/app.rs index 682b82ec0..9b5691154 100644 --- a/packages/backend/native-utils/src/model/schema/app.rs +++ b/packages/backend/native-utils/src/model/schema/app.rs @@ -91,7 +91,7 @@ pub enum AppPermission { impl Schema for App {} -pub static VALIDATOR: Lazy = Lazy::new(|| App::validator()); +pub static VALIDATOR: Lazy = Lazy::new(App::validator); #[cfg(test)] mod unit_test { diff --git a/packages/backend/native-utils/tests/common.rs b/packages/backend/native-utils/tests/common.rs index 186e862bd..b134319ca 100644 --- a/packages/backend/native-utils/tests/common.rs +++ b/packages/backend/native-utils/tests/common.rs @@ -148,8 +148,8 @@ async fn setup_model(db: &DbConn) { let user_model = entity::user::Model { id: user_id.to_owned(), created_at: Utc::now().into(), - username: name.to_lowercase().to_string(), - username_lower: name.to_lowercase().to_string(), + username: name.to_lowercase(), + username_lower: name.to_lowercase(), name: Some(name.to_string()), token: Some(gen_string(16)), is_admin: true, diff --git a/packages/backend/native-utils/tests/model/repository/antenna.rs b/packages/backend/native-utils/tests/model/repository/antenna.rs index 3bda2ca18..80eea6771 100644 --- a/packages/backend/native-utils/tests/model/repository/antenna.rs +++ b/packages/backend/native-utils/tests/model/repository/antenna.rs @@ -43,18 +43,16 @@ mod int_test { keywords: vec![ vec!["foo".to_string(), "bar".to_string()], vec!["foobar".to_string()], - ] - .into(), + ], exclude_keywords: vec![ vec!["abc".to_string()], vec!["def".to_string(), "ghi".to_string()], - ] - .into(), + ], src: schema::AntennaSrc::All, user_list_id: None, user_group_id: None, - users: vec![].into(), - instances: vec![].into(), + users: vec![], + instances: vec![], case_sensitive: true, notify: true, with_replies: false, diff --git a/packages/backend/package.json b/packages/backend/package.json index d05583845..99f1cadd5 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -73,6 +73,7 @@ "is-svg": "4.3.2", "js-yaml": "4.1.0", "jsdom": "20.0.3", + "json5": "2.2.3", "jsonld": "8.2.0", "jsrsasign": "10.8.6", "koa": "2.14.2", @@ -188,7 +189,6 @@ "cross-env": "7.0.3", "eslint": "^8.44.0", "execa": "6.1.0", - "json5": "2.2.3", "json5-loader": "4.0.1", "mocha": "10.2.0", "pug": "3.0.2", diff --git a/packages/backend/src/const.ts b/packages/backend/src/const.ts index 49f012c5e..2a955ee52 100644 --- a/packages/backend/src/const.ts +++ b/packages/backend/src/const.ts @@ -1,5 +1,8 @@ import config from "@/config/index.js"; -import { DB_MAX_NOTE_TEXT_LENGTH, DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js"; +import { + DB_MAX_NOTE_TEXT_LENGTH, + DB_MAX_IMAGE_COMMENT_LENGTH, +} from "@/misc/hard-limits.js"; export const MAX_NOTE_TEXT_LENGTH = Math.min( config.maxNoteLength ?? 3000, diff --git a/packages/backend/src/misc/download-url.ts b/packages/backend/src/misc/download-url.ts index 7fafb635b..e9975f348 100644 --- a/packages/backend/src/misc/download-url.ts +++ b/packages/backend/src/misc/download-url.ts @@ -24,6 +24,7 @@ export async function downloadUrl(url: string, path: string): Promise { .stream(url, { headers: { "User-Agent": config.userAgent, + Host: new URL(url).hostname, }, timeout: { lookup: timeout, diff --git a/packages/backend/src/server/proxy/proxy-media.ts b/packages/backend/src/server/proxy/proxy-media.ts index a9c257bfe..b3bb03124 100644 --- a/packages/backend/src/server/proxy/proxy-media.ts +++ b/packages/backend/src/server/proxy/proxy-media.ts @@ -1,4 +1,6 @@ import * as fs from "node:fs"; +import net from "node:net"; +import { promises } from "node:dns"; import type Koa from "koa"; import sharp from "sharp"; import type { IImage } from "@/services/drive/image-processor.js"; @@ -19,6 +21,40 @@ export async function proxyMedia(ctx: Koa.Context) { return; } + const { hostname } = new URL(url); + let resolvedIps; + try { + resolvedIps = await promises.resolve(hostname); + } catch (error) { + ctx.status = 400; + ctx.body = { message: "Invalid URL" }; + return; + } + + const isSSRF = resolvedIps.some((ip) => { + if (net.isIPv4(ip)) { + const parts = ip.split(".").map(Number); + return ( + parts[0] === 10 || + (parts[0] === 172 && parts[1] >= 16 && parts[1] < 32) || + (parts[0] === 192 && parts[1] === 168) || + parts[0] === 127 || + parts[0] === 0 + ); + } else if (net.isIPv6(ip)) { + return ( + ip.startsWith("::") || ip.startsWith("fc00:") || ip.startsWith("fe80:") + ); + } + return false; + }); + + if (isSSRF) { + ctx.status = 400; + ctx.body = { message: "Access to this URL is not allowed" }; + return; + } + // Create temp file const [path, cleanup] = await createTemp(); diff --git a/packages/client/.eslintrc.json b/packages/client/.eslintrc.json new file mode 100644 index 000000000..fd4718003 --- /dev/null +++ b/packages/client/.eslintrc.json @@ -0,0 +1,7 @@ +{ + "extends": ["@eslint-sets/vue3", "@eslint-sets/vue3-ts"], + "plugins": ["file-progress", "prettier"], + "rules": { + "file-progress/activate": 1 + } +} diff --git a/packages/client/package.json b/packages/client/package.json index 95fb9f9b2..3fe101b81 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -4,11 +4,14 @@ "scripts": { "watch": "pnpm vite build --watch --mode development", "build": "pnpm vite build", - "lint": "pnpm rome check \"src/**/*.{ts,vue}\"", - "format": "pnpm rome format * --write && pnpm prettier --write '**/*.{scss,vue}'" + "lint": "pnpm rome check **/*.ts --apply && pnpm run lint:vue", + "lint:vue": "pnpm paralint --ext .vue --fix '**/*.vue' --cache", + "format": "pnpm rome format * --write && pnpm prettier --write '**/*.{scss,vue}' --cache --cache-strategy metadata" }, "devDependencies": { "@discordapp/twemoji": "14.1.2", + "@eslint-sets/eslint-config-vue3": "^5.6.1", + "@eslint-sets/eslint-config-vue3-ts": "^3.3.0", "@phosphor-icons/web": "^2.0.3", "@rollup/plugin-alias": "3.1.9", "@rollup/plugin-json": "4.1.0", @@ -46,6 +49,8 @@ "date-fns": "2.30.0", "emojilib": "github:thatonecalculator/emojilib", "escape-regexp": "0.0.1", + "eslint-config-prettier": "^8.6.0", + "eslint-plugin-file-progress": "^1.3.0", "eventemitter3": "5.0.1", "fast-blurhash": "^1.1.2", "focus-trap": "^7.5.2", @@ -57,6 +62,7 @@ "katex": "0.16.8", "matter-js": "0.18.0", "mfm-js": "0.23.3", + "paralint": "^1.2.1", "photoswipe": "5.3.8", "prettier": "3.0.0", "prettier-plugin-vue": "1.1.6", diff --git a/packages/client/src/components/MkAbuseReport.vue b/packages/client/src/components/MkAbuseReport.vue index ccb85d722..f92d10ba7 100644 --- a/packages/client/src/components/MkAbuseReport.vue +++ b/packages/client/src/components/MkAbuseReport.vue @@ -80,11 +80,11 @@ const emit = defineEmits<{ (ev: "resolved", reportId: string): void; }>(); -let forward = $ref(props.report.forwarded); +const forward = $ref(props.report.forwarded); function resolve() { os.apiWithDialog("admin/resolve-abuse-user-report", { - forward: forward, + forward, reportId: props.report.id, }).then(() => { emit("resolved", props.report.id); diff --git a/packages/client/src/components/MkAbuseReportWindow.vue b/packages/client/src/components/MkAbuseReportWindow.vue index 6fdf3b9e0..fc80cd66f 100644 --- a/packages/client/src/components/MkAbuseReportWindow.vue +++ b/packages/client/src/components/MkAbuseReportWindow.vue @@ -41,7 +41,7 @@