diff --git a/README.md b/README.md
index c7518fd41..7a8bc14f6 100644
--- a/README.md
+++ b/README.md
@@ -93,7 +93,7 @@ Validator | Description
**isAscii(str)** | check if the string contains ASCII chars only.
**isBase32(str [, options])** | check if the string is base32 encoded. `options` is optional and defaults to `{ crockford: false }`.
When `crockford` is true it tests the given base32 encoded string using [Crockford's base32 alternative][Crockford Base32].
**isBase58(str)** | check if the string is base58 encoded.
-**isBase64(str [, options])** | check if the string is base64 encoded. `options` is optional and defaults to `{ urlSafe: false }`
when `urlSafe` is true it tests the given base64 encoded string is [url safe][Base64 URL Safe].
+**isBase64(str [, options])** | check if the string is base64 encoded. `options` is optional and defaults to `{ urlSafe: false, padding: true }`
when `urlSafe` is true default value for `padding` is false and it tests the given base64 encoded string is [url safe][Base64 URL Safe].
**isBefore(str [, date])** | check if the string is a date that is before the specified date.
**isBIC(str)** | check if the string is a BIC (Bank Identification Code) or SWIFT code.
**isBoolean(str [, options])** | check if the string is a boolean.
`options` is an object which defaults to `{ loose: false }`. If `loose` is set to false, the validator will strictly match ['true', 'false', '0', '1']. If `loose` is set to true, the validator will also match 'yes', 'no', and will match a valid boolean string of any case. (e.g.: ['true', 'True', 'TRUE']).
diff --git a/src/lib/isBase64.js b/src/lib/isBase64.js
index 02dead0f4..7eb3a5b56 100644
--- a/src/lib/isBase64.js
+++ b/src/lib/isBase64.js
@@ -1,28 +1,23 @@
import assertString from './util/assertString';
import merge from './util/merge';
-const notBase64 = /[^A-Z0-9+\/=]/i;
-const urlSafeBase64 = /^[A-Z0-9_\-]*$/i;
-
-const defaultBase64Options = {
- urlSafe: false,
-};
+const base64WithPadding = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$/;
+const base64WithoutPadding = /^[A-Za-z0-9+/]+$/;
+const base64UrlWithPadding = /^(?:[A-Za-z0-9_-]{4})*(?:[A-Za-z0-9_-]{2}==|[A-Za-z0-9_-]{3}=|[A-Za-z0-9_-]{4})$/;
+const base64UrlWithoutPadding = /^[A-Za-z0-9_-]+$/;
export default function isBase64(str, options) {
assertString(str);
- options = merge(options, defaultBase64Options);
- const len = str.length;
+ options = merge(options, { urlSafe: false, padding: !options?.urlSafe });
- if (options.urlSafe) {
- return urlSafeBase64.test(str);
- }
+ if (str === '') return true;
- if (len % 4 !== 0 || notBase64.test(str)) {
- return false;
+ let regex;
+ if (options.urlSafe) {
+ regex = options.padding ? base64UrlWithPadding : base64UrlWithoutPadding;
+ } else {
+ regex = options.padding ? base64WithPadding : base64WithoutPadding;
}
- const firstPaddingChar = str.indexOf('=');
- return firstPaddingChar === -1 ||
- firstPaddingChar === len - 1 ||
- (firstPaddingChar === len - 2 && str[len - 1] === '=');
+ return (!options.padding || str.length % 4 === 0) && regex.test(str);
}
diff --git a/test/validators.test.js b/test/validators.test.js
index 08d76f821..4aeed5b3a 100644
--- a/test/validators.test.js
+++ b/test/validators.test.js
@@ -1,9 +1,7 @@
import assert from 'assert';
import fs from 'fs';
import timezone_mock from 'timezone-mock';
-import { format } from 'util';
import vm from 'vm';
-import validator from '../src/index';
import test from './testFunctions';
let validator_js = fs.readFileSync(require.resolve('../validator.js')).toString();
@@ -7103,76 +7101,6 @@ describe('Validators', () => {
});
});
- it('should validate base64 strings', () => {
- test({
- validator: 'isBase64',
- valid: [
- '',
- 'Zg==',
- 'Zm8=',
- 'Zm9v',
- 'Zm9vYg==',
- 'Zm9vYmE=',
- 'Zm9vYmFy',
- 'TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=',
- 'Vml2YW11cyBmZXJtZW50dW0gc2VtcGVyIHBvcnRhLg==',
- 'U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==',
- 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMPNS1Ufof9EW/M98FNw' +
- 'UAKrwflsqVxaxQjBQnHQmiI7Vac40t8x7pIb8gLGV6wL7sBTJiPovJ0V7y7oc0Ye' +
- 'rhKh0Rm4skP2z/jHwwZICgGzBvA0rH8xlhUiTvcwDCJ0kc+fh35hNt8srZQM4619' +
- 'FTgB66Xmp4EtVyhpQV+t02g6NzK72oZI0vnAvqhpkxLeLiMCyrI416wHm5Tkukhx' +
- 'QmcL2a6hNOyu0ixX/x2kSFXApEnVrJ+/IxGyfyw8kf4N2IZpW5nEP847lpfj0SZZ' +
- 'Fwrd1mnfnDbYohX2zRptLy2ZUn06Qo9pkG5ntvFEPo9bfZeULtjYzIl6K8gJ2uGZ' +
- 'HQIDAQAB',
- ],
- invalid: [
- '12345',
- 'Vml2YW11cyBmZXJtZtesting123',
- 'Zg=',
- 'Z===',
- 'Zm=8',
- '=m9vYg==',
- 'Zm9vYmFy====',
- ],
- });
-
- test({
- validator: 'isBase64',
- args: [{ urlSafe: true }],
- valid: [
- '',
- 'bGFkaWVzIGFuZCBnZW50bGVtZW4sIHdlIGFyZSBmbG9hdGluZyBpbiBzcGFjZQ',
- '1234',
- 'bXVtLW5ldmVyLXByb3Vk',
- 'PDw_Pz8-Pg',
- 'VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw',
- ],
- invalid: [
- ' AA',
- '\tAA',
- '\rAA',
- '\nAA',
- 'This+isa/bad+base64Url==',
- '0K3RgtC+INC30LDQutC+0LTQuNGA0L7QstCw0L3QvdCw0Y8g0YHRgtGA0L7QutCw',
- ],
- error: [
- null,
- undefined,
- {},
- [],
- 42,
- ],
- });
-
- for (let i = 0, str = '', encoded; i < 1000; i++) {
- str += String.fromCharCode(Math.random() * 26 | 97); // eslint-disable-line no-bitwise
- encoded = Buffer.from(str).toString('base64');
- if (!validator.isBase64(encoded)) {
- let msg = format('validator.isBase64() failed with "%s"', encoded);
- throw new Error(msg);
- }
- }
- });
it('should validate hex-encoded MongoDB ObjectId', () => {
test({
@@ -13703,37 +13631,6 @@ describe('Validators', () => {
});
});
- it('should validate base64URL', () => {
- test({
- validator: 'isBase64',
- args: [{ urlSafe: true }],
- valid: [
- '',
- 'bGFkaWVzIGFuZCBnZW50bGVtZW4sIHdlIGFyZSBmbG9hdGluZyBpbiBzcGFjZQ',
- '1234',
- 'bXVtLW5ldmVyLXByb3Vk',
- 'PDw_Pz8-Pg',
- 'VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw',
- ],
- invalid: [
- ' AA',
- '\tAA',
- '\rAA',
- '\nAA',
- '123=',
- 'This+isa/bad+base64Url==',
- '0K3RgtC+INC30LDQutC+0LTQuNGA0L7QstCw0L3QvdCw0Y8g0YHRgtGA0L7QutCw',
- ],
- error: [
- null,
- undefined,
- {},
- [],
- 42,
- ],
- });
- });
-
it('should validate date', () => {
test({
validator: 'isDate',
diff --git a/test/validators/isBase64.test.js b/test/validators/isBase64.test.js
new file mode 100644
index 000000000..c0074343a
--- /dev/null
+++ b/test/validators/isBase64.test.js
@@ -0,0 +1,201 @@
+import { format } from 'util';
+import test from '../testFunctions';
+import validator from '../../src';
+
+describe('isBase64', () => {
+ it('should validate base64 strings with default options', () => {
+ test({
+ validator: 'isBase64',
+ valid: [
+ '',
+ 'Zg==',
+ 'Zm8=',
+ 'Zm9v',
+ 'Zm9vYg==',
+ 'Zm9vYmE=',
+ 'Zm9vYmFy',
+ 'TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=',
+ 'Vml2YW11cyBmZXJtZW50dW0gc2VtcGVyIHBvcnRhLg==',
+ 'U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==',
+ 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMPNS1Ufof9EW/M98FNw' +
+ 'UAKrwflsqVxaxQjBQnHQmiI7Vac40t8x7pIb8gLGV6wL7sBTJiPovJ0V7y7oc0Ye' +
+ 'rhKh0Rm4skP2z/jHwwZICgGzBvA0rH8xlhUiTvcwDCJ0kc+fh35hNt8srZQM4619' +
+ 'FTgB66Xmp4EtVyhpQV+t02g6NzK72oZI0vnAvqhpkxLeLiMCyrI416wHm5Tkukhx' +
+ 'QmcL2a6hNOyu0ixX/x2kSFXApEnVrJ+/IxGyfyw8kf4N2IZpW5nEP847lpfj0SZZ' +
+ 'Fwrd1mnfnDbYohX2zRptLy2ZUn06Qo9pkG5ntvFEPo9bfZeULtjYzIl6K8gJ2uGZ' +
+ 'HQIDAQAB',
+ ],
+ invalid: [
+ '12345',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ 'Zg=',
+ 'Z===',
+ 'Zm=8',
+ '=m9vYg==',
+ 'Zm9vYmFy====',
+ ],
+ });
+
+ test({
+ validator: 'isBase64',
+ args: [{ urlSafe: true }],
+ valid: [
+ '',
+ 'bGFkaWVzIGFuZCBnZW50bGVtZW4sIHdlIGFyZSBmbG9hdGluZyBpbiBzcGFjZQ',
+ '1234',
+ 'bXVtLW5ldmVyLXByb3Vk',
+ 'PDw_Pz8-Pg',
+ 'VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw',
+ ],
+ invalid: [
+ ' AA',
+ '\tAA',
+ '\rAA',
+ '\nAA',
+ 'This+isa/bad+base64Url==',
+ '0K3RgtC+INC30LDQutC+0LTQuNGA0L7QstCw0L3QvdCw0Y8g0YHRgtGA0L7QutCw',
+ ],
+ error: [
+ null,
+ undefined,
+ {},
+ [],
+ 42,
+ ],
+ });
+
+ for (let i = 0, str = '', encoded; i < 1000; i++) {
+ str += String.fromCharCode(Math.random() * 26 | 97); // eslint-disable-line no-bitwise
+ encoded = Buffer.from(str).toString('base64');
+ if (!validator.isBase64(encoded)) {
+ let msg = format('validator.isBase64() failed with "%s"', encoded);
+ throw new Error(msg);
+ }
+ }
+ });
+
+ it('should validate standard Base64 with padding', () => {
+ test({
+ validator: 'isBase64',
+ args: [{ urlSafe: false, padding: true }],
+ valid: [
+ '',
+ 'TWFu',
+ 'TWE=',
+ 'TQ==',
+ 'SGVsbG8=',
+ 'U29mdHdhcmU=',
+ 'YW55IGNhcm5hbCBwbGVhc3VyZS4=',
+ ],
+ invalid: [
+ 'TWF',
+ 'TWE===',
+ 'SGVsbG8@',
+ 'SGVsbG8===',
+ 'SGVsb G8=',
+ '====',
+ ],
+ });
+ });
+
+ it('should validate standard Base64 without padding', () => {
+ test({
+ validator: 'isBase64',
+ args: [{ urlSafe: false, padding: false }],
+ valid: [
+ '',
+ 'TWFu',
+ 'TWE',
+ 'TQ',
+ 'SGVsbG8',
+ 'U29mdHdhcmU',
+ 'YW55IGNhcm5hbCBwbGVhc3VyZS4',
+ ],
+ invalid: [
+ 'TWE=',
+ 'TQ===',
+ 'SGVsbG8@',
+ 'SGVsbG8===',
+ 'SGVsb G8',
+ '====',
+ ],
+ });
+ });
+
+ it('should validate Base64url with padding', () => {
+ test({
+ validator: 'isBase64',
+ args: [{ urlSafe: true, padding: true }],
+ valid: [
+ '',
+ 'SGVsbG8=',
+ 'U29mdHdhcmU=',
+ 'YW55IGNhcm5hbCBwbGVhc3VyZS4=',
+ 'SGVsbG8-',
+ 'SGVsbG8_',
+ ],
+ invalid: [
+ 'SGVsbG8===',
+ 'SGVsbG8@',
+ 'SGVsb G8=',
+ '====',
+ ],
+ });
+ });
+
+ it('should validate Base64url without padding', () => {
+ test({
+ validator: 'isBase64',
+ args: [{ urlSafe: true, padding: false }],
+ valid: [
+ '',
+ 'SGVsbG8',
+ 'U29mdHdhcmU',
+ 'YW55IGNhcm5hbCBwbGVhc3VyZS4',
+ 'SGVsbG8-',
+ 'SGVsbG8_',
+ ],
+ invalid: [
+ 'SGVsbG8=',
+ 'SGVsbG8===',
+ 'SGVsbG8@',
+ 'SGVsb G8',
+ '====',
+ ],
+ });
+ });
+
+ it('should handle mixed cases correctly', () => {
+ test({
+ validator: 'isBase64',
+ args: [{ urlSafe: false, padding: true }],
+ valid: [
+ '',
+ 'TWFu',
+ 'TWE=',
+ 'TQ==',
+ ],
+ invalid: [
+ 'TWE',
+ 'TQ=',
+ 'TQ===',
+ ],
+ });
+
+ test({
+ validator: 'isBase64',
+ args: [{ urlSafe: true, padding: false }],
+ valid: [
+ '',
+ 'SGVsbG8',
+ 'SGVsbG8-',
+ 'SGVsbG8_',
+ ],
+ invalid: [
+ 'SGVsbG8=',
+ 'SGVsbG8@',
+ 'SGVsb G8',
+ ],
+ });
+ });
+});