Repository: directus/directus
File: packages/storage-driver-s3/src/index.ts (TypeScript)

Code before:
import { Agent as HttpAgent } from 'node:http';
import { Agent as HttpsAgent } from 'node:https';
import os from 'node:os';
import { join } from 'node:path';
import stream, { promises as streamProm, type Readable } from 'node:stream';

export type DriverS3Config = {
	root?: string | undefined;
	key?: string | undefined;
	secret?: string | undefined;
	bucket: string | undefined;
	acl?: ObjectCannedACL | undefined;
	serverSideEncryption?: ServerSideEncryption | undefined;
	serverSideEncryptionKmsKeyId?: string | undefined;
	endpoint?: string | undefined;
	region?: string | undefined;
	forcePathStyle?: boolean | undefined;
	tus?:
		| {
				chunkSize?: number | undefined;
		  }
		| undefined;
	connectionTimeout?: number | undefined;
	socketTimeout?: number | undefined;
	maxSockets?: number | undefined;
	keepAlive?: boolean | undefined;
};

export class DriverS3 implements TusDriver {
	private config: DriverS3Config;
	private readonly client: S3Client;
	private readonly root: string;

	// TUS specific members
	private partUploadSemaphore: Semaphore;
	private readonly preferredPartSize: number;
	public maxMultipartParts = 10_000 as const;
	public minPartSize = 5_242_880 as const; // 5MiB
	public maxUploadSize = 5_497_558_138_880 as const; // 5TiB

	constructor(config: DriverS3Config) {
		this.config = config;
		this.client = this.getClient();
		this.root = this.config.root ? normalizePath(this.config.root, { removeLeading: true }) : '';

		this.preferredPartSize = config.tus?.chunkSize ?? this.minPartSize;
		this.partUploadSemaphore = new Semaphore(60);
	}

	private getClient() {
		/*

Review comment (security):
`bucket` looks to be made optional by accident, let's just add the `serverSideEncryptionKmsKeyId`.

```suggestion
	root?: string;
	key?: string;
	secret?: string;
	bucket: string;
	acl?: ObjectCannedACL;
	serverSideEncryption?: ServerSideEncryption;
	serverSideEncryptionKmsKeyId?: string;
	endpoint?: string;
	region?: string;
	forcePathStyle?: boolean;
	tus?: {
		chunkSize?: number;
	};
	connectionTimeout?: number;
	socketTimeout?: number;
	maxSockets?: number;
	keepAlive?: boolean;
```

Code after:
import fs, { promises as fsProm } from 'node:fs';
import { Agent as HttpAgent } from 'node:http';
import { Agent as HttpsAgent } from 'node:https';
import os from 'node:os';
import { join } from 'node:path';
import stream, { promises as streamProm, type Readable } from 'node:stream';

export type DriverS3Config = {
	root?: string;
	key?: string;
	secret?: string;
	bucket: string;
	acl?: ObjectCannedACL;
	serverSideEncryption?: ServerSideEncryption;
	serverSideEncryptionKmsKeyId?: string;
	endpoint?: string;
	region?: string;
	forcePathStyle?: boolean;
	tus?: {
		chunkSize?: number;
	};
	connectionTimeout?: number;
	socketTimeout?: number;
	maxSockets?: number;
	keepAlive?: boolean;
};

export const kmsKeyIdCheck = [
	ServerSideEncryption.aws_kms,
	ServerSideEncryption.aws_kms_dsse,
] as ServerSideEncryption[];

export class DriverS3 implements TusDriver {
	private config: DriverS3Config;
	private readonly client: S3Client;
	private readonly root: string;

	// TUS specific members
	private partUploadSemaphore: Semaphore;
	private readonly preferredPartSize: number;
	public maxMultipartParts = 10_000 as const;
	public minPartSize = 5_242_880 as const; // 5MiB
	public maxUploadSize = 5_497_558_138_880 as const; // 5TiB

	constructor(config: DriverS3Config) {
		this.config = config;
		this.client = this.getClient();
		this.root = this.config.root ? normalizePath(this.config.root, { removeLeading: true }) : '';

		this.preferredPartSize = config.tus?.chunkSize ?? this.minPartSize;
		this.partUploadSemaphore = new Semaphore(60);