Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions library/sinks/Postgres.idor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,104 @@ t.test("IDOR protection for Postgres (pg)", async (t) => {
);
}
});

await t.test(
"allows SELECT with table-valued function (generate_series) in subquery",
async () => {
t.same(
(
await runWithContext(context, () => {
return client.query(
"SELECT petname, (SELECT COUNT(*) FROM generate_series(1, 5)) AS series_count FROM cats_pg_idor WHERE tenant_id = $1",
["org_123"]
);
})
).rows,
[]
);
}
);

await t.test(
"allows SELECT with jsonb_array_elements table-valued function in subquery",
async () => {
t.same(
(
await runWithContext(context, () => {
return client.query(
"SELECT petname, (SELECT string_agg(elem #>> '{}', ' | ') FROM jsonb_array_elements('[1,2,3]'::jsonb) AS elem) AS summary FROM cats_pg_idor WHERE tenant_id = $1",
["org_123"]
);
})
).rows,
[]
);
}
);

await t.test(
"allows SELECT from table-valued function only (no real table)",
async () => {
const result = await runWithContext(context, () => {
return client.query("SELECT * FROM generate_series(1, 5) AS s");
});
t.equal(result.rows.length, 5);
}
);

await t.test(
"allows SELECT joining real table with table-valued function and tenant filter",
async () => {
t.same(
(
await runWithContext(context, () => {
return client.query(
"SELECT c.petname, s FROM cats_pg_idor c, generate_series(1, 3) AS s WHERE c.tenant_id = $1",
["org_123"]
);
})
).rows,
[]
);
}
);

await t.test(
"blocks SELECT joining real table with table-valued function without tenant filter",
async () => {
const error = await t.rejects(async () => {
await runWithContext(context, () => {
return client.query(
"SELECT c.petname, s FROM cats_pg_idor c, generate_series(1, 3) AS s"
);
});
});

if (error instanceof Error) {
t.match(
error.message,
"Zen IDOR protection: query on table 'cats_pg_idor' is missing a filter on column 'tenant_id'"
);
}
}
);

await t.test(
"allows SELECT with unnest table-valued function in subquery",
async () => {
t.same(
(
await runWithContext(context, () => {
return client.query(
"SELECT petname, (SELECT COUNT(*) FROM unnest(ARRAY[1, 2, 3])) AS n FROM cats_pg_idor WHERE tenant_id = $1",
["org_123"]
);
})
).rows,
[]
);
}
);
} finally {
await client.end();
}
Expand Down
14 changes: 7 additions & 7 deletions library/vulnerabilities/sql-injection/detectSQLInjection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,31 +297,31 @@ t.test("It does not match GROUP keyword", async () => {

t.test("It works with non-UTF-8 characters and emojis", async () => {
isSqlInjection(
"SELECT * FROM users WHERE id = 'a \udce9'\nOR 1=1 --'",
"SELECT * FROM users WHERE id = 'a \udce9'\nOR 1=1 -- '",
"a \udce9'\nOR 1=1 --"
);
isSqlInjection(
"SELECT * FROM users WHERE id = 'a \uD800'\nOR 1=1 --'",
"SELECT * FROM users WHERE id = 'a \uD800'\nOR 1=1 -- '",
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes are needed because in MySQL a space is mandatory for this to be a comment, and this was fixed in the version of the SQL parser we use.

"a \uD800'\nOR 1=1 --"
);
isSqlInjection(
"SELECT * FROM users WHERE id = 'a \uDFFF'\nOR 1=1 --'",
"SELECT * FROM users WHERE id = 'a \uDFFF'\nOR 1=1 -- '",
"a \uDFFF'\nOR 1=1 --"
);
isSqlInjection(
"SELECT * FROM users WHERE id = 'a \uDFFF\uDFFF'\nOR 1=1 --'",
"SELECT * FROM users WHERE id = 'a \uDFFF\uDFFF'\nOR 1=1 -- '",
"a \uDFFF\uDFFF'\nOR 1=1 --"
);
isSqlInjection(
"SELECT * FROM users WHERE id = 'a \uDFAB'\nOR 1=1 --'",
"SELECT * FROM users WHERE id = 'a \uDFAB'\nOR 1=1 -- '",
"a \uDFAB'\nOR 1=1 --"
);
isSqlInjection(
"SELECT * FROM users WHERE id = 'a 😀'\nOR 1=1 --'",
"SELECT * FROM users WHERE id = 'a 😀'\nOR 1=1 -- '",
"a 😀'\nOR 1=1 --"
);
isSqlInjection(
"SELECT * FROM users WHERE id = 'a 🛡️'\nOR 1=1 --'",
"SELECT * FROM users WHERE id = 'a 🛡️'\nOR 1=1 -- '",
"a 🛡️'\nOR 1=1 --"
);

Expand Down
2 changes: 1 addition & 1 deletion scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ async function execAsyncWithPipe(command, options) {
}

// Zen Internals configuration
const INTERNALS_VERSION = "v0.1.61";
const INTERNALS_VERSION = "v0.1.65";
const INTERNALS_URL = `https://github.com/AikidoSec/zen-internals/releases/download/${INTERNALS_VERSION}`;
// ---

Expand Down
Loading