Skip to content

Commit 649f80f

Browse files
fix: Code scanning alerts #90 (#1024)
2 parents 7c195bf + 2e5e273 commit 649f80f

File tree

3 files changed

+65
-20
lines changed

3 files changed

+65
-20
lines changed

apps/api/src/app/import-jobs/usecase/create-userjob/create-userjob.usecase.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { Injectable } from '@nestjs/common';
1+
import { BadRequestException, Injectable } from '@nestjs/common';
22
import { UserJobEntity, UserJobRepository } from '@impler/dal';
33
import { CreateUserJobCommand } from './create-userjob.command';
44
import { APIMessages } from '@shared/constants';
5-
import { BadRequestException } from '@nestjs/common';
65
import { RSSXMLService } from '@impler/services';
7-
import { isValidXMLMimeType } from '@shared/helpers/common.helper';
6+
import { getMimeType, isValidXMLMimeType } from '@shared/helpers/common.helper';
87
import { WebSocketService } from '@shared/services';
98

109
@Injectable()
@@ -24,10 +23,11 @@ export class CreateUserJob {
2423
}: CreateUserJobCommand): Promise<UserJobEntity> {
2524
const rssService = new RSSXMLService(url);
2625

27-
const mimeType = await rssService.getMimeType(url);
26+
try {
27+
const mimeType = await getMimeType(url);
28+
console.log('mime type is >>', mimeType);
2829

29-
if (isValidXMLMimeType(mimeType)) {
30-
try {
30+
if (isValidXMLMimeType(mimeType)) {
3131
const abortController = new AbortController();
3232

3333
this.webSocketService.registerSessionAbort(webSocketSessionId, abortController);
@@ -54,9 +54,11 @@ export class CreateUserJob {
5454
_templateId: _templateId,
5555
externalUserId: externalUserId || (formattedExtra as unknown as Record<string, any>)?.externalUserId,
5656
});
57-
} catch (error) {}
58-
} else {
59-
throw new BadRequestException(APIMessages.INVALID_RSS_URL);
57+
} else {
58+
throw new BadRequestException(APIMessages.INVALID_RSS_URL);
59+
}
60+
} catch (error) {
61+
throw new BadRequestException(error);
6062
}
6163
}
6264
}

apps/api/src/app/shared/helpers/common.helper.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as Sentry from '@sentry/node';
2+
import axios from 'axios';
23
import { BadRequestException } from '@nestjs/common';
34
import { APIMessages } from '../constants';
45
import { PaginationResult, Defaults, FileMimeTypesEnum } from '@impler/shared';
@@ -95,3 +96,56 @@ export function isValidXMLMimeType(mimeType: string): boolean {
9596

9697
return false;
9798
}
99+
100+
export const getMimeType = async (url: string): Promise<string | null> => {
101+
try {
102+
if (!isUrlSafe(url)) {
103+
throw new BadRequestException('Invalid URL');
104+
}
105+
106+
const response = await axios.head(url, {
107+
timeout: 10000,
108+
maxRedirects: 3,
109+
});
110+
111+
const mimeType = response.headers['content-type'] || null;
112+
113+
return mimeType?.split(';')[0] || null;
114+
} catch (error) {
115+
throw error;
116+
}
117+
};
118+
119+
export function isUrlSafe(url: string): boolean {
120+
try {
121+
const parsedUrl = new URL(url);
122+
123+
// Only allow HTTP and HTTPS
124+
if (!['http:', 'https:'].includes(parsedUrl.protocol)) {
125+
return false;
126+
}
127+
128+
// Block localhost and private IPs (except in development)
129+
const hostname = parsedUrl.hostname.toLowerCase();
130+
const isDevelopment = process.env.NODE_ENV === 'dev';
131+
132+
if (!isDevelopment && ['localhost', '127.0.0.1', '::1'].includes(hostname)) {
133+
return false;
134+
}
135+
136+
if (
137+
hostname.startsWith('10.') ||
138+
hostname.startsWith('192.168.') ||
139+
hostname.startsWith('169.254.') ||
140+
/^172\.(1[6-9]|2[0-9]|3[01])\./.test(hostname)
141+
) {
142+
return false;
143+
}
144+
145+
return true;
146+
} catch (error) {
147+
return false;
148+
}
149+
}
150+
151+
export default { getMimeType };

libs/services/src/rss-xml/rssxml.service.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -584,15 +584,4 @@ export class RSSXMLService {
584584
throw error;
585585
}
586586
}
587-
588-
async getMimeType(url: string): Promise<string | null> {
589-
try {
590-
const response = await axios.head(url);
591-
const mimeType = response.headers['content-type'] || null;
592-
593-
return mimeType?.split(';')[0] || null;
594-
} catch (error) {
595-
return null;
596-
}
597-
}
598587
}

0 commit comments

Comments
 (0)