fix: test & lint

This commit is contained in:
DarkSky
2026-06-22 01:19:37 +08:00
parent 9f712e690b
commit 778d34ca7c
3 changed files with 66 additions and 3 deletions
@@ -10,6 +10,8 @@ use sha2::{Digest, Sha256};
use super::{BackendRuntime, error::napi_error, types::RuntimeBlobCompleteResult}; use super::{BackendRuntime, error::napi_error, types::RuntimeBlobCompleteResult};
const MAX_BLOB_SIZE: i64 = i32::MAX as i64;
fn object_missing_error(err: &napi::Error) -> bool { fn object_missing_error(err: &napi::Error) -> bool {
let message = err.to_string(); let message = err.to_string();
message.contains("NoSuchKey") || message.contains("NotFound") || message.contains("not found") message.contains("NoSuchKey") || message.contains("NotFound") || message.contains("not found")
@@ -110,6 +112,11 @@ async fn upsert_completed_blob(
mime: &str, mime: &str,
size: i64, size: i64,
) -> Result<()> { ) -> Result<()> {
if !(0..=MAX_BLOB_SIZE).contains(&size) {
return Err(napi_error("BlobComplete size exceeds limit"));
}
let size = i32::try_from(size).map_err(|_| napi_error("BlobComplete size exceeds limit"))?;
sqlx::query( sqlx::query(
r#" r#"
INSERT INTO blobs (workspace_id, key, mime, size, status, upload_id) INSERT INTO blobs (workspace_id, key, mime, size, status, upload_id)
@@ -125,7 +132,7 @@ async fn upsert_completed_blob(
.bind(workspace_id) .bind(workspace_id)
.bind(key) .bind(key)
.bind(mime) .bind(mime)
.bind(size as i32) .bind(size)
.execute(&runtime.pool().await?) .execute(&runtime.pool().await?)
.await .await
.map_err(|err| napi_error(format!("BlobComplete upsert metadata failed: {err}")))?; .map_err(|err| napi_error(format!("BlobComplete upsert metadata failed: {err}")))?;
@@ -143,6 +150,10 @@ impl BackendRuntime {
expected_size: i64, expected_size: i64,
expected_mime: String, expected_mime: String,
) -> Result<RuntimeBlobCompleteResult> { ) -> Result<RuntimeBlobCompleteResult> {
if !(0..=MAX_BLOB_SIZE).contains(&expected_size) {
return Ok(blob_complete_failure("size_too_large"));
}
let object_key = format!("{workspace_id}/{key}"); let object_key = format!("{workspace_id}/{key}");
let object = match self.object_storage_get(object_key.clone()).await { let object = match self.object_storage_get(object_key.clone()).await {
Ok(Some(object)) => object, Ok(Some(object)) => object,
@@ -151,6 +162,14 @@ impl BackendRuntime {
Err(err) => return Err(err), Err(err) => return Err(err),
}; };
if !(0..=MAX_BLOB_SIZE).contains(&object.metadata.content_length) {
match self.object_storage_delete(object_key).await {
Ok(()) => {}
Err(err) if object_missing_error(&err) => {}
Err(err) => return Err(err),
}
return Ok(blob_complete_failure("size_too_large"));
}
if object.metadata.content_length != expected_size { if object.metadata.content_length != expected_size {
return Ok(blob_complete_failure("size_mismatch")); return Ok(blob_complete_failure("size_mismatch"));
} }
@@ -194,6 +213,10 @@ impl BackendRuntime {
expected_size: i64, expected_size: i64,
expected_mime: String, expected_mime: String,
) -> Result<RuntimeBlobCompleteResult> { ) -> Result<RuntimeBlobCompleteResult> {
if !(0..=MAX_BLOB_SIZE).contains(&expected_size) {
return Ok(blob_complete_failure("size_too_large"));
}
let storage_key = format!("{workspace_id}/{key}"); let storage_key = format!("{workspace_id}/{key}");
let path = fs_object_path(&root, &bucket, &storage_key)?; let path = fs_object_path(&root, &bucket, &storage_key)?;
let metadata = match read_fs_metadata(&path)? { let metadata = match read_fs_metadata(&path)? {
@@ -201,6 +224,11 @@ impl BackendRuntime {
None => return Ok(blob_complete_failure("not_found")), None => return Ok(blob_complete_failure("not_found")),
}; };
if !(0..=MAX_BLOB_SIZE).contains(&metadata.content_length) {
let _ = fs::remove_file(&path);
let _ = fs::remove_file(PathBuf::from(format!("{}.metadata.json", path.display())));
return Ok(blob_complete_failure("size_too_large"));
}
if metadata.content_length != expected_size { if metadata.content_length != expected_size {
return Ok(blob_complete_failure("size_mismatch")); return Ok(blob_complete_failure("size_mismatch"));
} }
@@ -1,7 +1,7 @@
use super::{runtime_state::*, *};
use anyhow::{Context, Result as AnyResult, anyhow}; use anyhow::{Context, Result as AnyResult, anyhow};
use super::{runtime_state::*, *};
static PG_TEST_LOCK: std::sync::OnceLock<tokio::sync::Mutex<()>> = std::sync::OnceLock::new(); static PG_TEST_LOCK: std::sync::OnceLock<tokio::sync::Mutex<()>> = std::sync::OnceLock::new();
const TEST_VERIFICATION_TOKEN_TYPE: i32 = 99_999; const TEST_VERIFICATION_TOKEN_TYPE: i32 = 99_999;
+35
View File
@@ -2,6 +2,7 @@ import serverNativeModule, {
type ActionEvent as NativeActionEventContract, type ActionEvent as NativeActionEventContract,
type ActionRuntimeInput as NativeActionRuntimeInputContract, type ActionRuntimeInput as NativeActionRuntimeInputContract,
type AssertSafeUrlRequest, type AssertSafeUrlRequest,
type BackendRuntimeHealth,
type BuiltInPromptRenderContract, type BuiltInPromptRenderContract,
type BuiltInPromptSessionContract, type BuiltInPromptSessionContract,
type BuiltInPromptSpec, type BuiltInPromptSpec,
@@ -45,6 +46,22 @@ import serverNativeModule, {
type RequestedModelMatchResponse, type RequestedModelMatchResponse,
type ResolvedEntitlement, type ResolvedEntitlement,
type ResolveEntitlementInput, type ResolveEntitlementInput,
type RuntimeBlobCleanupResult,
type RuntimeBlobCompleteResult,
type RuntimeByokLocalLeaseRecord,
type RuntimeDocCompactionResult,
type RuntimeMagicLinkOtpConsumeResult,
type RuntimeMultipartUploadInit,
type RuntimeMultipartUploadPart,
type RuntimeObjectGetResult,
type RuntimeObjectListEntry,
type RuntimeObjectMetadata,
type RuntimeObjectStorageHealth,
type RuntimeObjectStoragePutOptions,
type RuntimePresignedObjectRequest,
type RuntimeVerificationTokenRecord,
type RuntimeWorkspaceInviteLinkRecord,
type RuntimeWorkspaceStatsDailyRecalibrationResult,
type SafeFetchRequest, type SafeFetchRequest,
type SafeFetchResponse, type SafeFetchResponse,
type Tokenizer, type Tokenizer,
@@ -52,6 +69,7 @@ import serverNativeModule, {
export type { export type {
AssertSafeUrlRequest, AssertSafeUrlRequest,
BackendRuntimeHealth,
CapabilityAttachmentContract, CapabilityAttachmentContract,
CapabilityModelCapability, CapabilityModelCapability,
CommandResponse, CommandResponse,
@@ -73,6 +91,22 @@ export type {
RemoteMimeTypeRequest, RemoteMimeTypeRequest,
ResolvedEntitlement, ResolvedEntitlement,
ResolveEntitlementInput, ResolveEntitlementInput,
RuntimeBlobCleanupResult,
RuntimeBlobCompleteResult,
RuntimeByokLocalLeaseRecord,
RuntimeDocCompactionResult,
RuntimeMagicLinkOtpConsumeResult,
RuntimeMultipartUploadInit,
RuntimeMultipartUploadPart,
RuntimeObjectGetResult,
RuntimeObjectListEntry,
RuntimeObjectMetadata,
RuntimeObjectStorageHealth,
RuntimeObjectStoragePutOptions,
RuntimePresignedObjectRequest,
RuntimeVerificationTokenRecord,
RuntimeWorkspaceInviteLinkRecord,
RuntimeWorkspaceStatsDailyRecalibrationResult,
SafeFetchRequest, SafeFetchRequest,
SafeFetchResponse, SafeFetchResponse,
}; };
@@ -180,6 +214,7 @@ export const readAllDocIdsFromRootDoc =
export const AFFINE_PRO_PUBLIC_KEY = serverNativeModule.AFFINE_PRO_PUBLIC_KEY; export const AFFINE_PRO_PUBLIC_KEY = serverNativeModule.AFFINE_PRO_PUBLIC_KEY;
export const AFFINE_PRO_LICENSE_AES_KEY = export const AFFINE_PRO_LICENSE_AES_KEY =
serverNativeModule.AFFINE_PRO_LICENSE_AES_KEY; serverNativeModule.AFFINE_PRO_LICENSE_AES_KEY;
export const BackendRuntime = serverNativeModule.BackendRuntime;
export type PermissionWorkspaceRole = 'external' | 'member' | 'admin' | 'owner'; export type PermissionWorkspaceRole = 'external' | 'member' | 'admin' | 'owner';
export type PermissionDocRole = export type PermissionDocRole =