UNPKG

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
"use strict"; 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;