Security Context with several elements on CubeJS - javascript

Currently working on Cube.JS and I'm building a cube within I want to restrict the data to an user based on his gender. So I end up with :
cube(`Data`, {
sql: `select * from my_table where ${SECURITY_CONTEXT.user_gender.filter(user_gender)}`,
...
as explained here
But now I want to restrict the data to an user based on his gender AND his age, how should I proceed ? I was thinking about something like that...
cube(`Data`, {
sql: `select * from my_table where ${SECURITY_CONTEXT.user_gender.user_age.filter(user_gender,user_age)}`, //????
...
...but it seems weird to put two "attributes" .user_gender.user_age.filter to the SECURITY_CONTEXT
I hope someone has already tried something like that.
Thank you!

You'll need to use SECURITY_CONTEXT twice:
cube(`Data`, {
sql: `select * from my_table where ${SECURITY_CONTEXT.user_gender.filter(user_gender)} AND ${SECURITY_CONTEXT.user_age.filter(user_age)}`,
...

Related

How can I extend an SQL query in a variable?

I am testing possible SQL injections on my DB, and I am running a simple function to get results which a user should not be getting. The return value is correct based on id, however, the rest of the query is completely being ignored.
I want to return all the data from the data table.
Is there something wrong in my syntax?
Here is my implementation:
function test(id) {
db.query("SELECT * FROM users WHERE id = ?", [id], (err, result) => {
console.log(result[0]);
});
}
const id = "122 UNION SELECT * FROM data";
test(id);
This looks like nodejs Javascript with the npm mysql driver package. And, I guess your id column is defined as an INT or BIGINT, not as some kind of text string.
The way you use the .query() method is the correct way to prevent SQL injection. It's parameterized. That means each parameter in the SQL is represented by a ? placeholder. The second argument to .query() is an array of parameter values to substitute for the placeholders. For your use case the driver generates a query looking like this.
SELECT * FROM users WHERE id = '122 UNION SELECT * FROM data'
and passes it to the MySQL server. The server then takes the string you passed and attempts to interpret it as a number. Due to a quirk in MySQL, it interprets your '122 UNION SELECT * FROM data' string as the number 122, and so looks up WHERE id = 122. (MySQL coerces strings to integers by looking for a leading number. So 123RedLight gives 123, and Hello gives 0. It can be confusing. Other makes and models of RDBMS throw errors when given strings where they expect integers.)
It correctly ignores the rest of your string.
If you wanted to make your code vulnerable to SQL injection (you do not want to do that!) you would write
function test(id) { /* danger: sql injection in next line */
db.query("SELECT * FROM users WHERE id = " + id, (err, result) => { /* wrong ! */
console.log(result[0]);
});
}
This would send
SELECT * FROM users WHERE id = 122 UNION SELECT * FROM data
to the server, and give you your data leak.
You can't do it this way. In fact, not being able to do this is the WHOLE POINT of parameterized queries. It prevents an attacker from giving you a string like 122; DROP Table users; as the input.

Sequelize Many to Many search using OR

The main idea for my code is to create a search field which I have two many to many fields, and I want to bind the search for the many to many table
I want to select the modelos.nome_modelo_produto field that is inside the table modelos, but never works, because the sequelize dont recognizes the field, even after I mentioned the relationship between the tables:
Modelo.belongsToMany(Produto, {through: 'ProdutosModelos'});
Produto.belongsToMany(Modelo, {through: 'ProdutosModelos'});
And the idea for the search is this...
Table.findAll({
where: {
[Db.Sequelize.Op.or]: [
{'nome_produto': Db.sequelize.where(Db.sequelize.fn('LOWER', Db.sequelize.col('nome_produto')), 'LIKE', '%' + req.query.search + '%')},
{'$modelos.nome_modelo_produto$': Db.sequelize.where(Db.sequelize.fn('LOWER',Db.sequelize.col('$modelos.nome_modelo_produto$')), 'LIKE', '%' + req.query.search + '%')}
]
}
})
But don't works...
Can someone help-me or give-me a way to proceed ?
If anyone have this error, I've checked that when you're using limit or offset tags inside your where there's a annoying error that I could'n solve.
So the tip that I have is to remove the offset and limit, and also rename your table columns to find only by the name, for example, if you have the field tags in the table things, you can rename the tags field to things_tags, if it is a relational field, and when you set the field inside where you can only insert the field name without the table name.

Knex subquery to sum data from 2nd table

I'm trying to write a query using knex to SUM the votes for each question but am not getting the correct sum. I can write the subquery in SQL but can't seem to piece it all together. I am a student and not sure if I'm doing something wrong with Knex or if my underlying logic is wrong. Thanks in advance for any help!
My knex query looks like this
return knex
.from('question')
.select(
'question.id AS question_id',
knex.raw(
`count(DISTINCT vote) AS number_of_votes`, //this returns the number_of_votes for each question_id as expected
),
knex.raw(
`sum(vote.vote) AS sum_of_votes`, //something wrong here... E.g., question_id 1 has 3 down votes so the sum should be -3, however I am getting -9
),
)
.leftJoin('user', 'question.user_id', 'user.id')
.leftJoin('vote', 'question.id', 'vote.question_id')
.groupBy('question.id', 'user.id');
There are 3 tables that look like:
user
id
user_name
question
id
title
body
user_id (FK references user.id)
vote
question_id (FK references question.id)
user_id (FK references user.id)
vote (-1 or 1)
PRIMARY KEY (question_id, user_id)
I did manage to write the query as a stand-alone SQL query and verified that it works as expected. This is what I am trying to accomplish in the above knex query:
SELECT question.id, sum(vote.vote) AS sum_of_votes FROM question LEFT JOIN vote ON question.id = vote.question_id GROUP BY question.id;
So, broadly your SQL query is correct (after fixing a couple of typos) although as #felixmosh points out it has no user information in it: might be tricky to figure out who voted for what! But perhaps you don't need that for your purposes.
Your posted solution will do the trick, but is perhaps not the most efficient query for the job as it involves a subquery and several joins. Here's the SQL it generates:
SELECT "question"."id" AS "question_id",
count(DISTINCT vote) AS number_of_votes,
(
SELECT sum(vote) FROM vote
WHERE question_id = question.id
GROUP BY question_id
) AS sum_of_votes
FROM "question"
LEFT JOIN "user" ON "question"."user_id" = "user"."id"
LEFT JOIN "vote" ON "question"."id" = "vote"."question_id"
GROUP BY "question"."id", "user"."id";
We can take a simpler approach to get the same information. How about this?
SELECT question_id,
count(vote) AS number_of_votes,
sum(vote) AS sum_of_votes
FROM vote
GROUP BY question_id;
This gets all the information you were looking for, without joining any tables or using subqueries. It also avoids DISTINCT, which could lead to incorrectly counting the number of votes. The Knex to generate such a query looks like this:
knex("vote")
.select("question_id")
.count("vote AS number_of_votes")
.sum("vote AS sum_of_votes")
.groupBy("question_id")
You only really need to join tables here if you were looking for further information from those tables (such as the user's name or the question's title).
After hours of trying to figure this out I finally got it. Here is solution:
return knex
.from('question')
.select(
'question.id AS question_id',
knex.raw(
`count(DISTINCT vote) AS number_of_votes`,
),
knex.raw(
`SELECT sum(vote) from vote WHERE question_id = question.id GROUP BY question_id) AS sum_of_votes`
)
.leftJoin('user', 'question.user_id', 'user.id')
.leftJoin('vote', 'question.id', 'vote.question_id')
.groupBy('question.id', 'user.id');

prepared statement / sql-injection preventation on variable from

I read about that prepare statments are a good way to avoid sql injections to databases.
the problem is the Customer wants a quiet variable UI
where he first selects a table, then some contraints consisting of a column and a text.
So basically the (naive) endproduct will look like this:
Select * from %TABLENAME% where %ATTRIBUTENAME% = %VALUE%
Now the question is how to get this secure?
I could, of course, build a prepare Statement solution where I create statements for all tables in advance, but that sounds to me like a pretty stupid idea, because the effort to maintain this would be quiet big (the customer has quiet a few tables).
Any idea how to solve this in a secure manner that is as generic as possible?
You should change your example to
select * from %TABLENAME% where %ATTRIBUTENAME% = ?
So that at least the VALUE can't be used for SQL injection. You could then have validation for TABLENAME and ATTRIBUTENAME against the known tables and columns in your database.
See DatabaseMetaData.getColumns(...) which you might use in your validation at runtime. Or perhaps you might keep a static file, generated at build time, with the valid tables/columns.
You could generate an Enum at build time for every table/column combination? I know jOOQ does this sort of build time java code generation from a db schema... perhaps it can help?
Eg
public enum TableColumn {
CUSTOMER_NAME("customer", "name"), CUSTOMER_ID("customer", "id"),
ORDER_ID("order", "id"), // etc etc
String table;
String column;
public TableColumn(String table, String column) {
// set values
}
}
public List<Row> doSelect(TableColumn tc, Object value) {
String sql = String.format("select * from %s where %s = ?", tc.table, tc.column);
Connection con = getConnection();
try {
PreparedStatement ps = con.prepareStatement(sql);
ps.setObject(1, value);
...

ON DUPLICATE KEY UPDATE with Web sql and variables

I am trying to do this but this is not working :
tx.executeSql('INSERT INTO MOVIE (id, rate) VALUES(?,?) ON DUPLICATE KEY UPDATE rate=VALUES(rate)',[result.id,score]);
My id is an INT NOT NULL UNIQUE and rate an INT.
I think my syntax is wrong... Do you have a solution ?
Thx :)
Anthony.
As stated in the Web SQL Database:
User agents must implement the SQL dialect supported by Sqlite 3.6.19.
So my guess is that you are going to face the same issues you get with Sqlite. It seems that SQLite UPSERT - ON DUPLICATE KEY UPDATE is not supported by Sqlite, so I suggest just trying one of the solutions provided in the answers. Maybe something like this would work:
db.transaction(function (tx) {
tx.executeSql('INSERT OR IGNORE INTO MOVIE VALUES (?, ?)', [result.id,score]);
tx.executeSql('UPDATE MOVIE SET rate = ? WHERE id = ?', [score,result.id]);
});
See demo
By the way, Web SQL Database is deprecated.

Categories

Resources