-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added tests, made fieldName configurable
- Loading branch information
Showing
15 changed files
with
256 additions
and
187 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
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
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
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
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 |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { Config, CookieOptions, TokenOptions } from './config'; | ||
import type { ConfigOptions } from './config'; | ||
|
||
describe('CookieOptions tests', () => { | ||
it('returns default values when options are absent', () => { | ||
const cookieOpts = new CookieOptions(); | ||
expect(cookieOpts.domain).toEqual(''); | ||
expect(cookieOpts.httpOnly).toEqual(true); | ||
expect(cookieOpts.maxAge).toEqual(undefined); | ||
expect(cookieOpts.name).toEqual('_csrfSecret'); | ||
expect(cookieOpts.partitioned).toEqual(undefined); | ||
expect(cookieOpts.path).toEqual('/'); | ||
expect(cookieOpts.sameSite).toEqual('strict'); | ||
expect(cookieOpts.secure).toEqual(true); | ||
}); | ||
|
||
it('handles overrides', () => { | ||
const cookieOpts = new CookieOptions({ domain: 'xxx' }); | ||
expect(cookieOpts.domain).toEqual('xxx'); | ||
}); | ||
}); | ||
|
||
describe('TokenOptions tests', () => { | ||
it('returns default values when options are absent', () => { | ||
const tokenOpts = new TokenOptions(); | ||
expect(tokenOpts.fieldName).toEqual('csrf_token'); | ||
expect(tokenOpts.value).toEqual(undefined); | ||
}); | ||
|
||
it('handles overrides', () => { | ||
const fn = async () => ''; | ||
const tokenOpts = new TokenOptions({ | ||
fieldName: 'csrfToken', | ||
value: fn, | ||
}); | ||
expect(tokenOpts.fieldName).toEqual('csrfToken'); | ||
expect(tokenOpts.value).toBe(fn); | ||
}); | ||
}); | ||
|
||
describe('Config tests', () => { | ||
const initConfigFn = (opts: Partial<ConfigOptions>) => () => new Config(opts); | ||
|
||
it('returns default config when options are absent', () => { | ||
const config = new Config(); | ||
expect(config.excludePathPrefixes).toEqual([]); | ||
expect(config.ignoreMethods).toEqual(['GET', 'HEAD', 'OPTIONS']); | ||
expect(config.saltByteLength).toEqual(8); | ||
expect(config.secretByteLength).toEqual(18); | ||
expect(config.cookie instanceof CookieOptions).toBe(true); | ||
expect(config.token instanceof TokenOptions).toBe(true); | ||
}); | ||
|
||
it('handles top-level overrides', () => { | ||
const config = new Config({ saltByteLength: 10 }); | ||
expect(config.saltByteLength).toEqual(10); | ||
}); | ||
|
||
it('handles nested cookie overrides', () => { | ||
const config = new Config({ cookie: { domain: 'xxx' } }); | ||
expect(config.cookie.domain).toEqual('xxx'); | ||
}); | ||
|
||
it('handles nested token overrides', () => { | ||
const fn = async () => ''; | ||
const config = new Config({ token: { fieldName: 'csrfToken', value: fn } }); | ||
expect(config.token.fieldName).toEqual('csrfToken'); | ||
expect(config.token.value).toBe(fn); | ||
}); | ||
|
||
it('saltByteLength must be greater than 0', () => { | ||
expect(initConfigFn({ saltByteLength: 0 })).toThrow(Error); | ||
expect(initConfigFn({ saltByteLength: 1 })).not.toThrow(Error); | ||
}); | ||
|
||
it('saltByteLength must be less than 256', () => { | ||
expect(initConfigFn({ saltByteLength: 256 })).toThrow(Error); | ||
expect(initConfigFn({ saltByteLength: 255 })).not.toThrow(Error); | ||
}); | ||
|
||
it('secretByteLength must be greater than 0', () => { | ||
expect(initConfigFn({ secretByteLength: 0 })).toThrow(Error); | ||
expect(initConfigFn({ secretByteLength: 1 })).not.toThrow(Error); | ||
}); | ||
|
||
it('secretByteLength must be less than 256', () => { | ||
expect(initConfigFn({ secretByteLength: 256 })).toThrow(Error); | ||
expect(initConfigFn({ secretByteLength: 255 })).not.toThrow(Error); | ||
}); | ||
}); |
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,90 @@ | ||
/** | ||
* Represents cookie options in config | ||
*/ | ||
export class CookieOptions { | ||
domain: string = ''; | ||
|
||
httpOnly: boolean = true; | ||
|
||
maxAge: number | undefined = undefined; | ||
|
||
name: string = '_csrfSecret'; | ||
|
||
partitioned: boolean | undefined = undefined; | ||
|
||
path: string = '/'; | ||
|
||
sameSite: boolean | 'none' | 'strict' | 'lax' = 'strict'; | ||
|
||
secure: boolean = true; | ||
|
||
constructor(opts?: Partial<CookieOptions>) { | ||
Object.assign(this, opts); | ||
} | ||
} | ||
|
||
/** | ||
* Represents a function to retrieve token value from a request | ||
*/ | ||
export type TokenValueFunction = { | ||
(request: Request): Promise<string> | ||
}; | ||
|
||
/** | ||
* Represents token options in config | ||
*/ | ||
export class TokenOptions { | ||
readonly fieldName: string = 'csrf_token'; | ||
|
||
value: TokenValueFunction | undefined = undefined; | ||
|
||
_fieldNameRegex: RegExp; | ||
|
||
constructor(opts?: Partial<TokenOptions>) { | ||
Object.assign(this, opts); | ||
|
||
// create fieldname regex | ||
this._fieldNameRegex = new RegExp(`^(\\d+_)*${this.fieldName}$`); | ||
} | ||
} | ||
|
||
/** | ||
* Represents CsrfProtect configuration object | ||
*/ | ||
export class Config { | ||
excludePathPrefixes: string[] = []; | ||
|
||
ignoreMethods: string[] = ['GET', 'HEAD', 'OPTIONS']; | ||
|
||
saltByteLength: number = 8; | ||
|
||
secretByteLength: number = 18; | ||
|
||
cookie: CookieOptions = new CookieOptions(); | ||
|
||
token: TokenOptions = new TokenOptions(); | ||
|
||
constructor(opts?: Partial<ConfigOptions>) { | ||
const newOpts = opts || {}; | ||
if (newOpts.cookie) newOpts.cookie = new CookieOptions(newOpts.cookie); | ||
if (newOpts.token) newOpts.token = new TokenOptions(newOpts.token); | ||
Object.assign(this, newOpts); | ||
|
||
// basic validation | ||
if (this.saltByteLength < 1 || this.saltByteLength > 255) { | ||
throw Error('saltBytLength must be greater than 0 and less than 256'); | ||
} | ||
|
||
if (this.secretByteLength < 1 || this.secretByteLength > 255) { | ||
throw Error('secretBytLength must be greater than 0 and less than 256'); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Represents CsrfProtect configuration options object | ||
*/ | ||
export interface ConfigOptions extends Omit<Config, 'cookie' | 'token'> { | ||
cookie: Partial<CookieOptions>; | ||
token: Partial<TokenOptions>; | ||
} |
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
Oops, something went wrong.