67074a7581
## Summary - **File storage (LocalDriver):** Add realpath resolution and symlink rejection to `writeFile`, `downloadFile`, and `downloadFolder` — brings them in line with the existing `readFile` protections. Includes unit tests. - **JWT:** Pin signing/verification to HS256 explicitly. - **Auth:** Revoke active refresh tokens when a user changes their password. - **Logic functions:** Validate `handlerName` as a safe JS identifier at both DTO and runtime level, preventing injection into the generated runner script. - **User entity:** Remove `passwordHash` from the GraphQL schema (`@Field` decorator removed, column stays). - **Query params:** Use `crypto.randomBytes` instead of `Math.random` for SQL parameter name generation. - **Exception filter:** Mirror the request `Origin` header instead of sending `Access-Control-Allow-Origin: *`. ## Test plan - [x] `local.driver.spec.ts` — writeFile rejects symlinks, downloadFile rejects paths outside storage - [ ] Verify JWT auth flow still works (login, token refresh) - [ ] Verify password change invalidates existing sessions - [ ] Verify logic function creation with valid/invalid handler names - [ ] Verify file upload/download in dev environment Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
104 lines
2.3 KiB
TypeScript
104 lines
2.3 KiB
TypeScript
import { Field, InputType } from '@nestjs/graphql';
|
|
|
|
import {
|
|
IsBoolean,
|
|
IsNotEmpty,
|
|
IsNumber,
|
|
IsObject,
|
|
IsOptional,
|
|
IsString,
|
|
IsUUID,
|
|
Matches,
|
|
Max,
|
|
Min,
|
|
} from 'class-validator';
|
|
import graphqlTypeJson from 'graphql-type-json';
|
|
import {
|
|
CronTriggerSettings,
|
|
DatabaseEventTriggerSettings,
|
|
HttpRouteTriggerSettings,
|
|
} from 'twenty-shared/application';
|
|
|
|
import type { InputJsonSchema } from 'twenty-shared/logic-function';
|
|
|
|
import type { JsonbProperty } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/jsonb-property.type';
|
|
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
|
|
|
@InputType()
|
|
export class CreateLogicFunction {
|
|
@IsUUID()
|
|
@IsOptional()
|
|
@Field(() => UUIDScalarType, { nullable: true })
|
|
id?: string;
|
|
|
|
@IsUUID()
|
|
@IsOptional()
|
|
@Field(() => UUIDScalarType, { nullable: true })
|
|
universalIdentifier?: string;
|
|
|
|
@IsString()
|
|
@IsNotEmpty()
|
|
@Field()
|
|
name: string;
|
|
|
|
@IsString()
|
|
@IsOptional()
|
|
@Field({ nullable: true })
|
|
description?: string;
|
|
|
|
@IsNumber()
|
|
@Field({ nullable: true })
|
|
@Min(1)
|
|
@Max(900)
|
|
@IsOptional()
|
|
timeoutSeconds?: number;
|
|
|
|
@Field(() => graphqlTypeJson, { nullable: false })
|
|
@IsObject()
|
|
toolInputSchema: InputJsonSchema;
|
|
|
|
@IsBoolean()
|
|
@Field({ nullable: true })
|
|
@IsOptional()
|
|
isTool?: boolean;
|
|
|
|
@IsBoolean()
|
|
@Field({ nullable: false })
|
|
isBuildUpToDate: boolean;
|
|
|
|
@IsString()
|
|
@Field({ nullable: true })
|
|
@IsOptional()
|
|
checksum?: string;
|
|
|
|
@IsString()
|
|
@Matches(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/, {
|
|
message: 'handlerName must be a valid JavaScript identifier',
|
|
})
|
|
@Field({ nullable: false })
|
|
handlerName: string;
|
|
|
|
@IsString()
|
|
@Field({ nullable: false })
|
|
sourceHandlerPath: string;
|
|
|
|
@IsString()
|
|
@Field({ nullable: false })
|
|
builtHandlerPath: string;
|
|
|
|
@IsObject()
|
|
@Field(() => graphqlTypeJson, { nullable: true })
|
|
@IsOptional()
|
|
cronTriggerSettings?: JsonbProperty<CronTriggerSettings>;
|
|
|
|
@IsObject()
|
|
@Field(() => graphqlTypeJson, { nullable: true })
|
|
@IsOptional()
|
|
databaseEventTriggerSettings?: JsonbProperty<DatabaseEventTriggerSettings>;
|
|
|
|
@IsObject()
|
|
@Field(() => graphqlTypeJson, { nullable: true })
|
|
@IsOptional()
|
|
httpRouteTriggerSettings?: JsonbProperty<HttpRouteTriggerSettings>;
|
|
}
|