Skip to content

Commit 23fbe81

Browse files
committed
chore: force HTTPS protocol for protocol-relative URLs
1 parent a5f3ae3 commit 23fbe81

File tree

6 files changed

+61
-17
lines changed

6 files changed

+61
-17
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: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,21 @@ describe('isCdnUrl', () => {
1414
expect(
1515
isCdnUrl('http://custom.domain.com', 'custom.domain.com')
1616
).toBeTruthy();
17-
expect(isCdnUrl('//ucarecdn.com/', 'ucarecdn.com')).toBeTruthy();
1817
});
1918

2019
it("should return false in cases when provided URL doesn't matches provided domain", () => {
2120
expect(
2221
isCdnUrl('https://ucarecdn.com/:uuid/:filename', 'other.com')
2322
).toBeFalsy();
2423
expect(isCdnUrl('http://custom.domain.com', 'ucarecdn.com')).toBeFalsy();
25-
expect(isCdnUrl('//custom.domain.com/', 'ucarecdn.com')).toBeFalsy();
2624
});
2725

2826
it('should throw a error if invalid URL provided', () => {
2927
expect(() => isCdnUrl('ucarecdn', 'ucarecdn.com')).toThrowError(
3028
/Invalid URL/
3129
);
30+
expect(() => isCdnUrl('//ucarecdn.com', 'ucarecdn.com')).toThrowError(
31+
/Invalid URL/
32+
);
3233
});
3334
});

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/utils/helpers.ts

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

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+
}
123+
124+
export function ensureUrlProtocol(url: string): string {
125+
if (isProtocolRelativeUrl(url)) {
126+
return 'https:' + url;
127+
}
128+
return url;
129+
}
130+
116131
export function isCdnUrl(url: string, cdnDomain: string): boolean {
117-
url = url.trim();
118-
if (url.startsWith('//')) {
119-
url = location.protocol + url;
120-
} else if (url.startsWith('/')) {
132+
if (isRelativeUrl(url)) {
121133
return false;
122134
}
123-
124135
return new URL(url).hostname === cdnDomain.trim();
125136
}
126137

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)