-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: improve base64 validation based on RFC4648
add padding to the option list update regexes to support validation with/without padding update default options to keep the changes backward compatible add new test to cover different scenarios
- Loading branch information
Showing
4 changed files
with
141 additions
and
127 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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', | ||
], | ||
}); | ||
}); | ||
}); |
This file was deleted.
Oops, something went wrong.