@aikidosec/firewall
Version:
Zen by Aikido is an embedded Web Application Firewall that autonomously protects Node.js apps against common and critical attacks
92 lines (91 loc) • 4.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MySQL2 = void 0;
const Context_1 = require("../agent/Context");
const wrapExport_1 = require("../agent/hooks/wrapExport");
const isPlainObject_1 = require("../helpers/isPlainObject");
const wrap_1 = require("../helpers/wrap");
const checkContextForSqlInjection_1 = require("../vulnerabilities/sql-injection/checkContextForSqlInjection");
const SQLDialectMySQL_1 = require("../vulnerabilities/sql-injection/dialects/SQLDialectMySQL");
class MySQL2 {
constructor() {
this.dialect = new SQLDialectMySQL_1.SQLDialectMySQL();
}
inspectQuery(operation, args) {
const context = (0, Context_1.getContext)();
if (!context) {
return undefined;
}
if (args.length > 0) {
if (typeof args[0] === "string" && args[0].length > 0) {
const sql = args[0];
return (0, checkContextForSqlInjection_1.checkContextForSqlInjection)({
operation: operation,
sql: sql,
context: context,
dialect: this.dialect,
});
}
if ((0, isPlainObject_1.isPlainObject)(args[0]) &&
args[0].sql &&
typeof args[0].sql === "string") {
const sql = args[0].sql;
return (0, checkContextForSqlInjection_1.checkContextForSqlInjection)({
operation: operation,
sql: sql,
context: context,
dialect: this.dialect,
});
}
}
return undefined;
}
// This function is copied from the OpenTelemetry MySQL2 instrumentation (Apache 2.0 license)
// https://github.com/open-telemetry/opentelemetry-js-contrib/blob/21e1331a29e06092fb1e460ca99e0c28b1b57ac4/plugins/node/opentelemetry-instrumentation-mysql2/src/utils.ts#L150
getConnectionPrototypeToInstrument(connection) {
const connectionPrototype = connection.prototype;
const basePrototype = Object.getPrototypeOf(connectionPrototype);
// mysql2@3.11.5 included a refactoring, where most code was moved out of the `Connection` class and into a shared base
// so we need to instrument that instead, see https://github.com/sidorares/node-mysql2/pull/3081
// This checks if the functions we're instrumenting are there on the base - we cannot use the presence of a base
// prototype since EventEmitter is the base for mysql2@<=3.11.4
if (typeof (basePrototype === null || basePrototype === void 0 ? void 0 : basePrototype.query) === "function" &&
typeof (basePrototype === null || basePrototype === void 0 ? void 0 : basePrototype.execute) === "function") {
return basePrototype;
}
// otherwise instrument the connection directly.
return connectionPrototype;
}
wrap(hooks) {
const wrapConnection = (exports, pkgInfo, isPromise) => {
const connectionPrototype = this.getConnectionPrototypeToInstrument(isPromise ? exports.PromiseConnection : exports.Connection);
if (!(0, wrap_1.isWrapped)(connectionPrototype.query)) {
// Wrap connection.query
(0, wrapExport_1.wrapExport)(connectionPrototype, "query", pkgInfo, {
kind: "sql_op",
inspectArgs: (args) => this.inspectQuery("mysql2.query", args),
});
}
if (!(0, wrap_1.isWrapped)(connectionPrototype.execute)) {
// Wrap connection.execute
(0, wrapExport_1.wrapExport)(connectionPrototype, "execute", pkgInfo, {
kind: "sql_op",
inspectArgs: (args) => this.inspectQuery("mysql2.execute", args),
});
}
};
const pkg = hooks.addPackage("mysql2");
// For all versions of mysql2 newer than 3.0.0
pkg
.withVersion("^3.0.0")
.onRequire((exports, pkgInfo) => wrapConnection(exports, pkgInfo, false));
// For all versions of mysql2 newer than / equal 3.11.5
// Reason: https://github.com/sidorares/node-mysql2/pull/3081
pkg
.withVersion("^3.11.5")
.onFileRequire("promise.js", (exports, pkgInfo) => {
return wrapConnection(exports, pkgInfo, true);
});
}
}
exports.MySQL2 = MySQL2;