@aikidosec/firewall
Version:
Zen by Aikido is an embedded Application Firewall that autonomously protects Node.js apps against common and critical attacks, provides rate limiting, detects malicious traffic (including bots), and more.
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;
;