feat: new crlfFilter appender to prevent log forging

This commit is contained in:
Lam Wei Li 2023-01-17 17:12:18 +08:00
parent 8fd25d55e1
commit e337bcdb8f
No known key found for this signature in database
GPG Key ID: 90F6ABECF080D7BF
3 changed files with 84 additions and 0 deletions

View File

@ -23,6 +23,7 @@ The following appenders are included with log4js. Some require extra dependencie
- [categoryFilter](categoryFilter.md)
- [console](console.md)
- [crlfFilter](crlfFilter.md)
- [dateFile](dateFile.md)
- [file](file.md)
- [fileSync](fileSync.md)

36
docs/crlfFilter.md Normal file
View File

@ -0,0 +1,36 @@
# CRLF Filter
This is not strictly an appender - it wraps around another appender and to prevent log forging by replacing starting the line with a `>` prefix after line-breaks.
## Configuration
- `type` - `"crlfFilter"`
- `appender` - `string | Array<String>` - the name of the appender to filter. (exists for backward compatibility)
- `appenders` - `string | Array<String>` - the names of the appenders to filter.
## Example
```javascript
log4js.configure({
appenders: {
everything: { type: "file", filename: "all-the-logs.log" },
crlfFilter: {
type: "crlfFilter",
appenders: ["everything"],
},
},
categories: {
default: { appenders: ["crlfFilter"], level: "debug" },
},
});
const logger = log4js.getLogger();
logger.info(
"Some debug messages\n[2023-01-17T11:58:38.150] [INFO] default - Log forging"
);
```
```
[2023-01-17T11:58:38.150] [INFO] default - Some debug messages
> [2023-01-17T11:58:38.150] [INFO] default - Log forging
```

View File

@ -0,0 +1,47 @@
const debug = require('debug')('log4js:crlfFilter');
function crlfFilter(appenders) {
return (logEvent) => {
const filteredData = logEvent.data.map((i) => {
if (typeof i.replace === 'function') return i.replace(/\n/g, '\n> '); // add a > prefix
return i;
});
logEvent.data = filteredData;
appenders.forEach((appender) => {
appender(logEvent);
});
};
}
function configure(config, layouts, findAppender) {
const addToSet = function (input, set = new Set()) {
const addAppender = function (appenderName) {
debug(`actual appender is ${appenderName}`);
const appender = findAppender(appenderName);
if (!appender) {
debug(`actual appender "${config.appender}" not found`);
throw new Error(`crlfFilter appender "${config.appender}" not defined`);
} else {
set.add(appender);
}
};
if (input && !Array.isArray(input)) input = [input];
if (input) input.forEach((appenderName) => addAppender(appenderName));
return set;
};
const appenders = new Set();
addToSet(config.appender, appenders);
addToSet(config.appenders, appenders);
if (appenders.size === 0) {
debug(`no appender found in config ${config}`);
throw new Error('crlfFilter appender must have an "appender" defined');
}
return crlfFilter(appenders);
}
module.exports.configure = configure;