Skip to content

Commit b941268

Browse files
authored
Merge pull request #27 from uploadcare/fix/code-ql
Fix/code ql
2 parents 3cb98c0 + 23fbe81 commit b941268

File tree

7 files changed

+105
-27
lines changed

7 files changed

+105
-27
lines changed

example/pages/index.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const Home: NextPage = () => (
1414
<div className={styles.card}>
1515
<h1>
1616
Uploadcare custom loader for Image Component{' '}
17-
<a href="https://github.com/uploadcare/nextjs-loader">
17+
<a href="//github.com/uploadcare/nextjs-loader">
1818
@uploadcare/nextjs-loader
1919
</a>
2020
</h1>
@@ -29,7 +29,7 @@ const Home: NextPage = () => (
2929
<Image
3030
loader={uploadcareLoader}
3131
alt="Vercel logo"
32-
src="https://ucarecdn.com/a6f8abc8-f92e-460a-b7a1-c5cd70a18cdb/vercel.png"
32+
src="//ucarecdn.com/a6f8abc8-f92e-460a-b7a1-c5cd70a18cdb/vercel.png"
3333
width={1000}
3434
height={1000}
3535
/>
@@ -40,7 +40,7 @@ const Home: NextPage = () => (
4040
</p>
4141
<UploadcareImage
4242
alt="Vercel logo"
43-
src="https://ucarecdn.com/a6f8abc8-f92e-460a-b7a1-c5cd70a18cdb/vercel.png"
43+
src="//ucarecdn.com/a6f8abc8-f92e-460a-b7a1-c5cd70a18cdb/vercel.png"
4444
width={500}
4545
height={500}
4646
/>
@@ -53,7 +53,7 @@ const Home: NextPage = () => (
5353
</p>
5454
<UploadcareImage
5555
alt="Vercel logo"
56-
src="https://ucarecdn.com/c768f1c2-891a-4f54-8e1e-7242df218b51/pinewatt2Hzmz15wGikunsplash.jpg"
56+
src="//ucarecdn.com/c768f1c2-891a-4f54-8e1e-7242df218b51/pinewatt2Hzmz15wGikunsplash.jpg"
5757
width={500}
5858
height={500}
5959
placeholder="blur"
@@ -66,7 +66,7 @@ const Home: NextPage = () => (
6666
<p>It will be proxied through Media Proxy.</p>
6767
<Image
6868
alt="Next.js logo"
69-
src="https://assets.vercel.com/image/upload/v1538361091/repositories/next-js/next-js.png"
69+
src="//assets.vercel.com/image/upload/v1538361091/repositories/next-js/next-js.png"
7070
width={1200}
7171
height={400}
7272
loader={uploadcareLoader}
@@ -75,14 +75,14 @@ const Home: NextPage = () => (
7575
<p>SVGs and GIFs will be used without transformations</p>
7676
<Image
7777
alt="Next.js logo"
78-
src="https://ucarecdn.com/375bba4b-35db-4cb8-8fc7-7540625f2181/next.svg"
78+
src="//ucarecdn.com/375bba4b-35db-4cb8-8fc7-7540625f2181/next.svg"
7979
width={64}
8080
height={64}
8181
loader={uploadcareLoader}
8282
/>
8383
<Image
8484
alt="Vercel logo"
85-
src="https://ucarecdn.com/0f23a269-13eb-4fc9-b378-86f224380d26/vercel.gif"
85+
src="//ucarecdn.com/0f23a269-13eb-4fc9-b378-86f224380d26/vercel.gif"
8686
width={64}
8787
height={64}
8888
loader={uploadcareLoader}
@@ -101,7 +101,7 @@ const Home: NextPage = () => (
101101
/>
102102
<hr className={styles.hr} />
103103
Checkout the project documentation on Github{' '}
104-
<a href="https://github.com/uploadcare/nextjs-loader">
104+
<a href="//github.com/uploadcare/nextjs-loader">
105105
@uploadcare/nextjs-loader
106106
</a>
107107
.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { ensureUrlProtocol } from '../utils/helpers';
2+
3+
describe('ensureUrlProtocol', () => {
4+
it('should force https protocol for protocol relative URLs', () => {
5+
expect(ensureUrlProtocol('//domain.com')).toBe('https://domain.com');
6+
});
7+
8+
it('should not modify relative URLs', () => {
9+
expect(ensureUrlProtocol('/path/')).toBe('/path/');
10+
});
11+
12+
it('should not modify absolute URLs', () => {
13+
expect(ensureUrlProtocol('https://domain.com')).toBe('https://domain.com');
14+
expect(ensureUrlProtocol('http://domain.com')).toBe('http://domain.com');
15+
});
16+
});

src/__tests__/is-cdn-url.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* @jest-environment jsdom
3+
*/
4+
import { isCdnUrl } from '../utils/helpers';
5+
6+
describe('isCdnUrl', () => {
7+
it('should return true in cases when provided URL matches provided domain', () => {
8+
expect(
9+
isCdnUrl('https://ucarecdn.com/:uuid/:filename', 'ucarecdn.com')
10+
).toBeTruthy();
11+
expect(isCdnUrl('http://ucarecdn.com/', 'ucarecdn.com')).toBeTruthy();
12+
expect(isCdnUrl('http://ucarecdn.com', 'ucarecdn.com')).toBeTruthy();
13+
expect(isCdnUrl('http://ucarecdn.com:8080', 'ucarecdn.com')).toBeTruthy();
14+
expect(
15+
isCdnUrl('http://custom.domain.com', 'custom.domain.com')
16+
).toBeTruthy();
17+
});
18+
19+
it("should return false in cases when provided URL doesn't matches provided domain", () => {
20+
expect(
21+
isCdnUrl('https://ucarecdn.com/:uuid/:filename', 'other.com')
22+
).toBeFalsy();
23+
expect(isCdnUrl('http://custom.domain.com', 'ucarecdn.com')).toBeFalsy();
24+
});
25+
26+
it('should throw a error if invalid URL provided', () => {
27+
expect(() => isCdnUrl('ucarecdn', 'ucarecdn.com')).toThrowError(
28+
/Invalid URL/
29+
);
30+
expect(() => isCdnUrl('//ucarecdn.com', 'ucarecdn.com')).toThrowError(
31+
/Invalid URL/
32+
);
33+
});
34+
});

src/__tests__/is-relative-url.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { isRelativeUrl } from '../utils/helpers';
2+
3+
describe('isRelativeUrl', () => {
4+
it('should return true when relative URL provided', () => {
5+
expect(isRelativeUrl('/relative/path/')).toBeTruthy();
6+
expect(isRelativeUrl('/')).toBeTruthy();
7+
});
8+
9+
it('should return false when non-relative URL provided', () => {
10+
expect(isRelativeUrl('http://domain.com')).toBeFalsy();
11+
expect(isRelativeUrl('//domain.com')).toBeFalsy();
12+
});
13+
});

src/__tests__/uploadcare-loader.spec.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ describe('uploadcareLoader', () => {
6767

6868
const t = () => {
6969
uploadcareLoader({
70-
src: '',
70+
src: 'https://example.com',
7171
width: 0,
7272
quality: 80
7373
});
@@ -95,7 +95,7 @@ describe('uploadcareLoader', () => {
9595
});
9696

9797
test('The loader parses user paramters properly', () => {
98-
const src = 'https:/example.com/image.jpg';
98+
const src = 'https://example.com/image.jpg';
9999

100100
addEnvVar('NEXT_PUBLIC_UPLOADCARE_PUBLIC_KEY', 'test-public-key');
101101

@@ -108,7 +108,7 @@ describe('uploadcareLoader', () => {
108108
});
109109

110110
expect(result).toBe(
111-
'https://test-public-key.ucr.io/-/format/auto/-/stretch/off/-/progressive/yes/-/resize/500x/-/quality/normal/https:/example.com/image.jpg'
111+
'https://test-public-key.ucr.io/-/format/auto/-/stretch/off/-/progressive/yes/-/resize/500x/-/quality/normal/https://example.com/image.jpg'
112112
);
113113

114114
// Override default params, including resize and quality.
@@ -125,7 +125,7 @@ describe('uploadcareLoader', () => {
125125
});
126126

127127
expect(result).toBe(
128-
'https://test-public-key.ucr.io/-/format/jpg/-/stretch/on/-/progressive/no/-/resize/1x/-/quality/smart_retina/https:/example.com/image.jpg'
128+
'https://test-public-key.ucr.io/-/format/jpg/-/stretch/on/-/progressive/no/-/resize/1x/-/quality/smart_retina/https://example.com/image.jpg'
129129
);
130130

131131
// Add a new parameter (no defaults).
@@ -142,15 +142,15 @@ describe('uploadcareLoader', () => {
142142
});
143143

144144
expect(result).toBe(
145-
'https://test-public-key.ucr.io/-/format/auto/-/stretch/off/-/progressive/yes/-/resize/500x/-/quality/normal/-/new/parameter/https:/example.com/image.jpg'
145+
'https://test-public-key.ucr.io/-/format/auto/-/stretch/off/-/progressive/yes/-/resize/500x/-/quality/normal/-/new/parameter/https://example.com/image.jpg'
146146
);
147147

148148
removeEnvVar('NEXT_PUBLIC_UPLOADCARE_PUBLIC_KEY');
149149
removeEnvVar('NEXT_PUBLIC_UPLOADCARE_TRANSFORMATION_PARAMETERS');
150150
});
151151

152152
test("The loader doesn't process SVG and GIF (absolute url)", () => {
153-
const src = 'https:/example.com/image.svg';
153+
const src = 'https://example.com/image.svg';
154154

155155
addEnvVar('NEXT_PUBLIC_UPLOADCARE_PUBLIC_KEY', 'test-public-key');
156156

@@ -242,7 +242,7 @@ describe('uploadcareLoader', () => {
242242

243243
// Not a jpg image. Should be max 3000 width.
244244

245-
let src = 'https:/example.com/image.png';
245+
let src = 'https://example.com/image.png';
246246

247247
addEnvVar(
248248
'NEXT_PUBLIC_UPLOADCARE_TRANSFORMATION_PARAMETERS',
@@ -256,7 +256,7 @@ describe('uploadcareLoader', () => {
256256
});
257257

258258
expect(result).toBe(
259-
'https://test-public-key.ucr.io/-/format/auto/-/stretch/off/-/progressive/yes/-/resize/3000x/-/quality/normal/https:/example.com/image.png'
259+
'https://test-public-key.ucr.io/-/format/auto/-/stretch/off/-/progressive/yes/-/resize/3000x/-/quality/normal/https://example.com/image.png'
260260
);
261261

262262
// Jpg image by format. Should be max 5000 width.
@@ -273,13 +273,13 @@ describe('uploadcareLoader', () => {
273273
});
274274

275275
expect(result).toBe(
276-
'https://test-public-key.ucr.io/-/format/jpeg/-/stretch/off/-/progressive/yes/-/resize/5000x/-/quality/normal/https:/example.com/image.png'
276+
'https://test-public-key.ucr.io/-/format/jpeg/-/stretch/off/-/progressive/yes/-/resize/5000x/-/quality/normal/https://example.com/image.png'
277277
);
278278

279279
// // Jpg image by extension with auto format.
280280
// // Should be max 5000 width with /format/jpeg/ forced.
281281

282-
src = 'https:/example.com/image.jpg';
282+
src = 'https://example.com/image.jpg';
283283

284284
addEnvVar(
285285
'NEXT_PUBLIC_UPLOADCARE_TRANSFORMATION_PARAMETERS',
@@ -293,7 +293,7 @@ describe('uploadcareLoader', () => {
293293
});
294294

295295
expect(result).toBe(
296-
'https://test-public-key.ucr.io/-/format/jpeg/-/stretch/off/-/progressive/yes/-/resize/5000x/-/quality/normal/https:/example.com/image.jpg'
296+
'https://test-public-key.ucr.io/-/format/jpeg/-/stretch/off/-/progressive/yes/-/resize/5000x/-/quality/normal/https://example.com/image.jpg'
297297
);
298298

299299
removeEnvVar('NEXT_PUBLIC_UPLOADCARE_PUBLIC_KEY');

src/utils/helpers.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,26 @@ export function generateCustomProxyEndpoint(customProxyDomain: string): string {
113113
return `https://${customProxyDomain}`;
114114
}
115115

116-
export function isCdnUrl(url: string, cdnDomain: string): boolean {
117-
//eslint-disable-next-line
118-
const escapedCdnDomain = cdnDomain.replace('.', '.');
116+
function isProtocolRelativeUrl(url: string): boolean {
117+
return url.startsWith('//');
118+
}
119+
120+
export function isRelativeUrl(url: string): boolean {
121+
return url.startsWith('/') && !isProtocolRelativeUrl(url);
122+
}
119123

120-
//eslint-disable-next-line
121-
const regexp = new RegExp(`^https?:\/\/${escapedCdnDomain}`);
124+
export function ensureUrlProtocol(url: string): string {
125+
if (isProtocolRelativeUrl(url)) {
126+
return 'https:' + url;
127+
}
128+
return url;
129+
}
122130

123-
return regexp.test(url);
131+
export function isCdnUrl(url: string, cdnDomain: string): boolean {
132+
if (isRelativeUrl(url)) {
133+
return false;
134+
}
135+
return new URL(url).hostname === cdnDomain.trim();
124136
}
125137

126138
export function isProduction(): boolean {

src/utils/loader.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ import {
1818
isProduction,
1919
mergeParams,
2020
parseUserParamsString,
21-
trimTrailingSlash
21+
trimTrailingSlash,
22+
isRelativeUrl,
23+
ensureUrlProtocol
2224
} from './helpers';
2325

2426
export function uploadcareLoader({
@@ -43,10 +45,11 @@ export function uploadcareLoader({
4345
process.env.NEXT_PUBLIC_UPLOADCARE_APP_BASE_URL || ''
4446
);
4547

48+
src = ensureUrlProtocol(src);
4649
const proxy = trimTrailingSlash(proxyEndpoint);
4750
const isProductionMode = isProduction();
4851
const isImageOnCdn = isCdnUrl(src, cdnDomain);
49-
const isImageRelative = src.startsWith('/');
52+
const isImageRelative = isRelativeUrl(src);
5053

5154
// Development mode; not on CDN.
5255
if (!isProductionMode && !isImageOnCdn) {

0 commit comments

Comments
 (0)