Skip to content

Commit 7e23f76

Browse files
fix: improved calculation of the signature in url
1 parent 1b5be87 commit 7e23f76

File tree

5 files changed

+144
-73
lines changed

5 files changed

+144
-73
lines changed

lib-es5/utils/index.js

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ var encodeDoubleArray = require('./encoding/encodeDoubleArray');
4949

5050
var config = require("../config");
5151
var generate_token = require("../auth_token");
52-
var utf8_encode = require('./utf8_encode');
5352
var crc32 = require('./crc32');
5453
var ensurePresenceOf = require('./ensurePresenceOf');
5554
var ensureOption = require('./ensureOption').defaults(config());
@@ -425,7 +424,8 @@ function build_upload_params(options) {
425424
quality_override: options.quality_override,
426425
accessibility_analysis: utils.as_safe_bool(options.accessibility_analysis),
427426
use_asset_folder_as_public_id_prefix: utils.as_safe_bool(options.use_asset_folder_as_public_id_prefix),
428-
visual_search: utils.as_safe_bool(options.visual_search)
427+
visual_search: utils.as_safe_bool(options.visual_search),
428+
on_success: options.on_success
429429
};
430430
return utils.updateable_resource_params(options, params);
431431
}
@@ -928,16 +928,20 @@ function url(public_id) {
928928
var to_sign = [transformation, source_to_sign].filter(function (part) {
929929
return part != null && part !== '';
930930
}).join('/');
931-
try {
932-
for (var i = 0; to_sign !== decodeURIComponent(to_sign) && i < 10; i++) {
933-
to_sign = decodeURIComponent(to_sign);
934-
}
935-
// eslint-disable-next-line no-empty
936-
} catch (error) {}
937-
var hash = compute_hash(to_sign + api_secret, signature_algorithm, 'base64');
938-
signature = hash.replace(/\//g, '_').replace(/\+/g, '-').substring(0, long_url_signature ? 32 : 8);
939-
signature = `s--${signature}--`;
931+
932+
var signatureConfig = {};
933+
if (long_url_signature) {
934+
signatureConfig.algorithm = 'sha256';
935+
signatureConfig.signatureLength = 32;
936+
} else {
937+
signatureConfig.algorithm = signature_algorithm;
938+
signatureConfig.signatureLength = 8;
939+
}
940+
941+
var truncated = compute_hash(to_sign + api_secret, signatureConfig.algorithm, 'base64').slice(0, signatureConfig.signatureLength).replace(/\//g, '_').replace(/\+/g, '-');
942+
signature = `s--${truncated}--`;
940943
}
944+
941945
var prefix = build_distribution_domain(public_id, options);
942946
var resultUrl = [prefix, resource_type, type, signature, transformation, version, public_id].filter(function (part) {
943947
return part != null && part !== '';
@@ -1156,9 +1160,8 @@ function compute_hash(input, signature_algorithm, encoding) {
11561160
if (!SUPPORTED_SIGNATURE_ALGORITHMS.includes(signature_algorithm)) {
11571161
throw new Error(`Signature algorithm ${signature_algorithm} is not supported. Supported algorithms: ${SUPPORTED_SIGNATURE_ALGORITHMS.join(', ')}`);
11581162
}
1159-
var hash = crypto.createHash(signature_algorithm);
1160-
hash.update(utf8_encode(input), 'binary');
1161-
return hash.digest(encoding);
1163+
var hash = crypto.createHash(signature_algorithm).update(input).digest();
1164+
return Buffer.from(hash).toString(encoding);
11621165
}
11631166

11641167
function clear_blank(hash) {

lib/utils/index.js

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ const encodeDoubleArray = require('./encoding/encodeDoubleArray');
3838

3939
const config = require("../config");
4040
const generate_token = require("../auth_token");
41-
const utf8_encode = require('./utf8_encode');
4241
const crc32 = require('./crc32');
4342
const ensurePresenceOf = require('./ensurePresenceOf');
4443
const ensureOption = require('./ensureOption').defaults(config());
@@ -856,17 +855,23 @@ function url(public_id, options = {}) {
856855
let to_sign = [transformation, source_to_sign].filter(function (part) {
857856
return (part != null) && part !== '';
858857
}).join('/');
859-
try {
860-
for (let i = 0; to_sign !== decodeURIComponent(to_sign) && i < 10; i++) {
861-
to_sign = decodeURIComponent(to_sign);
862-
}
863-
// eslint-disable-next-line no-empty
864-
} catch (error) {
858+
859+
const signatureConfig = {};
860+
if (long_url_signature) {
861+
signatureConfig.algorithm = 'sha256';
862+
signatureConfig.signatureLength = 32;
863+
} else {
864+
signatureConfig.algorithm = signature_algorithm;
865+
signatureConfig.signatureLength = 8;
865866
}
866-
let hash = compute_hash(to_sign + api_secret, signature_algorithm, 'base64');
867-
signature = hash.replace(/\//g, '_').replace(/\+/g, '-').substring(0, long_url_signature ? 32 : 8);
868-
signature = `s--${signature}--`;
867+
868+
const truncated = compute_hash(to_sign + api_secret, signatureConfig.algorithm, 'base64')
869+
.slice(0, signatureConfig.signatureLength)
870+
.replace(/\//g, '_')
871+
.replace(/\+/g, '-');
872+
signature = `s--${truncated}--`;
869873
}
874+
870875
let prefix = build_distribution_domain(public_id, options);
871876
let resultUrl = [prefix, resource_type, type, signature, transformation, version, public_id].filter(function (part) {
872877
return (part != null) && part !== '';
@@ -1080,9 +1085,8 @@ function compute_hash(input, signature_algorithm, encoding) {
10801085
if (!SUPPORTED_SIGNATURE_ALGORITHMS.includes(signature_algorithm)) {
10811086
throw new Error(`Signature algorithm ${signature_algorithm} is not supported. Supported algorithms: ${SUPPORTED_SIGNATURE_ALGORITHMS.join(', ')}`);
10821087
}
1083-
let hash = crypto.createHash(signature_algorithm);
1084-
hash.update(utf8_encode(input), 'binary');
1085-
return hash.digest(encoding);
1088+
const hash = crypto.createHash(signature_algorithm).update(input).digest();
1089+
return Buffer.from(hash).toString(encoding);
10861090
}
10871091

10881092
function clear_blank(hash) {

test/unit/url/sign_url_spec.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
const cloudinary = require('../../../lib/cloudinary');
2+
const assert = require('assert');
3+
4+
const TEST_CLOUD_NAME = 'test123';
5+
const TEST_API_KEY = '1234';
6+
const TEST_API_SECRET = 'b';
7+
8+
9+
describe("URL for authenticated asset", () => {
10+
before(function () {
11+
cloudinary.config({
12+
cloud_name: TEST_CLOUD_NAME,
13+
api_key: TEST_API_KEY,
14+
api_secret: TEST_API_SECRET
15+
});
16+
});
17+
18+
let TEST_PUBLIC_ID = 'image.jpg';
19+
it('should have signature for transformation and version', () => {
20+
const signedUrl = cloudinary.utils.url(TEST_PUBLIC_ID, {
21+
version: 1234,
22+
transformation: {
23+
crop: "crop",
24+
width: 10,
25+
height: 20
26+
},
27+
sign_url: true
28+
});
29+
30+
assert.strictEqual(signedUrl, 'http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/v1234/image.jpg');
31+
});
32+
33+
it('should have signature for version', () => {
34+
const signedUrl = cloudinary.utils.url(TEST_PUBLIC_ID, {
35+
version: 1234,
36+
sign_url: true
37+
});
38+
39+
assert.strictEqual(signedUrl, 'http://res.cloudinary.com/test123/image/upload/s----SjmNDA--/v1234/image.jpg');
40+
});
41+
42+
it('should have signature for transformation', () => {
43+
const signedUrl = cloudinary.utils.url(TEST_PUBLIC_ID, {
44+
transformation: {
45+
crop: "crop",
46+
width: 10,
47+
height: 20
48+
},
49+
sign_url: true
50+
});
51+
52+
assert.strictEqual(signedUrl, 'http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/image.jpg');
53+
});
54+
55+
it('should have signature for authenticated asset with transformation', () => {
56+
const signedUrl = cloudinary.utils.url(TEST_PUBLIC_ID, {
57+
type: 'authenticated',
58+
transformation: {
59+
crop: "crop",
60+
width: 10,
61+
height: 20
62+
},
63+
sign_url: true
64+
});
65+
66+
assert.strictEqual(signedUrl, 'http://res.cloudinary.com/test123/image/authenticated/s--Ai4Znfl3--/c_crop,h_20,w_10/image.jpg');
67+
});
68+
69+
it('should have signature for fetched asset', () => {
70+
const signedUrl = cloudinary.utils.url('http://google.com/path/to/image.png', {
71+
type: 'fetch',
72+
version: 1234,
73+
sign_url: true
74+
});
75+
76+
assert.strictEqual(signedUrl, 'http://res.cloudinary.com/test123/image/fetch/s--hH_YcbiS--/v1234/http://google.com/path/to/image.png');
77+
});
78+
79+
it('should have signature for authenticated asset with text overlay transformation including encoded text', () => {
80+
const signedUrl = cloudinary.utils.url(TEST_PUBLIC_ID, {
81+
type: 'authenticated',
82+
sign_url: true,
83+
secure: true,
84+
transformation: {
85+
color: 'red',
86+
overlay: {
87+
text: 'Cool%25F0%259F%2598%258D',
88+
font_family: 'Times',
89+
font_size: 70,
90+
font_weight: 'bold'
91+
}
92+
}
93+
});
94+
95+
assert.strictEqual(signedUrl, 'https://res.cloudinary.com/test123/image/authenticated/s--Uqk1a-5W--/co_red,l_text:Times_70_bold:Cool%25F0%259F%2598%258D/image.jpg');
96+
});
97+
98+
it('should have signature for raw transformation string', () => {
99+
const signedUrl = cloudinary.utils.url(TEST_PUBLIC_ID, {
100+
type: 'authenticated',
101+
sign_url: true,
102+
secure: true,
103+
transformation: {
104+
raw_transformation: 'co_red,l_text:Times_70_bold:Cool%25F0%259F%2598%258D'
105+
}
106+
});
107+
108+
assert.strictEqual(signedUrl, 'https://res.cloudinary.com/test123/image/authenticated/s--Uqk1a-5W--/co_red,l_text:Times_70_bold:Cool%25F0%259F%2598%258D/image.jpg');
109+
});
110+
})

test/utils/get_sdk_versionID_spec.js

Whitespace-only changes.

test/utils/utils_spec.js

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,52 +1558,6 @@ describe("utils", function () {
15581558
).to.eql(false);
15591559
});
15601560
});
1561-
context("sign URLs", function () {
1562-
beforeEach(function () {
1563-
cloudinary.config({
1564-
cloud_name: 'test123',
1565-
api_key: "1234",
1566-
api_secret: "b"
1567-
});
1568-
});
1569-
it("should correctly sign URLs", function () {
1570-
test_cloudinary_url("image.jpg", {
1571-
version: 1234,
1572-
transformation: {
1573-
crop: "crop",
1574-
width: 10,
1575-
height: 20
1576-
},
1577-
sign_url: true
1578-
}, "http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/v1234/image.jpg", {});
1579-
test_cloudinary_url("image.jpg", {
1580-
version: 1234,
1581-
sign_url: true
1582-
}, "http://res.cloudinary.com/test123/image/upload/s----SjmNDA--/v1234/image.jpg", {});
1583-
test_cloudinary_url("image.jpg", {
1584-
transformation: {
1585-
crop: "crop",
1586-
width: 10,
1587-
height: 20
1588-
},
1589-
sign_url: true
1590-
}, "http://res.cloudinary.com/test123/image/upload/s--Ai4Znfl3--/c_crop,h_20,w_10/image.jpg", {});
1591-
test_cloudinary_url("image.jpg", {
1592-
transformation: {
1593-
crop: "crop",
1594-
width: 10,
1595-
height: 20
1596-
},
1597-
type: 'authenticated',
1598-
sign_url: true
1599-
}, "http://res.cloudinary.com/test123/image/authenticated/s--Ai4Znfl3--/c_crop,h_20,w_10/image.jpg", {});
1600-
test_cloudinary_url("http://google.com/path/to/image.png", {
1601-
type: "fetch",
1602-
version: 1234,
1603-
sign_url: true
1604-
}, "http://res.cloudinary.com/test123/image/fetch/s--hH_YcbiS--/v1234/http://google.com/path/to/image.png", {});
1605-
});
1606-
});
16071561
context("sign requests", function () {
16081562
var configBck = void 0;
16091563
before(function () {

0 commit comments

Comments
 (0)