🙊 escape sql strings

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-03-28 15:57:13 +01:00
parent 40b98f36a4
commit ecf68463c9
16 changed files with 104 additions and 56 deletions

View File

@@ -1,4 +1,5 @@
import { Widget } from '@/components/widget';
import { escape } from 'sqlstring';
import { db, getEvents } from '@openpanel/db';
@@ -21,7 +22,7 @@ export default async function EventConversionsListServer({ projectId }: Props) {
}
const events = await getEvents(
`SELECT * FROM events WHERE project_id = '${projectId}' AND name IN (${conversions.map((c) => `'${c.name}'`).join(', ')}) ORDER BY created_at DESC LIMIT 20;`,
`SELECT * FROM events WHERE project_id = ${escape(projectId)} AND name IN (${conversions.map((c) => escape(c.name)).join(', ')}) ORDER BY created_at DESC LIMIT 20;`,
{
profile: true,
meta: true,

View File

@@ -5,6 +5,7 @@ import {
} from '@/components/ui/tooltip';
import { Widget, WidgetBody, WidgetHead } from '@/components/widget';
import { cn } from '@/utils/cn';
import { escape } from 'sqlstring';
import { chQuery } from '@openpanel/db';
@@ -20,7 +21,7 @@ export default async function ProfileLastSeenServer({ projectId }: Props) {
// Days since last event from users
// group by days
const res = await chQuery<Row>(
`SELECT age('days',created_at, now()) as days, count(distinct profile_id) as count FROM events where project_id = '${projectId}' group by days order by days ASC`
`SELECT age('days',created_at, now()) as days, count(distinct profile_id) as count FROM events where project_id = ${escape(projectId)} group by days order by days ASC`
);
const take = 18;

View File

@@ -4,6 +4,7 @@ import { Widget, WidgetHead } from '@/components/widget';
import { WidgetTable } from '@/components/widget-table';
import { getProfileName } from '@/utils/getters';
import Link from 'next/link';
import { escape } from 'sqlstring';
import { chQuery, getProfiles } from '@openpanel/db';
@@ -19,7 +20,7 @@ export default async function ProfileTopServer({
// Days since last event from users
// group by days
const res = await chQuery<{ profile_id: string; count: number }>(
`SELECT profile_id, count(*) as count from events where profile_id != '' and project_id = '${projectId}' group by profile_id order by count() DESC LIMIT 10`
`SELECT profile_id, count(*) as count from events where profile_id != '' and project_id = ${escape(projectId)} group by profile_id order by count() DESC LIMIT 10`
);
const profiles = await getProfiles({ ids: res.map((r) => r.profile_id) });
const list = res.map((item) => {

View File

@@ -1,5 +1,6 @@
import { shortNumber } from '@/hooks/useNumerFormatter';
import Link from 'next/link';
import { escape } from 'sqlstring';
import type { IServiceProject } from '@openpanel/db';
import { chQuery } from '@openpanel/db';
@@ -13,19 +14,19 @@ export async function ProjectCard({
}: IServiceProject) {
const [chart, [data]] = await Promise.all([
chQuery<{ value: number; date: string }>(
`SELECT countDistinct(profile_id) as value, toStartOfDay(created_at) as date FROM events WHERE project_id = '${id}' AND name = 'session_start' AND created_at >= now() - interval '1 month' GROUP BY date ORDER BY date ASC`
`SELECT countDistinct(profile_id) as value, toStartOfDay(created_at) as date FROM events WHERE project_id = ${escape(id)} AND name = 'session_start' AND created_at >= now() - interval '1 month' GROUP BY date ORDER BY date ASC`
),
chQuery<{ total: number; month: number; day: number }>(
`
SELECT
(
SELECT count(DISTINCT profile_id) as count FROM events WHERE project_id = '${id}'
SELECT count(DISTINCT profile_id) as count FROM events WHERE project_id = ${escape(id)}
) as total,
(
SELECT count(DISTINCT profile_id) as count FROM events WHERE project_id = '${id}' AND created_at >= now() - interval '1 month'
SELECT count(DISTINCT profile_id) as count FROM events WHERE project_id = ${escape(id)} AND created_at >= now() - interval '1 month'
) as month,
(
SELECT count(DISTINCT profile_id) as count FROM events WHERE project_id = '${id}' AND created_at >= now() - interval '1 day'
SELECT count(DISTINCT profile_id) as count FROM events WHERE project_id = ${escape(id)} AND created_at >= now() - interval '1 day'
) as day
`
),

View File

@@ -2,6 +2,7 @@ import { round } from '@/utils/math';
import { subDays } from 'date-fns';
import * as mathjs from 'mathjs';
import { repeat, reverse, sort } from 'ramda';
import { escape } from 'sqlstring';
import { alphabetIds, NOT_SET_VALUE } from '@openpanel/constants';
import {
@@ -430,7 +431,7 @@ export async function getFunnelData({ projectId, ...payload }: IChartInput) {
const funnels = payload.events.map((event) => {
const { sb, getWhere } = createSqlBuilder();
sb.where = getEventFiltersWhereClause(event.filters);
sb.where.name = `name = '${event.name}'`;
sb.where.name = `name = ${escape(event.name)}`;
return getWhere().replace('WHERE ', '');
});
@@ -438,7 +439,7 @@ export async function getFunnelData({ projectId, ...payload }: IChartInput) {
session_id,
windowFunnel(6048000000000000,'strict_increase')(toUnixTimestamp(created_at), ${funnels.join(', ')}) AS level
FROM events
WHERE (project_id = '${projectId}' AND created_at >= '${formatClickhouseDate(startDate)}') AND (created_at <= '${formatClickhouseDate(endDate)}')
WHERE (project_id = ${escape(projectId)} AND created_at >= '${formatClickhouseDate(startDate)}') AND (created_at <= '${formatClickhouseDate(endDate)}')
GROUP BY session_id`;
const sql = `SELECT level, count() AS count FROM (${innerSql}) GROUP BY level ORDER BY level DESC`;
@@ -446,7 +447,7 @@ export async function getFunnelData({ projectId, ...payload }: IChartInput) {
const [funnelRes, sessionRes] = await Promise.all([
chQuery<{ level: number; count: number }>(sql),
chQuery<{ count: number }>(
`SELECT count(name) as count FROM events WHERE project_id = '${projectId}' AND name = 'session_start' AND (created_at >= '${formatClickhouseDate(startDate)}') AND (created_at <= '${formatClickhouseDate(endDate)}')`
`SELECT count(name) as count FROM events WHERE project_id = ${escape(projectId)} AND name = 'session_start' AND (created_at >= '${formatClickhouseDate(startDate)}') AND (created_at <= '${formatClickhouseDate(endDate)}')`
),
]);

View File

@@ -5,6 +5,7 @@ import {
} from '@/trpc/api/trpc';
import { average, max, min, round, sum } from '@/utils/math';
import { flatten, map, pipe, prop, sort, uniq } from 'ramda';
import { escape } from 'sqlstring';
import { z } from 'zod';
import { chQuery, createSqlBuilder } from '@openpanel/db';
@@ -60,7 +61,7 @@ export const chartRouter = createTRPCRouter({
.input(z.object({ projectId: z.string() }))
.query(async ({ input: { projectId } }) => {
const events = await chQuery<{ name: string }>(
`SELECT DISTINCT name FROM events WHERE project_id = '${projectId}'`
`SELECT DISTINCT name FROM events WHERE project_id = ${escape(projectId)}`
);
return [
@@ -76,8 +77,8 @@ export const chartRouter = createTRPCRouter({
.query(async ({ input: { projectId, event } }) => {
const events = await chQuery<{ keys: string[] }>(
`SELECT distinct mapKeys(properties) as keys from events where ${
event && event !== '*' ? `name = '${event}' AND ` : ''
} project_id = '${projectId}';`
event && event !== '*' ? `name = ${escape(event)} AND ` : ''
} project_id = ${escape(projectId)};`
);
const properties = events
@@ -122,14 +123,14 @@ export const chartRouter = createTRPCRouter({
)
.query(async ({ input: { event, property, projectId } }) => {
const { sb, getSql } = createSqlBuilder();
sb.where.project_id = `project_id = '${projectId}'`;
sb.where.project_id = `project_id = ${escape(projectId)}`;
if (event !== '*') {
sb.where.event = `name = '${event}'`;
sb.where.event = `name = ${escape(event)}`;
}
if (property.startsWith('properties.')) {
sb.select.values = `distinct mapValues(mapExtractKeyLike(properties, '${property
.replace(/^properties\./, '')
.replace('.*.', '.%.')}')) as values`;
sb.select.values = `distinct mapValues(mapExtractKeyLike(properties, ${escape(
property.replace(/^properties\./, '').replace('.*.', '.%.')
)})) as values`;
} else {
sb.select.values = `distinct ${property} as values`;
}

View File

@@ -4,6 +4,7 @@ import {
publicProcedure,
} from '@/trpc/api/trpc';
import { flatten, map, pipe, prop, sort, uniq } from 'ramda';
import { escape } from 'sqlstring';
import { z } from 'zod';
import { chQuery, createSqlBuilder } from '@openpanel/db';
@@ -13,7 +14,7 @@ export const profileRouter = createTRPCRouter({
.input(z.object({ projectId: z.string() }))
.query(async ({ input: { projectId } }) => {
const events = await chQuery<{ keys: string[] }>(
`SELECT distinct mapKeys(properties) as keys from profiles where project_id = '${projectId}';`
`SELECT distinct mapKeys(properties) as keys from profiles where project_id = ${escape(projectId)};`
);
const properties = events
@@ -40,11 +41,11 @@ export const profileRouter = createTRPCRouter({
.query(async ({ input: { property, projectId } }) => {
const { sb, getSql } = createSqlBuilder();
sb.from = 'profiles';
sb.where.project_id = `project_id = '${projectId}'`;
sb.where.project_id = `project_id = ${escape(projectId)}`;
if (property.startsWith('properties.')) {
sb.select.values = `distinct mapValues(mapExtractKeyLike(properties, '${property
.replace(/^properties\./, '')
.replace('.*.', '.%.')}')) as values`;
sb.select.values = `distinct mapValues(mapExtractKeyLike(properties, ${escape(
property.replace(/^properties\./, '').replace('.*.', '.%.')
)})) as values`;
} else {
sb.select.values = `${property} as values`;
}