• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

teableio / teable / 15038842505

15 May 2025 07:09AM UTC coverage: 80.629% (-0.4%) from 81.036%
15038842505

Pull #1524

github

web-flow
Merge df4c55b74 into 8d33852c6
Pull Request #1524: feat: base chat UI

7868 of 8372 branches covered (93.98%)

52 of 296 new or added lines in 4 files covered. (17.57%)

55 existing lines in 2 files now uncovered.

37340 of 46311 relevant lines covered (80.63%)

1762.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.3
/apps/nestjs-backend/src/features/base-sql-executor/utils.ts
1
import { BadRequestException } from '@nestjs/common';
2✔
2
import { DriverClient } from '@teable/core';
3
import type { AST } from 'node-sql-parser';
4
import { Parser } from 'node-sql-parser';
5

6
export const validateRoleOperations = (sql: string) => {
2✔
7
  const removeQuotedContent = (sql: string) => {
16✔
8
    return sql.replace(/'[^']*'|"[^"]*"/g, ' ');
16✔
9
  };
16✔
10

11
  const normalizedSql = sql.toLowerCase().replace(/\s+/g, ' ');
16✔
12
  const sqlWithoutQuotes = removeQuotedContent(normalizedSql);
16✔
13

14
  const roleOperationPatterns = [/set\s+role/, /reset\s+role/, /set\s+session/];
16✔
15

16
  for (const pattern of roleOperationPatterns) {
16✔
17
    if (pattern.test(sqlWithoutQuotes)) {
28✔
18
      throw new BadRequestException(`not allowed to execute sql with keyword: ${pattern.source}`);
12✔
19
    }
12✔
20
  }
28✔
21
};
4✔
22

23
const databaseTypeMap = {
2✔
24
  [DriverClient.Pg]: 'postgresql',
2✔
25
  [DriverClient.Sqlite]: 'sqlite',
2✔
26
};
2✔
27

28
const collectWithNames = (ast: AST) => {
2✔
29
  const withNames: string[] = [];
8✔
30
  if (ast.type === 'select' && ast.with) {
8✔
31
    ast.with.forEach((withItem) => {
6✔
32
      withNames.push(withItem.name.value);
6✔
33
    });
6✔
34
  }
6✔
35
  return withNames;
8✔
36
};
8✔
37

38
export const checkTableAccess = (
2✔
39
  sql: string,
8✔
40
  {
8✔
41
    tableNames,
8✔
42
    database,
8✔
43
  }: {
44
    tableNames: string[];
45
    database: DriverClient;
46
  }
8✔
47
) => {
48
  const parser = new Parser();
8✔
49
  const opt = {
8✔
50
    database: databaseTypeMap[database],
8✔
51
  };
8✔
52
  const { ast } = parser.parse(sql, opt);
8✔
53
  const withNames = Array.isArray(ast) ? ast.map(collectWithNames).flat() : collectWithNames(ast);
8!
54
  const allWithNames = new Set([...withNames, ...tableNames]);
8✔
55
  const whiteColumnList = Array.from(allWithNames).map((table) => {
8✔
56
    const [schema, tableName] = table.includes('.') ? table.split('.') : [null, table];
18✔
57
    return `select::${schema}::${tableName}`;
18✔
58
  });
18✔
59
  try {
8✔
60
    const error = parser.whiteListCheck(sql, whiteColumnList, opt);
8✔
61
    if (error) {
8!
NEW
62
      throw error;
×
NEW
63
    }
×
64
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
8✔
65
  } catch (error: any) {
8✔
66
    throw new BadRequestException(error?.message || 'An error occurred while checking table access.');
2!
67
  }
2✔
68
};
8✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc