Skip to content

Commit ab9feab

Browse files
author
khanh2906
committed
Update type release v1.1.1
1 parent 0abf616 commit ab9feab

File tree

5 files changed

+233
-45
lines changed

5 files changed

+233
-45
lines changed

SECURITY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
| Version | Supported |
66
| ------- | ------------------ |
7-
| 1.0.0 | :white_check_mark: |
7+
| 1.0.3 | :white_check_mark: |
8+
| 1.1.1 | :white_check_mark: |
89

910

1011
## Reporting a Vulnerability

lib/cjs/index.js

Lines changed: 104 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,30 @@
1-
"use strict"
1+
"use strict";
22

33
/**
4-
* ************************************
5-
* Cross-site request forgery
6-
* ************************************
4+
* @module csrf
5+
* @description Cross-site request forgery (CSRF) protection middleware.
76
*/
87

9-
const crypto = require("crypto")
8+
const crypto = require("crypto");
109

1110
/**
1211
* @typedef {Object} CsrfConfig
13-
* @property {number} tokenLength
14-
* @property {Object} storage
15-
* @property {string} storage.type
16-
* @property {Object} storage.options
17-
* @property {string} param
18-
* @property {string} value
19-
* @property {function} errorResponse
20-
* @property {function} protectCondition
21-
* @property {function} getTransmitToken
12+
* @property {number} [tokenLength=16] - The length of the CSRF token in bytes.
13+
* @property {Object} [storage] - The storage configuration for the CSRF token.
14+
* @property {string} [storage.type='session'] - The type of storage to use for the CSRF token ('session' or 'cookie').
15+
* @property {Object} [storage.options={}] - Options to pass to the cookie storage (e.g., `domain`, `path`, `secure`, `httpOnly`). Only applicable when `storage.type` is 'cookie'.
16+
* @property {string} [param='_csrf'] - The name of the request body or query parameter to check for the CSRF token.
17+
* @property {string} [value='csrfToken'] - The name of the local variable to set the CSRF token to for use in templates.
18+
* @property {function} [errorResponse] - A function to call when the CSRF token is invalid. It should accept `(req, res, next)` and send an appropriate error response.
19+
* @property {function} [protectCondition] - A function to determine if CSRF protection should be applied. It should accept `(req)` and return `true` to protect, `false` to skip.
20+
* @property {function} [getTransmitToken] - A function to retrieve the CSRF token from the request. It should accept `(req)` and return the token string or `null`.
2221
*/
2322

2423
/**
2524
* @type {Object}
2625
* @property {Object} STORAGE
27-
* @property {string} STORAGE.SESSION
28-
* @property {string} STORAGE.COOKIE
26+
* @property {string} STORAGE.SESSION - String constant representing session storage type.
27+
* @property {string} STORAGE.COOKIE - String constant representing cookie storage type.
2928
*/
3029
const CONSTANT = {
3130
STORAGE: {
@@ -36,20 +35,21 @@ const CONSTANT = {
3635

3736
/**
3837
* @typedef {Object} Csrf
39-
* @property {string} param
40-
* @property {string} value
41-
* @property {Object} storage
42-
* @property {string} storage.type
43-
* @property {Object} storage.options
44-
* @property {number} tokenLength
45-
* @property {function} getToken
46-
* @property {function} clearToken
47-
* @property {function} protectCondition
48-
* @property {function} getTransmitToken
49-
* @property {function} errorResponse
38+
* @property {string} param - The name of the request body or query parameter to check for the CSRF token.
39+
* @property {string} value - The name of the local variable to set the CSRF token to for use in templates.
40+
* @property {Object} storage - The storage configuration for the CSRF token.
41+
* @property {string} storage.type - The type of storage to use for the CSRF token ('session' or 'cookie').
42+
* @property {Object} storage.options - Options to pass to the cookie storage (e.g., `domain`, `path`, `secure`, `httpOnly`).
43+
* @property {number} tokenLength - The length of the CSRF token in bytes.
44+
* @property {function} getToken - A function to get the CSRF token from the request.
45+
* @property {function} clearToken - A function to clear the CSRF token from the request and/or response.
46+
* @property {function} protectCondition - A function to determine if CSRF protection should be applied.
47+
* @property {function} getTransmitToken - A function to retrieve the CSRF token from the request.
48+
* @property {function} errorResponse - A function to call when the CSRF token is invalid.
5049
*/
5150

5251
/**
52+
* @private
5353
* @type {Csrf}
5454
*/
5555
let csrf = {
@@ -60,14 +60,28 @@ let csrf = {
6060
options: {}
6161
},
6262
tokenLength: 16,
63+
/**
64+
* Gets the CSRF token from the request, based on the configured storage type.
65+
* @param {Object} req - The Express request object.
66+
* @returns {string|null} The CSRF token, or null if not found.
67+
*/
6368
getToken: (req) => {
6469
switch (csrf.storage.type) {
6570
case CONSTANT.STORAGE.SESSION:
6671
return req.session[csrf.param] || null;
6772
case CONSTANT.STORAGE.COOKIE:
6873
return req.cookies[csrf.param] || null;
74+
default:
75+
console.warn("CSRF: Unknown storage type:", csrf.storage.type);
76+
return null; // Or throw an error
6977
}
7078
},
79+
/**
80+
* Clears the CSRF token from the request and/or response, based on the configured storage type.
81+
* @param {Object} req - The Express request object.
82+
* @param {Object} res - The Express response object.
83+
* @returns {void}
84+
*/
7185
clearToken: (req, res) => {
7286
switch (csrf.storage.type) {
7387
case CONSTANT.STORAGE.SESSION:
@@ -76,14 +90,34 @@ let csrf = {
7690
case CONSTANT.STORAGE.COOKIE:
7791
res.clearCookie(csrf.param)
7892
break
93+
default:
94+
console.warn("CSRF: Unknown storage type:", csrf.storage.type);
95+
break;
7996
}
8097
},
98+
/**
99+
* Determines if CSRF protection should be applied to the request.
100+
* @param {Object} req - The Express request object.
101+
* @returns {boolean} True if CSRF protection should be applied, false otherwise.
102+
*/
81103
protectCondition: (req) => {
82104
return req.method === 'POST' || req.method === 'PUT' || req.method === 'DELETE'
83105
},
106+
/**
107+
* Gets the CSRF token from the request headers or body.
108+
* @param {Object} req - The Express request object.
109+
* @returns {string|null|undefined} The CSRF token, or undefined if not found.
110+
*/
84111
getTransmitToken: (req) => {
85112
return req.body._csrf || req.headers['csrf-token'];
86113
},
114+
/**
115+
* Sends an error response when the CSRF token is invalid.
116+
* @param {Object} req - The Express request object.
117+
* @param {Object} res - The Express response object.
118+
* @param {function} next - The Express next function.
119+
* @returns {void}
120+
*/
87121
errorResponse: (req, res, next) => {
88122
res.status(403).send('CSRF token invalid');
89123
}
@@ -92,10 +126,16 @@ let csrf = {
92126
module.exports = {
93127
CONSTANT,
94128
/**
95-
* Generate CSRF protection middleware
96-
*
97-
* @param {CsrfConfig} [csrfConfig]
98-
* @returns {function} Middleware function
129+
* Generates CSRF protection middleware.
130+
*
131+
* @param {CsrfConfig} [csrfConfig] - The CSRF configuration object.
132+
* @returns {function} An Express middleware function.
133+
*
134+
* @example
135+
* const csrfMiddleware = csrf.generate({
136+
* storage: { type: 'cookie', options: { secure: true } }
137+
* });
138+
* app.use(csrfMiddleware);
99139
*/
100140
generate: (csrfConfig = {
101141
tokenLength: 16,
@@ -109,8 +149,18 @@ module.exports = {
109149
protectCondition: csrf.protectCondition,
110150
getTransmitToken: csrf.getTransmitToken
111151
}) => {
152+
//@ts-ignore
112153
csrf = { ...csrf, ...csrfConfig };
113154
return async (req, res, next) => {
155+
if (csrfConfig.storage.type === CONSTANT.STORAGE.SESSION && typeof req.session === 'undefined') {
156+
throw new Error('CSRF: Session middleware (e.g., express-session) is required when using session storage.');
157+
}
158+
159+
// Check for cookie middleware
160+
if (csrfConfig.storage.type === CONSTANT.STORAGE.COOKIE && typeof req.cookies === 'undefined') {
161+
throw new Error('CSRF: cookie-parser middleware is required when using cookie storage.');
162+
}
163+
114164
try {
115165
if (!csrf.getToken(req)) {
116166
const token = crypto.randomBytes(csrf.tokenLength).toString('hex');
@@ -122,6 +172,9 @@ module.exports = {
122172
case CONSTANT.STORAGE.COOKIE:
123173
res.cookie(csrf.param, token, csrf.storage.options);
124174
break;
175+
default:
176+
console.warn("CSRF: Unknown storage type:", csrf.storage.type);
177+
break;
125178
}
126179
req.currentCsrfToken = token
127180
}
@@ -133,22 +186,32 @@ module.exports = {
133186
}
134187
},
135188
/**
136-
* Set CSRF token in response locals
137-
*
138-
* @param {Object} req
139-
* @param {Object} res
140-
* @param {function} next
189+
* Sets the CSRF token in the response locals, making it available to templates.
190+
*
191+
* @param {Object} req - The Express request object.
192+
* @param {Object} res - The Express response object.
193+
* @param {function} next - The Express next function.
194+
* @returns {void}
195+
*
196+
* @example
197+
* app.use(csrf.setTokenLocalsParam);
141198
*/
142199
setTokenLocalsParam: (req, res, next) => {
143200
res.locals[csrf.value] = csrf.getToken(req) || req.currentCsrfToken
144201
next();
145202
},
146203
/**
147-
* CSRF protection middleware
148-
*
149-
* @param {Object} req
150-
* @param {Object} res
151-
* @param {function} next
204+
* Protects routes from CSRF attacks by validating the CSRF token.
205+
*
206+
* @param {Object} req - The Express request object.
207+
* @param {Object} res - The Express response object.
208+
* @param {function} next - The Express next function.
209+
* @returns {void}
210+
*
211+
* @example
212+
* app.post('/form', csrf.protect, (req, res) => {
213+
* // ... handle form submission
214+
* });
152215
*/
153216
protect: (req, res, next) => {
154217
try {

package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
{
22
"name": "@knfs-tech/csrf",
3-
"version": "1.0.3",
3+
"version": "1.1.1",
44
"description": "Cross-site request forgery module",
55
"main": "./lib/cjs/index.js",
66
"module": "./lib/esm/index.js",
7+
"types": "./types/index.d.ts",
78
"scripts": {
89
"test": "jest --runInBand --force-exit --detectOpenHandles"
910
},
1011
"files": [
11-
"lib"
12+
"lib",
13+
"types"
1214
],
1315
"repository": {
1416
"type": "git",
@@ -34,6 +36,8 @@
3436
"express-session": "^1.18.0",
3537
"jest": "^29.7.0",
3638
"sinon": "^18.0.0",
37-
"supertest": "^7.0.0"
39+
"supertest": "^7.0.0",
40+
"typescript": "^5.8.3",
41+
"undici-types": "^7.8.0"
3842
}
3943
}

tsconfig.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2020",
4+
"module": "ESNext",
5+
"declaration": true,
6+
"emitDeclarationOnly": true,
7+
"outDir": "types",
8+
"allowJs": true,
9+
"checkJs": true,
10+
"moduleResolution": "node",
11+
"esModuleInterop": true,
12+
"skipLibCheck": true
13+
},
14+
"include": [
15+
"lib/cjs"
16+
]
17+
}

types/index.d.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
declare namespace _exports {
2+
export { CsrfConfig, Csrf };
3+
}
4+
declare namespace _exports {
5+
export { CONSTANT };
6+
export function generate(csrfConfig?: CsrfConfig): Function;
7+
export function setTokenLocalsParam(req: any, res: any, next: Function): void;
8+
export function protect(req: any, res: any, next: Function): void;
9+
}
10+
export = _exports;
11+
type CsrfConfig = {
12+
/**
13+
* - The length of the CSRF token in bytes.
14+
*/
15+
tokenLength?: number;
16+
/**
17+
* - The storage configuration for the CSRF token.
18+
*/
19+
storage?: {
20+
type?: string;
21+
options?: any;
22+
};
23+
/**
24+
* - The name of the request body or query parameter to check for the CSRF token.
25+
*/
26+
param?: string;
27+
/**
28+
* - The name of the local variable to set the CSRF token to for use in templates.
29+
*/
30+
value?: string;
31+
/**
32+
* - A function to call when the CSRF token is invalid. It should accept `(req, res, next)` and send an appropriate error response.
33+
*/
34+
errorResponse?: Function;
35+
/**
36+
* - A function to determine if CSRF protection should be applied. It should accept `(req)` and return `true` to protect, `false` to skip.
37+
*/
38+
protectCondition?: Function;
39+
/**
40+
* - A function to retrieve the CSRF token from the request. It should accept `(req)` and return the token string or `null`.
41+
*/
42+
getTransmitToken?: Function;
43+
};
44+
type Csrf = {
45+
/**
46+
* - The name of the request body or query parameter to check for the CSRF token.
47+
*/
48+
param: string;
49+
/**
50+
* - The name of the local variable to set the CSRF token to for use in templates.
51+
*/
52+
value: string;
53+
/**
54+
* - The storage configuration for the CSRF token.
55+
*/
56+
storage: {
57+
type: string;
58+
options: any;
59+
};
60+
/**
61+
* - The length of the CSRF token in bytes.
62+
*/
63+
tokenLength: number;
64+
/**
65+
* - A function to get the CSRF token from the request.
66+
*/
67+
getToken: Function;
68+
/**
69+
* - A function to clear the CSRF token from the request and/or response.
70+
*/
71+
clearToken: Function;
72+
/**
73+
* - A function to determine if CSRF protection should be applied.
74+
*/
75+
protectCondition: Function;
76+
/**
77+
* - A function to retrieve the CSRF token from the request.
78+
*/
79+
getTransmitToken: Function;
80+
/**
81+
* - A function to call when the CSRF token is invalid.
82+
*/
83+
errorResponse: Function;
84+
};
85+
/**
86+
* @typedef {Object} CsrfConfig
87+
* @property {number} [tokenLength=16] - The length of the CSRF token in bytes.
88+
* @property {Object} [storage] - The storage configuration for the CSRF token.
89+
* @property {string} [storage.type='session'] - The type of storage to use for the CSRF token ('session' or 'cookie').
90+
* @property {Object} [storage.options={}] - Options to pass to the cookie storage (e.g., `domain`, `path`, `secure`, `httpOnly`). Only applicable when `storage.type` is 'cookie'.
91+
* @property {string} [param='_csrf'] - The name of the request body or query parameter to check for the CSRF token.
92+
* @property {string} [value='csrfToken'] - The name of the local variable to set the CSRF token to for use in templates.
93+
* @property {function} [errorResponse] - A function to call when the CSRF token is invalid. It should accept `(req, res, next)` and send an appropriate error response.
94+
* @property {function} [protectCondition] - A function to determine if CSRF protection should be applied. It should accept `(req)` and return `true` to protect, `false` to skip.
95+
* @property {function} [getTransmitToken] - A function to retrieve the CSRF token from the request. It should accept `(req)` and return the token string or `null`.
96+
*/
97+
/**
98+
* @type {Object}
99+
* @property {Object} STORAGE
100+
* @property {string} STORAGE.SESSION - String constant representing session storage type.
101+
* @property {string} STORAGE.COOKIE - String constant representing cookie storage type.
102+
*/
103+
declare const CONSTANT: any;

0 commit comments

Comments
 (0)