fix: improve environment validation in published docs (#5962)

This commit is contained in:
sahilkhan09k 2026-04-22 21:25:10 +05:30 committed by GitHub
parent eb801889ba
commit bc3dbdea42
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 68 additions and 41 deletions

View file

@ -462,6 +462,12 @@ export const USER_ENVIRONMENT_IS_NOT_GLOBAL =
export const USER_ENVIRONMENT_UPDATE_FAILED = export const USER_ENVIRONMENT_UPDATE_FAILED =
'user_environment/user_env_update_failed' as const; 'user_environment/user_env_update_failed' as const;
/**
* User environment not found for the user
* (UserEnvironmentsService)
*/
export const USER_ENVIRONMENT_NOT_FOUND = 'user_environment/not_found' as const;
/** /**
* User environment invalid environment name * User environment invalid environment name
* (UserEnvironmentsService) * (UserEnvironmentsService)
@ -977,8 +983,8 @@ export const PUBLISHED_DOCS_DELETION_FAILED = 'published_docs/deletion_failed';
* Published Docs invalid environment * Published Docs invalid environment
* (PublishedDocsService) * (PublishedDocsService)
*/ */
export const PUBLISHED_DOCS_INVALID_ENVIRONMENT = export const PUBLISHED_DOCS_FORBIDDEN_ENVIRONMENT_ACCESS =
'published_docs/invalid_environment'; 'published_docs/forbidden_environment_access';
/** /**
* Published Docs not found * Published Docs not found

View file

@ -4,10 +4,12 @@ import {
PUBLISHED_DOCS_CREATION_FAILED, PUBLISHED_DOCS_CREATION_FAILED,
PUBLISHED_DOCS_DELETION_FAILED, PUBLISHED_DOCS_DELETION_FAILED,
PUBLISHED_DOCS_INVALID_COLLECTION, PUBLISHED_DOCS_INVALID_COLLECTION,
PUBLISHED_DOCS_INVALID_ENVIRONMENT, PUBLISHED_DOCS_FORBIDDEN_ENVIRONMENT_ACCESS,
PUBLISHED_DOCS_NOT_FOUND, PUBLISHED_DOCS_NOT_FOUND,
PUBLISHED_DOCS_UPDATE_FAILED, PUBLISHED_DOCS_UPDATE_FAILED,
TEAM_ENVIRONMENT_NOT_FOUND,
TEAM_INVALID_ID, TEAM_INVALID_ID,
USER_ENVIRONMENT_NOT_FOUND,
} from 'src/errors'; } from 'src/errors';
import * as E from 'fp-ts/Either'; import * as E from 'fp-ts/Either';
import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaService } from 'src/prisma/prisma.service';
@ -1491,7 +1493,7 @@ describe('createPublishedDoc - environment support', () => {
userUid: user.uid, userUid: user.uid,
} as any); } as any);
mockPrisma.publishedDocs.findFirst.mockResolvedValueOnce(null); mockPrisma.publishedDocs.findFirst.mockResolvedValueOnce(null);
mockPrisma.userEnvironment.findFirst.mockResolvedValueOnce(envData as any); mockPrisma.userEnvironment.findUnique.mockResolvedValueOnce(envData as any);
mockPrisma.publishedDocs.create.mockResolvedValueOnce({ mockPrisma.publishedDocs.create.mockResolvedValueOnce({
...userPublishedDoc, ...userPublishedDoc,
environmentID: 'env_1', environmentID: 'env_1',
@ -1537,7 +1539,7 @@ describe('createPublishedDoc - environment support', () => {
teamID: 'team_1', teamID: 'team_1',
} as any); } as any);
mockPrisma.publishedDocs.findFirst.mockResolvedValueOnce(null); mockPrisma.publishedDocs.findFirst.mockResolvedValueOnce(null);
mockPrisma.teamEnvironment.findFirst.mockResolvedValueOnce(envData as any); mockPrisma.teamEnvironment.findUnique.mockResolvedValueOnce(envData as any);
mockPrisma.publishedDocs.create.mockResolvedValueOnce({ mockPrisma.publishedDocs.create.mockResolvedValueOnce({
...teamPublishedDoc, ...teamPublishedDoc,
environmentID: 'team_env_1', environmentID: 'team_env_1',
@ -1568,14 +1570,14 @@ describe('createPublishedDoc - environment support', () => {
userUid: user.uid, userUid: user.uid,
} as any); } as any);
mockPrisma.publishedDocs.findFirst.mockResolvedValueOnce(null); mockPrisma.publishedDocs.findFirst.mockResolvedValueOnce(null);
mockPrisma.userEnvironment.findFirst.mockResolvedValueOnce(null); mockPrisma.userEnvironment.findUnique.mockResolvedValueOnce(null);
const result = await publishedDocsService.createPublishedDoc( const result = await publishedDocsService.createPublishedDoc(
{ ...createArgs, environmentID: 'invalid_env' }, { ...createArgs, environmentID: 'invalid_env' },
user, user,
); );
expect(result).toEqualLeft(PUBLISHED_DOCS_INVALID_ENVIRONMENT); expect(result).toEqualLeft(USER_ENVIRONMENT_NOT_FOUND);
}); });
test('should return error when team environment ID is invalid', async () => { test('should return error when team environment ID is invalid', async () => {
@ -1593,14 +1595,14 @@ describe('createPublishedDoc - environment support', () => {
teamID: 'team_1', teamID: 'team_1',
} as any); } as any);
mockPrisma.publishedDocs.findFirst.mockResolvedValueOnce(null); mockPrisma.publishedDocs.findFirst.mockResolvedValueOnce(null);
mockPrisma.teamEnvironment.findFirst.mockResolvedValueOnce(null); mockPrisma.teamEnvironment.findUnique.mockResolvedValueOnce(null);
const result = await publishedDocsService.createPublishedDoc( const result = await publishedDocsService.createPublishedDoc(
teamArgs, teamArgs,
user, user,
); );
expect(result).toEqualLeft(PUBLISHED_DOCS_INVALID_ENVIRONMENT); expect(result).toEqualLeft(TEAM_ENVIRONMENT_NOT_FOUND);
}); });
test('should create published doc without environment when environmentID is not provided', async () => { test('should create published doc without environment when environmentID is not provided', async () => {
@ -1640,7 +1642,7 @@ describe('updatePublishedDoc - environment support', () => {
}; };
mockPrisma.publishedDocs.findUnique.mockResolvedValueOnce(userPublishedDoc); mockPrisma.publishedDocs.findUnique.mockResolvedValueOnce(userPublishedDoc);
mockPrisma.userEnvironment.findFirst.mockResolvedValueOnce(envData as any); mockPrisma.userEnvironment.findUnique.mockResolvedValueOnce(envData as any);
mockPrisma.publishedDocs.update.mockResolvedValueOnce({ mockPrisma.publishedDocs.update.mockResolvedValueOnce({
...userPublishedDoc, ...userPublishedDoc,
environmentID: 'env_2', environmentID: 'env_2',
@ -1701,7 +1703,7 @@ describe('updatePublishedDoc - environment support', () => {
test('should return error when updating with invalid environment ID', async () => { test('should return error when updating with invalid environment ID', async () => {
mockPrisma.publishedDocs.findUnique.mockResolvedValueOnce(userPublishedDoc); mockPrisma.publishedDocs.findUnique.mockResolvedValueOnce(userPublishedDoc);
mockPrisma.userEnvironment.findFirst.mockResolvedValueOnce(null); mockPrisma.userEnvironment.findUnique.mockResolvedValueOnce(null);
const result = await publishedDocsService.updatePublishedDoc( const result = await publishedDocsService.updatePublishedDoc(
userPublishedDoc.id, userPublishedDoc.id,
@ -1709,7 +1711,7 @@ describe('updatePublishedDoc - environment support', () => {
user, user,
); );
expect(result).toEqualLeft(PUBLISHED_DOCS_INVALID_ENVIRONMENT); expect(result).toEqualLeft(USER_ENVIRONMENT_NOT_FOUND);
}); });
test('should not change environment when environmentID is not provided in update args', async () => { test('should not change environment when environmentID is not provided in update args', async () => {
@ -1743,7 +1745,7 @@ describe('updatePublishedDoc - environment support', () => {
mockPrisma.publishedDocs.findUnique.mockResolvedValueOnce(teamPublishedDoc); mockPrisma.publishedDocs.findUnique.mockResolvedValueOnce(teamPublishedDoc);
mockPrisma.team.findFirst.mockResolvedValueOnce({ id: 'team_1' } as any); mockPrisma.team.findFirst.mockResolvedValueOnce({ id: 'team_1' } as any);
mockPrisma.teamEnvironment.findFirst.mockResolvedValueOnce(envData as any); mockPrisma.teamEnvironment.findUnique.mockResolvedValueOnce(envData as any);
mockPrisma.publishedDocs.update.mockResolvedValueOnce({ mockPrisma.publishedDocs.update.mockResolvedValueOnce({
...teamPublishedDoc, ...teamPublishedDoc,
environmentID: 'team_env_1', environmentID: 'team_env_1',
@ -1796,7 +1798,7 @@ describe('getPublishedDocBySlugPublic - environment support', () => {
mockUserCollectionService.exportUserCollectionToJSONObject.mockResolvedValueOnce( mockUserCollectionService.exportUserCollectionToJSONObject.mockResolvedValueOnce(
E.right(collectionData as any), E.right(collectionData as any),
); );
mockPrisma.userEnvironment.findFirst.mockResolvedValueOnce(envData as any); mockPrisma.userEnvironment.findUnique.mockResolvedValueOnce(envData as any);
const result = await publishedDocsService.getPublishedDocBySlugPublic( const result = await publishedDocsService.getPublishedDocBySlugPublic(
'slug-collection-1', 'slug-collection-1',
@ -1894,14 +1896,14 @@ describe('getPublishedDocBySlugPublic - environment support', () => {
E.right(collectionData as any), E.right(collectionData as any),
); );
// Environment not found — fetchEnvironment returns Left // Environment not found — fetchEnvironment returns Left
mockPrisma.userEnvironment.findFirst.mockResolvedValueOnce(null); mockPrisma.userEnvironment.findUnique.mockResolvedValueOnce(null);
const result = await publishedDocsService.getPublishedDocBySlugPublic( const result = await publishedDocsService.getPublishedDocBySlugPublic(
'slug-collection-1', 'slug-collection-1',
'1.0.0', '1.0.0',
); );
expect(result).toEqualLeft(PUBLISHED_DOCS_INVALID_ENVIRONMENT); expect(result).toEqualLeft(USER_ENVIRONMENT_NOT_FOUND);
}); });
test('should return null environment fields when no environment is associated', async () => { test('should return null environment fields when no environment is associated', async () => {

View file

@ -13,12 +13,14 @@ import {
PUBLISHED_DOCS_CREATION_FAILED, PUBLISHED_DOCS_CREATION_FAILED,
PUBLISHED_DOCS_DELETION_FAILED, PUBLISHED_DOCS_DELETION_FAILED,
PUBLISHED_DOCS_INVALID_COLLECTION, PUBLISHED_DOCS_INVALID_COLLECTION,
PUBLISHED_DOCS_INVALID_ENVIRONMENT, PUBLISHED_DOCS_FORBIDDEN_ENVIRONMENT_ACCESS,
PUBLISHED_DOCS_NOT_FOUND, PUBLISHED_DOCS_NOT_FOUND,
PUBLISHED_DOCS_UPDATE_FAILED, PUBLISHED_DOCS_UPDATE_FAILED,
TEAM_ENVIRONMENT_NOT_FOUND,
TEAM_INVALID_COLL_ID, TEAM_INVALID_COLL_ID,
TEAM_INVALID_ID, TEAM_INVALID_ID,
USER_COLL_NOT_FOUND, USER_COLL_NOT_FOUND,
USER_ENVIRONMENT_NOT_FOUND,
} from 'src/errors'; } from 'src/errors';
import * as E from 'fp-ts/Either'; import * as E from 'fp-ts/Either';
import { PublishedDocs, PublishedDocsVersion } from './published-docs.model'; import { PublishedDocs, PublishedDocsVersion } from './published-docs.model';
@ -100,22 +102,43 @@ export class PublishedDocsService {
environmentID: string, environmentID: string,
workspaceType: WorkspaceType, workspaceType: WorkspaceType,
workspaceID: string, workspaceID: string,
): Promise<E.Either<string, { name: string; variables: JsonValue } | null>> { ): Promise<E.Either<string, { name: string; variables: JsonValue }>> {
// TEAM workspace environment
if (workspaceType === WorkspaceType.TEAM) { if (workspaceType === WorkspaceType.TEAM) {
const env = await this.prisma.teamEnvironment.findFirst({ const env = await this.prisma.teamEnvironment.findUnique({
where: { id: environmentID, teamID: workspaceID }, where: { id: environmentID },
}); });
if (!env) return E.left(PUBLISHED_DOCS_INVALID_ENVIRONMENT); if (!env) return E.left(TEAM_ENVIRONMENT_NOT_FOUND);
return E.right({ name: env.name, variables: env.variables });
} else if (workspaceType === WorkspaceType.USER) { // Validate environment exists and belongs to the correct team
const env = await this.prisma.userEnvironment.findFirst({ if (env.teamID !== workspaceID)
where: { id: environmentID, userUid: workspaceID }, return E.left(PUBLISHED_DOCS_FORBIDDEN_ENVIRONMENT_ACCESS);
return E.right({
name: env.name,
variables: env.variables,
}); });
if (!env) return E.left(PUBLISHED_DOCS_INVALID_ENVIRONMENT);
return E.right({ name: env.name ?? '', variables: env.variables });
} }
return E.left(PUBLISHED_DOCS_INVALID_ENVIRONMENT); // USER workspace environment
if (workspaceType === WorkspaceType.USER) {
const env = await this.prisma.userEnvironment.findUnique({
where: { id: environmentID },
});
if (!env) return E.left(USER_ENVIRONMENT_NOT_FOUND);
// Validate environment exists and belongs to the correct user
if (env.userUid !== workspaceID) {
return E.left(PUBLISHED_DOCS_FORBIDDEN_ENVIRONMENT_ACCESS);
}
return E.right({
name: env.name ?? '',
variables: env.variables,
});
}
return E.left(PUBLISHED_DOCS_FORBIDDEN_ENVIRONMENT_ACCESS);
} }
/** /**
@ -368,11 +391,9 @@ export class PublishedDocsService {
); );
if (E.isLeft(envResult)) return E.left(envResult.left); if (E.isLeft(envResult)) return E.left(envResult.left);
if (E.isRight(envResult) && envResult.right) {
environmentName = envResult.right.name; environmentName = envResult.right.name;
environmentVariables = envResult.right.variables; environmentVariables = envResult.right.variables;
} }
}
docToReturn = { docToReturn = {
...publishedDocs, ...publishedDocs,
@ -577,11 +598,10 @@ export class PublishedDocsService {
workspaceID, workspaceID,
); );
if (E.isLeft(envResult)) return E.left(envResult.left); if (E.isLeft(envResult)) return E.left(envResult.left);
if (envResult.right) {
environmentName = envResult.right.name; environmentName = envResult.right.name;
environmentVariables = envResult.right.variables; environmentVariables = envResult.right.variables;
} }
}
// Attempt to create the published document // Attempt to create the published document
const newPublishedDoc = await this.prisma.publishedDocs.create({ const newPublishedDoc = await this.prisma.publishedDocs.create({
@ -697,13 +717,12 @@ export class PublishedDocsService {
publishedDocs.workspaceID, publishedDocs.workspaceID,
); );
if (E.isLeft(envResult)) return E.left(envResult.left); if (E.isLeft(envResult)) return E.left(envResult.left);
if (envResult.right) {
environmentID = args.environmentID; environmentID = args.environmentID;
environmentName = envResult.right.name; environmentName = envResult.right.name;
environmentVariables = envResult.right.variables; environmentVariables = envResult.right.variables;
} }
} }
}
// Update published document // Update published document
const updatedPublishedDoc = await this.prisma.publishedDocs.update({ const updatedPublishedDoc = await this.prisma.publishedDocs.update({