Files
jambonz-api-server/lib/models/model.js
Guilherme Rauen 789a0ba3ff Fix SQL Injection Vulnerabilities (#134)
* avoid sql injections

* linter

* fix test using random sid

* add some test cases

* remove tests that don't use the new validation

* add test

* linter

* fix tests

* add test

---------

Co-authored-by: Guilherme Rauen <g.rauen@cognigy.com>
2023-03-29 12:36:51 -04:00

142 lines
4.0 KiB
JavaScript

const Emitter = require('events');
const { v4: uuidv4 } = require('uuid');
const assert = require('assert');
const {getMysqlConnection} = require('../db');
const {DbErrorBadRequest} = require('../utils/errors');
class Model extends Emitter {
constructor() {
super();
}
static getPrimaryKey() {
return this.fields.find((f) => f.primaryKey === true);
}
/**
* check validity of object to be inserted into db
*/
static checkIsInsertable(obj) {
// check all required fields are present
const required = this.fields.filter((f) => f.required === true);
const missing = required.find((f) => !(f.name in obj));
if (missing) throw new DbErrorBadRequest(`missing field ${missing.name}`);
return true;
}
/**
* insert object into the database
*/
static make(obj) {
return new Promise((resolve, reject) => {
const pk = this.getPrimaryKey();
this.checkIsInsertable(obj);
getMysqlConnection((err, conn) => {
if (err) return reject(err);
const uuid = uuidv4();
obj[pk.name] = uuid;
conn.query(`INSERT into ${this.table} SET ?`,
obj,
(err, results, fields) => {
conn.release();
if (err) return reject(err);
resolve(uuid);
});
});
});
}
/**
* delete object from database
*/
static remove(uuid) {
return new Promise((resolve, reject) => {
const pk = this.getPrimaryKey();
getMysqlConnection((err, conn) => {
if (err) return reject(err);
conn.query(`DELETE from ${this.table} WHERE ${pk.name} = ?`, uuid, (err, results) => {
conn.release();
if (err) return reject(err);
resolve(results.affectedRows);
});
});
});
}
/**
* retrieve all objects
*/
static retrieveAll() {
return new Promise((resolve, reject) => {
getMysqlConnection((err, conn) => {
if (err) return reject(err);
conn.query(`SELECT * from ${this.table}`, (err, results, fields) => {
conn.release();
if (err) return reject(err);
resolve(results);
});
});
});
}
/**
* retrieve a specific object
*/
static retrieve(sid) {
return new Promise((resolve, reject) => {
const pk = this.getPrimaryKey();
assert.ok(pk, 'field definitions must include the primary key');
getMysqlConnection((err, conn) => {
if (err) return reject(err);
conn.query(`SELECT * from ${this.table} WHERE ${pk.name} = ?`, sid, (err, results, fields) => {
conn.release();
if (err) return reject(err);
resolve(results);
});
});
});
}
/**
* update an object
*/
static update(sid, obj) {
const pk = this.getPrimaryKey();
assert.ok(pk, 'field definitions must include the primary key');
return new Promise((resolve, reject) => {
if (pk.name in obj) throw new DbErrorBadRequest(`primary key ${pk.name} is immutable`);
getMysqlConnection((err, conn) => {
if (err) return reject(err);
conn.query(`UPDATE ${this.table} SET ? WHERE ${pk.name} = ?`, [obj, sid], (err, results, fields) => {
conn.release();
if (err) return reject(err);
resolve(results.affectedRows);
});
});
});
}
static getForeignKeyReferences(fk, sid) {
return new Promise((resolve, reject) => {
const arr = /(.*)\.(.*)/.exec(fk);
assert.ok(arr, `foreign key must be written as table.column: ${fk}`);
const table = arr[1];
const column = arr[2];
getMysqlConnection((err, conn) => {
if (err) return reject(err);
conn.query(`SELECT COUNT(*) as count from ${table} WHERE ${column} = ?`,
sid, (err, results, fields) => {
conn.release();
if (err) return reject(err);
resolve(results[0].count);
});
});
});
}
}
Model.table = 'subclassResponsibility';
Model.fields = [];
module.exports = Model;