feat(api): add contextVar regex validation

This commit is contained in:
yassinedorbozgithub 2025-04-13 10:07:30 +01:00
parent dadb263176
commit e0fdd71693
4 changed files with 44 additions and 14 deletions

View File

@ -1,12 +1,12 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
import { ApiProperty, PartialType } from '@nestjs/swagger';
import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
@ -26,6 +26,14 @@ export class ContextVarCreateDto {
@IsOptional()
@IsBoolean()
permanent?: boolean;
@ApiPropertyOptional({
description: 'Context var pattern',
type: String,
})
@IsOptional()
@IsString()
pattern?: string;
}
export class ContextVarUpdateDto extends PartialType(ContextVarCreateDto) {}

View File

@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@ -10,8 +10,11 @@ import { ModelDefinition, Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { BaseSchema } from '@/utils/generics/base-schema';
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
import { buildZodSchemaValidator } from '@/utils/helpers/zod-validation';
import { THydratedDocument } from '@/utils/types/filter.types';
import { stringRegexPatternSchema } from './types/pattern';
@Schema({ timestamps: true })
export class ContextVar extends BaseSchema {
@Prop({
@ -39,6 +42,13 @@ export class ContextVar extends BaseSchema {
default: false,
})
permanent: boolean;
@Prop({
type: String,
validate: buildZodSchemaValidator(stringRegexPatternSchema),
default: '/.+/',
})
pattern?: string;
}
export const ContextVarModel: ModelDefinition = LifecycleHookManager.attach({

View File

@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@ -34,7 +34,7 @@ export class ContextVarService extends BaseService<
*/
async getContextVarsByBlock(
block: Block | BlockFull,
): Promise<Record<string, ContextVar>> {
): Promise<Record<string, ContextVar | undefined>> {
const vars = await this.find({
name: { $in: block.capture_vars.map(({ context_var }) => context_var) },
});

View File

@ -6,7 +6,7 @@
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
import { Injectable, Logger } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import EventWrapper from '@/channel/lib/EventWrapper';
import { BaseService } from '@/utils/generics/base-service';
@ -114,16 +114,28 @@ export class ConversationService extends BaseService<
contextValue =
typeof contextValue === 'string' ? contextValue.trim() : contextValue;
if (
profile.context?.vars &&
contextVars[capture.context_var]?.permanent
) {
Logger.debug(
`Adding context var to subscriber: ${capture.context_var} = ${contextValue}`,
const context_var = contextVars[capture.context_var];
const { permanent, pattern = '' } = context_var || {};
const isValidContextValue = new RegExp(pattern.slice(1, -1)).test(
`${contextValue}`,
);
if (isValidContextValue) {
if (profile.context?.vars && permanent) {
this.logger.debug(
`Adding context var to subscriber: ${capture.context_var} = ${contextValue}`,
);
profile.context.vars[capture.context_var] = contextValue;
}
convo.context!.vars[capture.context_var] = contextValue;
} else {
this.logger.warn(
`ContextVar[${capture.context_var}] value does not match the pattern!`,
);
this.logger.debug(`value:"${contextValue}" pattern:"${pattern}"`);
this.logger.warn(
`ContextVar[${capture.context_var}] update skipped!`,
);
profile.context.vars[capture.context_var] = contextValue;
}
convo.context!.vars[capture.context_var] = contextValue;
});
}