database-proxy
Version:
Through a set of access control rules configuration database access to realize the client directly access the database via HTTP.
185 lines (184 loc) • 5.04 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SecurityUtil = void 0;
const types_1 = require("../types");
/**
* 安全工具
*/
class SecurityUtil {
// 检查字段名是否合法:data field, query field
static checkField(name) {
if (this.isQueryOrLogicOperator(name)) {
return true;
}
const black_list = [
' ',
'#',
// ' or ',
';',
`'`,
`"`,
'`',
'-',
'/',
'*',
'\\',
'+',
'%',
];
if (this.containChars(name, black_list)) {
return false;
}
return true;
}
// 检查字段名是否合法:data field, query field
static checkProjection(name) {
const black_list = [
'#',
' or ',
';',
`'`,
`"`,
'`',
'+',
'-',
'/',
'\\',
'%',
];
if (this.containChars(name, black_list)) {
return false;
}
return true;
}
/**
* 递归收集 query 中的字段列表,去除操作符(逻辑、查询操作符)
* @param query 请求 query 对象
* @returns
*/
static resolveFieldFromQuery(query) {
const sets = [];
for (const key in query) {
if (this.isQueryOrLogicOperator(key)) {
const ret = this.resolveFieldFromQuery(query[key]);
sets.push(...ret);
}
else {
sets.push(key);
}
}
return sets;
}
/**
* 递归收集 data 中的字段列表,去除更新操作符
* @param data
*/
static resolveFieldFromData(data) {
const sets = [];
for (const key in data) {
if (this.isUpdateOperator(key)) {
const ret = this.resolveFieldFromData(data[key]);
sets.push(...ret);
}
else {
sets.push(key);
}
}
return sets;
}
/**
* 判断字段列是否都在白名单内
* @param input_fields [string] 输入字段列表
* @param allow_fields [string] 允许的字段列表
*/
static isAllowedFields(input_fields, allow_fields) {
for (const fd of input_fields) {
if (!allow_fields.includes(fd))
return `the field '${fd}' is NOT allowed]`;
}
return null;
}
/**
* 检查给定字符串中是否包含指定字符
* @param source 字符串
* @param str_list 字符白名单或黑名单
* @returns
*/
static containChars(source, str_list) {
for (const ch of str_list) {
if (source.indexOf(ch) >= 0)
return true;
}
return false;
}
// 是否为逻辑操作符
static isLogicOperator(key) {
const keys = Object.keys(types_1.LOGIC_COMMANDS).map((k) => types_1.LOGIC_COMMANDS[k]);
return keys.includes(key);
}
// 是否为查询操作符(QUERY_COMMANDS)
static isQueryOperator(key) {
const keys = Object.keys(types_1.QUERY_COMMANDS).map((k) => types_1.QUERY_COMMANDS[k]);
return keys.includes(key);
}
// 是否为查询或逻辑操作符
static isQueryOrLogicOperator(key) {
return this.isLogicOperator(key) || this.isQueryOperator(key);
}
// 是否存在更新操作符
static hasUpdateOperator(data) {
const OPTRS = Object.values(types_1.UPDATE_COMMANDS);
let has = false;
const checkMixed = (objs) => {
if (typeof objs !== 'object')
return;
for (const key in objs) {
if (OPTRS.includes(key)) {
has = true;
}
else if (typeof objs[key] === 'object') {
checkMixed(objs[key]);
}
}
};
checkMixed(data);
return has;
}
// 是否为更新操作符
static isUpdateOperator(key) {
const keys = Object.keys(types_1.UPDATE_COMMANDS).map((k) => types_1.UPDATE_COMMANDS[k]);
return keys.includes(key);
}
/**
* 将带操作符的 data 对象平铺
*
data: {
title: '',
$set: {
content: '',
author: 123
},
$inc: {
age: 1
},
$push: {
grades: 99,
},
}
*/
static flattenData(data = {}) {
const result = {};
for (const key in data) {
if (!this.isUpdateOperator(key)) {
result[key] = data[key];
continue;
}
const obj = data[key];
for (const k in obj) {
result[k] = obj[k];
}
}
return result;
}
}
exports.SecurityUtil = SecurityUtil;