Skip to content

Commit 8cbf0ef

Browse files
authored
fix: remove vulnerable dependency (#191)
fix: remove vulnerable dependency fixes #190
1 parent 4ab6e60 commit 8cbf0ef

File tree

4 files changed

+585
-349
lines changed

4 files changed

+585
-349
lines changed

lib/validate.js

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use strict';
22

33
const fs = require('fs');
4-
const is = require('is_js');
54
const path = require('path');
65

76
/**
@@ -12,7 +11,7 @@ const path = require('path');
1211
function validateClient(serverless, options) {
1312
const validationErrors = [];
1413

15-
if (!is.object(options)) {
14+
if (!isObject(options)) {
1615
validationErrors.push('Options must be an object defined under `custom.client`');
1716
throw validationErrors;
1817
}
@@ -25,27 +24,27 @@ function validateClient(serverless, options) {
2524
}
2625

2726
// bucketName must be a string
28-
if (!is.string(options.bucketName)) {
27+
if (typeof options.bucketName !== 'string') {
2928
validationErrors.push('Please specify a bucket name for the client in serverless.yml');
3029
}
3130

3231
// check header options
3332
if (options.objectHeaders) {
34-
if (!is.object(options.objectHeaders)) {
33+
if (!isObject(options.objectHeaders)) {
3534
validationErrors.push('objectHeaders must be an object');
3635
}
3736

3837
Object.keys(options.objectHeaders).forEach(p => {
39-
if (!is.array(options.objectHeaders[p])) {
38+
if (!Array.isArray(options.objectHeaders[p])) {
4039
validationErrors.push('Each member of objectHeaders must be an array');
4140
}
4241

4342
options.objectHeaders[p].forEach(h => {
44-
if (!(is.existy(h.name) && is.string(h.name))) {
43+
if (typeof h.name !== 'string') {
4544
validationErrors.push(`Each object header must have a (string) 'name' attribute`);
4645
}
4746

48-
if (!(is.existy(h.value) && is.string(h.value))) {
47+
if (typeof h.value !== 'string') {
4948
validationErrors.push(`Each object header must have a (string) 'value' attribute`);
5049
}
5150
});
@@ -80,48 +79,48 @@ function validateClient(serverless, options) {
8079
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-websiteconfiguration.html
8180
if (options.redirectAllRequestsTo) {
8281
const clientConfigOptions = Object.keys(options);
83-
if (is.inArray('indexDocument', clientConfigOptions)) {
82+
if (clientConfigOptions.includes('indexDocument')) {
8483
validationErrors.push('indexDocument cannot be specified with redirectAllRequestsTo');
8584
}
8685

87-
if (is.inArray('errorDocument', clientConfigOptions)) {
86+
if (clientConfigOptions.includes('errorDocument')) {
8887
validationErrors.push('errorDocument cannot be specified with redirectAllRequestsTo');
8988
}
9089

91-
if (is.inArray('routingRules', clientConfigOptions)) {
90+
if (clientConfigOptions.includes('routingRules')) {
9291
validationErrors.push('routingRules cannot be specified with redirectAllRequestsTo');
9392
}
9493

95-
if (!is.existy(options.redirectAllRequestsTo.hostName)) {
94+
if (!options.redirectAllRequestsTo.hostName) {
9695
validationErrors.push(
9796
'redirectAllRequestsTo.hostName is required if redirectAllRequestsTo is specified'
9897
);
9998
}
100-
if (!is.string(options.redirectAllRequestsTo.hostName)) {
99+
if (typeof options.redirectAllRequestsTo.hostName !== 'string') {
101100
validationErrors.push('redirectAllRequestsTo.hostName must be a string');
102101
}
103102

104103
if (options.redirectAllRequestsTo.protocol) {
105-
if (!is.string(options.redirectAllRequestsTo.protocol)) {
104+
if (typeof options.redirectAllRequestsTo.protocol !== 'string') {
106105
validationErrors.push('redirectAllRequestsTo.protocol must be a string');
107106
}
108-
if (!is.inArray(options.redirectAllRequestsTo.protocol.toLowerCase(), ['http', 'https'])) {
107+
if (!['http', 'https'].includes(options.redirectAllRequestsTo.protocol.toLowerCase())) {
109108
validationErrors.push('redirectAllRequestsTo.protocol must be either http or https');
110109
}
111110
}
112111
}
113112

114113
if (options.routingRules) {
115-
if (!is.array(options.routingRules)) {
114+
if (!Array.isArray(options.routingRules)) {
116115
validationErrors.push('routingRules must be a list');
117116
}
118117

119118
options.routingRules.forEach(r => {
120-
if (!is.existy(r.redirect)) {
119+
if (!r.redirect) {
121120
validationErrors.push('redirect must be specified for each member of routingRules');
122121
}
123122

124-
if (is.existy(r.redirect.replaceKeyPrefixWith) && is.existy(r.redirect.replaceKeyWith)) {
123+
if (r.redirect.replaceKeyPrefixWith && r.redirect.replaceKeyWith) {
125124
validationErrors.push(
126125
'replaceKeyPrefixWith and replaceKeyWith cannot both be specified for a member of member of routingRules'
127126
);
@@ -137,13 +136,13 @@ function validateClient(serverless, options) {
137136
redirectProps.forEach(p => {
138137
if (r.redirect[p]) {
139138
if (p === 'httpRedirectCode') {
140-
if (!is.integer(r.redirect[p])) {
139+
if (!Number.isInteger(r.redirect[p])) {
141140
validationErrors.push(
142141
'redirect.httpRedirectCode must be an integer for each member of routingRules'
143142
);
144143
}
145144
} else {
146-
if (!is.string(r.redirect[p])) {
145+
if (typeof r.redirect[p] !== 'string') {
147146
validationErrors.push(
148147
`redirect.${p} must be a string for each member of routingRules`
149148
);
@@ -153,12 +152,7 @@ function validateClient(serverless, options) {
153152
});
154153

155154
if (r.condition) {
156-
if (
157-
!(
158-
is.existy(r.condition.httpErrorCodeReturnedEquals) ||
159-
is.existy(r.condition.keyPrefixEquals)
160-
)
161-
) {
155+
if (!r.condition.httpErrorCodeReturnedEquals && !r.condition.keyPrefixEquals) {
162156
validationErrors.push(
163157
'condition.httpErrorCodeReturnedEquals or condition.keyPrefixEquals must be defined for each member of routingRules'
164158
);
@@ -168,11 +162,11 @@ function validateClient(serverless, options) {
168162
conditionProps.forEach(p => {
169163
if (r.condition[p]) {
170164
if (p === 'httpErrorCodeReturnedEquals') {
171-
if (!is.integer(r.condition[p])) {
165+
if (!Number.isInteger(r.condition[p])) {
172166
validationErrors.push('httpErrorCodeReturnedEquals must be an integer');
173167
}
174168
} else {
175-
if (!is.string(r.condition[p])) {
169+
if (typeof r.condition[p] !== 'string') {
176170
validationErrors.push(`${p} must be a string`);
177171
}
178172
}
@@ -187,4 +181,8 @@ function validateClient(serverless, options) {
187181
}
188182
}
189183

184+
function isObject(value) {
185+
return typeof value === 'object' && value !== null && !Array.isArray(value);
186+
}
187+
190188
module.exports = validateClient;

lib/validate.test.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,60 @@ describe('validate', () => {
7373
expect(() => validate(sls, { bucketName, objectHeaders })).not.to.throw();
7474
});
7575

76+
it('throws if redirectAllRequestsTo is missing hostName', () => {
77+
expect(() => validate(sls, { bucketName, redirectAllRequestsTo: {} })).to.throw();
78+
expect(() =>
79+
validate(sls, { bucketName, redirectAllRequestsTo: { hostName: 'example.com' } })
80+
).not.to.throw();
81+
});
82+
83+
it('throws if redirectAllRequestsTo has invalid protocol', () => {
84+
expect(() =>
85+
validate(sls, {
86+
bucketName,
87+
redirectAllRequestsTo: { hostName: 'example.com', protocol: 'ftp' }
88+
})
89+
).to.throw();
90+
expect(() =>
91+
validate(sls, {
92+
bucketName,
93+
redirectAllRequestsTo: { hostName: 'example.com', protocol: true }
94+
})
95+
).to.throw();
96+
expect(() =>
97+
validate(sls, {
98+
bucketName,
99+
redirectAllRequestsTo: { hostName: 'example.com', protocol: 'http' }
100+
})
101+
).not.to.throw();
102+
expect(() =>
103+
validate(sls, {
104+
bucketName,
105+
redirectAllRequestsTo: { hostName: 'example.com', protocol: 'https' }
106+
})
107+
).not.to.throw();
108+
});
109+
110+
it('throws if redirectAllRequestsTo is specified along with other website options', () => {
111+
const redirectAllRequestsTo = {
112+
hostName: 'example.com',
113+
protocol: 'https'
114+
};
115+
expect(() =>
116+
validate(sls, { bucketName, redirectAllRequestsTo, indexDocument: 'index.html' })
117+
).to.throw();
118+
expect(() =>
119+
validate(sls, { bucketName, redirectAllRequestsTo, errorDocument: 'error.html' })
120+
).to.throw();
121+
expect(() =>
122+
validate(sls, {
123+
bucketName,
124+
redirectAllRequestsTo,
125+
routingRules: { redirect: { replaceKeyWith: '' } }
126+
})
127+
).to.throw();
128+
});
129+
76130
it('throws if reading `corsFile` fails or contains invalid json', () => {
77131
readFileSyncStub.withArgs('nonexistent-file.json').throws();
78132
readFileSyncStub.withArgs('invalid-rules.json').returns('not json');

0 commit comments

Comments
 (0)