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/isBase64.test.js b/test/validators/isBase64.test.js
new file mode 100644
index 000000000..91004e3e6
--- /dev/null
+++ b/test/validators/isBase64.test.js
@@ -0,0 +1,128 @@
+import test from '../testFunctions';
+
+describe('isBase64', () => {
+ 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',
+ ],
+ });
+ });
+});
diff --git a/test/validators/isISBN.test.js b/test/validators/isISBN.test.js
deleted file mode 100644
index 99fb2e014..000000000
--- a/test/validators/isISBN.test.js
+++ /dev/null
@@ -1,109 +0,0 @@
-import test from '../testFunctions';
-
-describe('isISBN', () => {
- it('should validate ISBNs', () => {
- test({
- validator: 'isISBN',
- args: [{ version: 10 }],
- valid: [
- '3836221195', '3-8362-2119-5', '3 8362 2119 5',
- '1617290858', '1-61729-085-8', '1 61729 085-8',
- '0007269706', '0-00-726970-6', '0 00 726970 6',
- '3423214120', '3-423-21412-0', '3 423 21412 0',
- '340101319X', '3-401-01319-X', '3 401 01319 X',
- ],
- invalid: [
- '3423214121', '3-423-21412-1', '3 423 21412 1',
- '978-3836221191', '9783836221191',
- '123456789a', 'foo', '',
- ],
- });
- test({
- validator: 'isISBN',
- args: [{ version: 13 }],
- valid: [
- '9783836221191', '978-3-8362-2119-1', '978 3 8362 2119 1',
- '9783401013190', '978-3401013190', '978 3401013190',
- '9784873113685', '978-4-87311-368-5', '978 4 87311 368 5',
- ],
- invalid: [
- '9783836221190', '978-3-8362-2119-0', '978 3 8362 2119 0',
- '3836221195', '3-8362-2119-5', '3 8362 2119 5',
- '01234567890ab', 'foo', '',
- ],
- });
- test({
- validator: 'isISBN',
- valid: [
- '340101319X',
- '9784873113685',
- ],
- invalid: [
- '3423214121',
- '9783836221190',
- ],
- });
- test({
- validator: 'isISBN',
- args: [{ version: 'foo' }],
- invalid: [
- '340101319X',
- '9784873113685',
- ],
- });
- });
-
- describe('(legacy syntax)', () => {
- it('should validate ISBNs', () => {
- test({
- validator: 'isISBN',
- args: [10],
- valid: [
- '3836221195', '3-8362-2119-5', '3 8362 2119 5',
- '1617290858', '1-61729-085-8', '1 61729 085-8',
- '0007269706', '0-00-726970-6', '0 00 726970 6',
- '3423214120', '3-423-21412-0', '3 423 21412 0',
- '340101319X', '3-401-01319-X', '3 401 01319 X',
- ],
- invalid: [
- '3423214121', '3-423-21412-1', '3 423 21412 1',
- '978-3836221191', '9783836221191',
- '123456789a', 'foo', '',
- ],
- });
- test({
- validator: 'isISBN',
- args: [13],
- valid: [
- '9783836221191', '978-3-8362-2119-1', '978 3 8362 2119 1',
- '9783401013190', '978-3401013190', '978 3401013190',
- '9784873113685', '978-4-87311-368-5', '978 4 87311 368 5',
- ],
- invalid: [
- '9783836221190', '978-3-8362-2119-0', '978 3 8362 2119 0',
- '3836221195', '3-8362-2119-5', '3 8362 2119 5',
- '01234567890ab', 'foo', '',
- ],
- });
- test({
- validator: 'isISBN',
- valid: [
- '340101319X',
- '9784873113685',
- ],
- invalid: [
- '3423214121',
- '9783836221190',
- ],
- });
- test({
- validator: 'isISBN',
- args: ['foo'],
- invalid: [
- '340101319X',
- '9784873113685',
- ],
- });
- });
- });
-});