-
Notifications
You must be signed in to change notification settings - Fork 14
feat: sqlite3 web #497
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: sqlite3 web #497
Conversation
文件级别变更
提示和命令与 Sourcery 交互
自定义您的体验访问您的 dashboard 以:
获取帮助
Original review guide in EnglishReviewer's GuideImplements end-to-end SQLite database management: registers new server endpoints, extends the API client, and adds a full-featured frontend UI for browsing, querying, and modifying SQLite tables. Class diagram for new and updated types in SQLite API clientclassDiagram
class ColumnInfo {
+string name
+string type
+number pk
+number notnull
+string dflt_value
+number cid
}
class TableInfo {
+string name
+ColumnInfo[] columns
}
class SqliteTableData {
+any[] records
+number total
+number page
+number pageSize
}
class SqliteExecuteResult {
+number changes
+number lastID
}
TableInfo "1" -- "*" ColumnInfo : has
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
嘿 @sj817 - 我已经查看了你的更改,发现了一些需要解决的问题。
阻塞问题:
- 检测到用于手动构造 SQL 字符串的用户输入。这通常是不好的做法,因为手动构造可能会意外导致 SQL 注入。攻击者可以使用 SQL 注入来窃取或修改数据库的内容。请改用参数化查询,大多数数据库引擎默认提供该查询。或者,考虑使用对象关系映射器 (ORM),例如 Sequelize,它将保护您的查询。(link)
- 检测到用于手动构造 SQL 字符串的用户输入。这通常是不好的做法,因为手动构造可能会意外导致 SQL 注入。攻击者可以使用 SQL 注入来窃取或修改数据库的内容。请改用参数化查询,大多数数据库引擎默认提供该查询。或者,考虑使用对象关系映射器 (ORM),例如 Sequelize,它将保护您的查询。(link)
- PRAGMA table_info 使用中存在潜在的 SQL 注入风险。(link)
- 直接内插 tableName 和 where 子句会引入 SQL 注入风险。(link)
- SELECT 语句中存在 SQL 注入风险,用于表数据。(link)
- 执行任意 SQL 而不进行验证存在安全风险。(link)
- 直接字符串内插 SQL 值可能导致 SQL 注入或数据损坏。(link)
- 在 INSERT 语句中直接进行值内插是不安全的。(link)
- 在 DELETE 语句中直接内插表名和列名是不安全的。(link)
一般评论:
- 服务器和客户端代码正在通过连接原始输入(例如表名、where 子句、编辑的值)来构建 SQL 语句,这很容易受到 SQL 注入的攻击——考虑使用参数化查询或适当的转义。
- 新的 SqliteManagerPage 组件超过 500 行,并且混合了获取逻辑、模态框、表渲染和 SQL 执行——考虑将其重构为更小的钩子和子组件,以提高清晰度和可维护性。
- 三个 Express 处理程序重复打开和关闭数据库,逻辑几乎相同——将常见的数据库初始化、错误处理和响应格式化提取到共享实用程序中,以减少重复。
Prompt for AI Agents
请解决此代码审查中的评论:
## 总体评论
- 服务器和客户端代码正在通过连接原始输入(例如表名、where 子句、编辑的值)来构建 SQL 语句,这很容易受到 SQL 注入的攻击——考虑使用参数化查询或适当的转义。
- 新的 SqliteManagerPage 组件超过 500 行,并且混合了获取逻辑、模态框、表渲染和 SQL 执行——考虑将其重构为更小的钩子和子组件,以提高清晰度和可维护性。
- 三个 Express 处理程序重复打开和关闭数据库,逻辑几乎相同——将常见的数据库初始化、错误处理和响应格式化提取到共享实用程序中,以减少重复。
## 单独评论
### 评论 1
<location> `packages/core/src/server/database/sqlite.ts:16` </location>
<code_context>
+ return res.status(400).json({ code: 400, message: '数据库路径不能为空' })
+ }
+
+ const db = new sqlite3.Database(dbPath, (err) => {
+ if (err) {
+ console.error('连接数据库失败', err)
</code_context>
<issue_to_address>
数据库连接未在早期错误时关闭。
请在错误分支中显式关闭数据库连接,以确保即使构造函数部分成功,也能释放资源。
</issue_to_address>
### 评论 2
<location> `packages/core/src/server/database/sqlite.ts:35` </location>
<code_context>
+
+ const getColumns = (tableName: string): Promise<any[]> => {
+ return new Promise((resolve, reject) =>
8000
{
+ db.all(`PRAGMA table_info(${tableName})`, (err, rows) => {
+ if (err) reject(err)
+ else resolve(rows)
</code_context>
<issue_to_address>
PRAGMA table_info 使用中存在潜在的 SQL 注入风险。
在使用 PRAGMA 语句之前,请验证 `tableName` 是否为安全标识符,因为此处不支持参数化查询。
</issue_to_address>
### 评论 3
<location> `packages/core/src/server/database/sqlite.ts:94` </location>
<code_context>
+
+ const getCount = (): Promise<number> => {
+ return new Promise((resolve, reject) => {
+ db.get(`SELECT COUNT(*) as count FROM ${tableName} ${where ? 'WHERE ' + where : ''}`, (err, row: { count: number }) => {
+ if (err) reject(err)
+ else resolve(row.count)
</code_context>
<issue_to_address>
直接内插 tableName 和 where 子句会引入 SQL 注入风险。
验证 `tableName` 是否为标识符,并对 `where` 子句使用参数化查询,以防止 SQL 注入。
</issue_to_address>
### 评论 4
<location> `packages/core/src/server/database/sqlite.ts:104` </location>
<code_context>
+ const getData = (offset: number): Promise<any[]> => {
+ return new Promise((resolve, reject) => {
+ db.all(
+ `SELECT * FROM ${tableName} ${where ? 'WHERE ' + where : ''} LIMIT ${pageSize} OFFSET ${offset}`,
+ (err, rows) => {
+ if (err) reject(err)
</code_context>
<issue_to_address>
SELECT 语句中存在 SQL 注入风险,用于表数据。
直接内插 `tableName` 和 `where` 允许 SQL 注入。验证 `tableName` 并对 `where` 使用参数化查询。
</issue_to_address>
### 评论 5
<location> `packages/core/src/server/database/sqlite.ts:160` </location>
<code_context>
+ }
+ })
+
+ const statement = sql.trim().toLowerCase()
+
+ // 判断是否为查询语句
</code_context>
<issue_to_address>
SQL 语句类型检测区分大小写,但不够健壮。
当前的检测可能因前导注释、空格或以 `WITH` 等构造开头的查询而失败。考虑删除输入中的空格并处理注释,或使用更健壮的 SQL 解析器。
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
const statement = sql.trim().toLowerCase()
// 判断是否为查询语句
const isQuery = statement.startsWith('select')
=======
// 更健壮的SQL类型检测:去除前导空白和注释,检测SELECT/WITH等查询语句
function getFirstKeyword(sql: string): string | null {
// 去除前导空白
let s = sql.trim()
// 去除前导多行注释
while (s.startsWith('/*')) {
const end = s.indexOf('*/')
if (end === -1) break
s = s.slice(end + 2).trim()
}
// 去除前导单行注释
while (s.startsWith('--')) {
const end = s.indexOf('\n')
if (end === -1) return null
s = s.slice(end + 1).trim()
}
// 匹配第一个单词
const match = s.match(/^([a-zA-Z]+)/)
return match ? match[1].toLowerCase() : null
}
const firstKeyword = getFirstKeyword(sql)
// 判断是否为查询语句(SELECT 或 WITH 开头)
const isQuery = firstKeyword === 'select' || firstKeyword === 'with'
>>>>>>> REPLACE
</suggested_fix>
### 评论 6
<location> `packages/core/src/server/database/sqlite.ts:189` </location>
<code_context>
+ // 非查询语句
+ const executeUpdate = (): Promise<{ changes: number, lastID: number }> => {
+ return new Promise((resolve, reject) => {
+ db.run(sql, function (err) {
+ if (err) reject(err)
+ else resolve({ changes: this.changes, lastID: this.lastID })
</code_context>
<issue_to_address>
执行任意 SQL 而不进行验证存在安全风险。
实施诸如语句白名单、严格身份验证或沙盒之类的安全措施来降低风险。
</issue_to_address>
### 评论 7
<location> `packages/web/src/pages/dashboard/sqlite/index.tsx:125` </location>
<code_context>
+ const pkValue = editingRecord[pkColumn.name]
+
+ // 构建SET部分
+ const setClause = Object.entries(editedValues)
+ .map(([column, value]) => {
+ if (value === null) return `${column} = NULL`
+ return typeof value === 'string' ? `${column} = '${value}'` : `${column} = ${value}`
+ })
+ .join(', ')
+
+ // 构建SQL更新语句
</code_context>
<issue_to_address>
直接字符串内插 SQL 值可能导致 SQL 注入或数据损坏。
使用参数化查询或转义单引号以防止 SQL 注入并安全地处理特殊字符。
</issue_to_address>
### 评论 8
<location> `packages/web/src/pages/dashboard/sqlite/index.tsx:160` </location>
<code_context>
+
+ // 构建列和值
+ const columns = Object.keys(newRecord).join(', ')
+ const values = Object.values(newRecord).map(v =>
+ v === null ? 'NULL' : (typeof v === 'string' ? `'${v}'` : v)
+ ).join(', ')
+
+ // 构建SQL插入语句
</code_context>
<issue_to_address>
在 INSERT 语句中直接进行值内插是不安全的。
这种方法会将代码暴露于 SQL 注入以及特殊字符问题。请改用参数化查询。
</issue_to_address>
### 评论 9
<location> `packages/web/src/pages/dashboard/sqlite/index.tsx:209` </location>
<code_context>
+ }
+
+ // 构建SQL删除语句
+ const sql = `DELETE FROM ${selectedTable} WHERE ${pkColumn.name} = ${typeof pkValue === 'string' ? `'${pkValue}'` : pkValue}`
+
+ // 执行删除操作
</code_context>
<issue_to_address>
在 DELETE 语句中直接内插表名和列名是不安全的。
使用前验证表名和列名,并始终对值使用参数化查询,以防止 SQL 注入。
</issue_to_address>
### 评论 10
<location> `packages/web/src/pages/dashboard/sqlite/index.tsx:24` </location>
<code_context>
+ const [currentPage, setCurrentPage] = useState<number>(1)
+ const [searchValue, setSearchValue] = useState<string>('')
+ const [sqlQuery, setSqlQuery] = useState<string>('')
+ const [queryResult, setQueryResult] = useState<any[] | null>(null)
+
+ // 编辑相关状态
</code_context>
<issue_to_address>
切换表时未清除查询结果状态。
当所选表更改时,清除 `queryResult` 以防止显示过时的结果。
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
// 编辑相关状态
const { isOpen, onOpen, onClose } = useDisclosure()
=======
useEffect(() => {
setQueryResult(null)
}, [selectedTable])
// 编辑相关状态
const { isOpen, onOpen, onClose } = useDisclosure()
>>>>>>> REPLACE
</suggested_fix>
## 安全问题
### 问题 1
<location> `packages/core/src/server/database/sqlite.ts:94` </location>
<issue_to_address>
**security (opengrep-rules.javascript.express.security.injection.tainted-sql-string):** 检测到用于手动构造 SQL 字符串的用户输入。这通常是不好的做法,因为手动构造可能会意外导致 SQL 注入。攻击者可以使用 SQL 注入来窃取或修改数据库的内容。请改用参数化查询,大多数数据库引擎默认提供该查询。或者,考虑使用对象关系映射器 (ORM),例如 Sequelize,它将保护您的查询。
*Source: opengrep*
</issue_to_address>
### 问题 2
<location> `packages/core/src/server/database/sqlite.ts:104` </location>
<issue_to_address>
**security (opengrep-rules.javascript.express.security.injection.tainted-sql-string):** 检测到用于手动构造 SQL 字符串的用户输入。这通常是不好的做法,因为手动构造可能会意外导致 SQL 注入。攻击者可以使用 SQL 注入来窃取或修改数据库的内容。请改用参数化查询,大多数数据库引擎默认提供该查询。或者,考虑使用对象关系映射器 (ORM),例如 Sequelize,它将保护您的查询。
*Source: opengrep*
</issue_to_address>
Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Original comment in English
Hey @sj817 - I've reviewed your changes and found some issues that need to be addressed.
Blocking issues:
- Detected user input used to manually construct a SQL string. This is usually bad practice because manual construction could accidentally result in a SQL injection. An attacker could use a SQL injection to steal or modify contents of the database. Instead, use a parameterized query which is available by default in most database engines. Alternatively, consider using an object-relational mapper (ORM) such as Sequelize which will protect your queries. (link)
- Detected user input used to manually construct a SQL string. This is usually bad practice because manual construction could accidentally result in a SQL injection. An attacker could use a SQL injection to steal or modify contents of the database. Instead, use a parameterized query which is available by default in most database engines. Alternatively, consider using an object-relational mapper (ORM) such as Sequelize which will protect your queries. (link)
- Potential SQL injection risk in PRAGMA table_info usage. (link)
- Direct interpolation of tableName and where clause introduces SQL injection risk. (link)
- SQL injection risk in SELECT statement for table data. (link)
- Executing arbitrary SQL without validation is a security risk. (link)
- Direct string interpolation for SQL values can cause SQL injection or data corruption. (link)
- Direct value interpolation in INSERT statement is unsafe. (link)
- Direct interpolation of table and column names in DELETE statement is unsafe. (link)
General comments:
- The server and client code are building SQL statements by concatenating raw input (e.g. table names, where clauses, edited values), which is vulnerable to SQL injection—consider using parameterized queries or proper escaping.
- The new SqliteManagerPage component is over 500 lines long and mixes fetching logic, modals, table rendering, and SQL execution—consider refactoring it into smaller hooks and child components for clarity and maintainability.
- The three Express handlers repeatedly open and close the database with nearly identical logic—extract the common DB initialization, error handling, and response formatting into a shared utility to reduce duplication.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The server and client code are building SQL statements by concatenating raw input (e.g. table names, where clauses, edited values), which is vulnerable to SQL injection—consider using parameterized queries or proper escaping.
- The new SqliteManagerPage component is over 500 lines long and mixes fetching logic, modals, table rendering, and SQL execution—consider refactoring it into smaller hooks and child components for clarity and maintainability.
- The three Express handlers repeatedly open and close the database with nearly identical logic—extract the common DB initialization, error handling, and response formatting into a shared utility to reduce duplication.
## Individual Comments
### Comment 1
<location> `packages/core/src/server/database/sqlite.ts:16` </location>
<code_context>
+ return res.status(400).json({ code: 400, message: '数据库路径不能为空' })
+ }
+
+ const db = new sqlite3.Database(dbPath, (err) => {
+ if (err) {
+ console.error('连接数据库失败', err)
</code_context>
<issue_to_address>
Database connection is not closed on early error.
Explicitly close the database connection in the error branch to ensure resources are released, even if the constructor partially succeeds.
</issue_to_address>
### Comment 2
<location> `packages/core/src/server/database/sqlite.ts:35` </location>
<code_context>
+
+ const getColumns = (tableName: string): Promise<any[]> => {
+ return new Promise((resolve, reject) => {
+ db.all(`PRAGMA table_info(${tableName})`, (err, rows) => {
+ if (err) reject(err)
+ else resolve(rows)
</code_context>
<issue_to_address>
Potential SQL injection risk in PRAGMA table_info usage.
Validate that `tableName` is a safe identifier before using it in the PRAGMA statement, as parameterized queries are not supported here.
</issue_to_address>
### Comment 3
<location> `packages/core/src/server/database/sqlite.ts:94` </location>
<code_context>
+
+ const getCount = (): Promise<number> => {
+ return new Promise((resolve, reject) => {
+ db.get(`SELECT COUNT(*) as count FROM ${tableName} ${where ? 'WHERE ' + where : ''}`, (err, row: { count: number }) => {
+ if (err) reject(err)
+ else resolve(row.count)
</code_context>
<issue_to_address>
Direct interpolation of tableName and where clause introduces SQL injection risk.
Validate `tableName` as an identifier and use parameterized queries for the `where` clause to prevent SQL injection.
</issue_to_address>
### Comment 4
<location> `packages/core/src/server/database/sqlite.ts:104` </location>
<code_context>
+ const getData = (offset: number): Promise<any[]> => {
+ return new Promise((resolve, reject) => {
+ db.all(
+ `SELECT * FROM ${tableName} ${where ? 'WHERE ' + where : ''} LIMIT ${pageSize} OFFSET ${offset}`,
+ (err, rows) => {
+ if (err) reject(err)
</code_context>
<issue_to_address>
SQL injection risk in SELECT statement for table data.
Directly interpolating `tableName` and `where` allows SQL injection. Validate `tableName` and use parameterized queries for `where`.
</issue_to_address>
### Comment 5
<location> `packages/core/src/server/database/sqlite.ts:160` </location>
<code_context>
+ }
+ })
+
+ const statement = sql.trim().toLowerCase()
+
+ // 判断是否为查询语句
</code_context>
<issue_to_address>
SQL statement type detection is case-insensitive but not robust.
Current detection may fail with leading comments, whitespace, or queries starting with constructs like `WITH`. Consider trimming input and handling comments, or use a more robust SQL parser.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
const statement = sql.trim().toLowerCase()
// 判断是否为查询语句
const isQuery = statement.startsWith('select')
=======
// 更健壮的SQL类型检测:去除前导空白和注释,检测SELECT/WITH等查询语句
function getFirstKeyword(sql: string): string | null {
// 去除前导空白
let s = sql.trim()
// 去除前导多行注释
while (s.startsWith('/*')) {
const end = s.indexOf('*/')
if (end === -1) break
s = s.slice(end + 2).trim()
}
// 去除前导单行注释
while (s.startsWith('--')) {
const end = s.indexOf('\n')
if (end === -1) return null
s = s.slice(end + 1).trim()
}
// 匹配第一个单词
const match = s.match(/^([a-zA-Z]+)/)
return match ? match[1].toLowerCase() : null
}
const firstKeyword = getFirstKeyword(sql)
// 判断是否为查询语句(SELECT 或 WITH 开头)
const isQuery = firstKeyword === 'select' || firstKeyword === 'with'
>>>>>>> REPLACE
</suggested_fix>
### Comment 6
<location> `packages/core/src/server/database/sqlite.ts:189` </location>
<code_context>
+ // 非查询语句
+ const executeUpdate = (): Promise<{ changes: number, lastID: number }> => {
+ return new Promise((resolve, reject) => {
+ db.run(sql, function (err) {
+ if (err) reject(err)
+ else resolve({ changes: this.changes, lastID: this.lastID })
</code_context>
<issue_to_address>
Executing arbitrary SQL without validation is a security risk.
Implement safeguards such as statement whitelisting, strict authentication, or sandboxing to mitigate risks.
</issue_to_address>
### Comment 7
<location> `packages/web/src/pages/dashboard/sqlite/index.tsx:125` </location>
<code_context>
+ const pkValue = editingRecord[pkColumn.name]
+
+ // 构建SET部分
+ const setClause = Object.entries(editedValues)
+ .map(([column, value]) => {
+ if (value === null) return `${column} = NULL`
+ return typeof value === 'string' ? `${column} = '${value}'` : `${column} = ${value}`
+ })
+ .join(', ')
+
+ // 构建SQL更新语句
</code_context>
<issue_to_address>
Direct string interpolation for SQL values can cause SQL injection or data corruption.
Use parameterized queries or escape single quotes to prevent SQL injection and handle special characters safely.
</issue_to_address>
### Comment 8
<location> `packages/web/src/pages/dashboard/sqlite/index.tsx:160` </location>
<code_context>
+
+ // 构建列和值
+ const columns = Object.keys(newRecord).join(', ')
+ const values = Object.values(newRecord).map(v =>
+ v === null ? 'NULL' : (typeof v === 'string' ? `'${v}'` : v)
+ ).join(', ')
+
+ // 构建SQL插入语句
</code_context>
<issue_to_address>
Direct value interpolation in INSERT statement is unsafe.
This approach exposes the code to SQL injection and issues with special characters. Use parameterized queries instead.
</issue_to_address>
### Comment 9
<location> `packages/web/src/pages/dashboard/sqlite/index.tsx:209` </location>
<code_context>
+ }
+
+ // 构建SQL删除语句
+ const sql = `DELETE FROM ${selectedTable} WHERE ${pkColumn.name} = ${typeof pkValue === 'string' ? `'${pkValue}'` : pkValue}`
+
+ // 执行删除操作
</code_context>
<issue_to_address>
Direct interpolation of table and column names in DELETE statement is unsafe.
Validate table and column names before use, and always use parameterized queries for values to prevent SQL injection.
</issue_to_address>
### Comment 10
<location> `packages/web/src/pages/dashboard/sqlite/index.tsx:24` </location>
<code_context>
+ const [currentPage, setCurrentPage] = useState<number>(1)
+ const [searchValue, setSearchValue] = useState<string>('')
+ const [sqlQuery, setSqlQuery] = useState<string>('')
+ const [queryResult, setQueryResult] = useState<any[] | null>(null)
+
+ // 编辑相关状态
</code_context>
<issue_to_address>
Query result state is not cleared when switching tables.
Clear `queryResult` when the selected table changes to prevent displaying outdated results.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
// 编辑相关状态
const { isOpen, onOpen, onClose } = useDisclosure()
=======
useEffect(() => {
setQueryResult(null)
}, [selectedTable])
// 编辑相关状态
const { isOpen, onOpen, onClose } = useDisclosure()
>>>>>>> REPLACE
</suggested_fix>
## Security Issues
### Issue 1
<location> `packages/core/src/server/database/sqlite.ts:94` </location>
<issue_to_address>
**security (opengrep-rules.javascript.express.security.injection.tainted-sql-string):** Detected user input used to manually construct a SQL string. This is usually bad practice because manual construction could accidentally result in a SQL injection. An attacker could use a SQL injection to steal or modify contents of the database. Instead, use a parameterized query which is available by default in most database engines. Alternatively, consider using an object-relational mapper (ORM) such as Sequelize which will protect your queries.
*Source: opengrep*
</issue_to_address>
### Issue 2
<location> `packages/core/src/server/database/sqlite.ts:104` </location>
<issue_to_address>
**security (opengrep-rules.javascript.express.security.injection.tainted-sql-string):** Detected user input used to manually construct a SQL string. This is usually bad practice because manual construction could accidentally result in a SQL injection. An attacker could use a SQL injection to steal or modify contents of the database. Instead, use a parameterized query which is available by default in most database engines. Alternatively, consider using an object-relational mapper (ORM) such as Sequelize which will protect your queries.
*Source: opengrep*
</issue_to_address>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
return res.status(400).json({ code: 400, message: '数据库路径不能为空' }) | ||
} | ||
|
||
const db = new sqlite3.Database(dbPath, (err) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): 数据库连接未在早期错误时关闭。
请在错误分支中显式关闭数据库连接,以确保即使构造函数部分成功,也能释放资源。
Original comment in English
issue (bug_risk): Database connection is not closed on early error.
Explicitly close the database connection in the error branch to ensure resources are released, even if the constructor partially succeeds.
|
||
const getColumns = (tableName: string): Promise<any[]> => { | ||
return new Promise((resolve, reject) => { | ||
db.all(`PRAGMA table_info(${tableName})`, (err, rows) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚨 issue (security): PRAGMA table_info 使用中存在潜在的 SQL 注入风险。
在使用 PRAGMA 语句之前,请验证 tableName
是否为安全标识符,因为此处不支持参数化查询。
Original comment in English
🚨 issue (security): Potential SQL injection risk in PRAGMA table_info usage.
Validate that tableName
is a safe identifier before using it in the PRAGMA statement, as parameterized queries are not supported here.
|
||
const getCount = (): Promise<number> => { | ||
return new Promise((resolve, reject) => { | ||
db.get(`SELECT COUNT(*) as count FROM ${tableName} ${where ? 'WHERE ' + where : ''}`, (err, row: { count: number }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚨 issue (security): 直接内插 tableName 和 where 子句会引入 SQL 注入风险。
验证 tableName
是否为标识符,并对 where
子句使用参数化查询,以防止 SQL 注入。
Original comment in English
🚨 issue (security): Direct interpolation of tableName and where clause introduces SQL injection risk.
Validate tableName
as an identifier and use parameterized queries for the where
clause to prevent SQL injection.
const getData = (offset: number): Promise<any[]> => { | ||
return new Promise((resolve, reject) => { | ||
db.all( | ||
`SELECT * FROM ${tableName} ${where ? 'WHERE ' + where : ''} LIMIT ${pageSize} OFFSET ${offset}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚨 issue (security): SELECT 语句中存在 SQL 注入风险,用于表数据。
直接内插 tableName
和 where
允许 SQL 注入。验证 tableName
并对 where
使用参数化查询。
Original comment in English
🚨 issue (security): SQL injection risk in SELECT statement for table data.
Directly interpolating tableName
and where
allows SQL injection. Validate tableName
and use parameterized queries for where
.
const statement = sql.trim().toLowerCase() | ||
|
||
// 判断是否为查询语句 | ||
const isQuery = statement.startsWith('select') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): SQL 语句类型检测区分大小写,但不够健壮。
当前的检测可能因前导注释、空格或以 WITH
等构造开头的查询而失败。考虑删除输入中的空格并处理注释,或使用更健壮的 SQL 解析器。
const statement = sql.trim().toLowerCase() | |
// 判断是否为查询语句 | |
const isQuery = statement.startsWith('select') | |
// 更健壮的SQL类型检测:去除前导空白和注释,检测SELECT/WITH等查询语句 | |
function getFirstKeyword(sql: string): string | null { | |
// 去除前导空白 | |
let s = sql.trim() | |
// 去除前导多行注释 | |
while (s.startsWith('/*')) { | |
const end = s.indexOf('*/') | |
if (end === -1) break | |
s = s.slice(end + 2).trim() | |
} | |
// 去除前导单行注释 | |
while (s.startsWith('--')) { | |
const end = s.indexOf('\n') | |
if (end === -1) return null | |
s = s.slice(end + 1).trim() | |
} | |
// 匹配第一个单词 | |
const match = s.match(/^([a-zA-Z]+)/) | |
return match ? match[1].toLowerCase() : null | |
} | |
const firstKeyword = getFirstKeyword(sql) | |
// 判断是否为查询语句(SELECT 或 WITH 开头) | |
const isQuery = firstKeyword === 'select' || firstKeyword === 'with' |
Original comment in English
suggestion (bug_risk): SQL statement type detection is case-insensitive but not robust.
Current detection may fail with leading comments, whitespace, or queries starting with constructs like WITH
. Consider trimming input and handling comments, or use a more robust SQL parser.
const statement = sql.trim().toLowerCase() | |
// 判断是否为查询语句 | |
const isQuery = statement.startsWith('select') | |
// 更健壮的SQL类型检测:去除前导空白和注释,检测SELECT/WITH等查询语句 | |
function getFirstKeyword(sql: string): string | null { | |
// 去除前导空白 | |
let s = sql.trim() | |
// 去除前导多行注释 | |
while (s.startsWith('/*')) { | |
const end = s.indexOf('*/') | |
if (end === -1) break | |
s = s.slice(end + 2).trim() | |
} | |
// 去除前导单行注释 | |
while (s.startsWith('--')) { | |
const end = s.indexOf('\n') | |
if (end === -1) return null | |
s = s.slice(end + 1).trim() | |
} | |
// 匹配第一个单词 | |
const match = s.match(/^([a-zA-Z]+)/) | |
return match ? match[1].toLowerCase() : null | |
} | |
const firstKeyword = getFirstKeyword(sql) | |
// 判断是否为查询语句(SELECT 或 WITH 开头) | |
const isQuery = firstKeyword === 'select' || firstKeyword === 'with' |
const values = Object.values(newRecord).map(v => | ||
v === null ? 'NULL' : (typeof v === 'string' ? `'${v}'` : v) | ||
).join(', ') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚨 issue (security): 在 INSERT 语句中直接进行值内插是不安全的。
这种方法会将代码暴露于 SQL 注入以及特殊字符问题。请改用参数化查询。
Original comment in English
🚨 issue (security): Direct value interpolation in INSERT statement is unsafe.
This approach exposes the code to SQL injection and issues with special characters. Use parameterized queries instead.
} | ||
|
||
// 构建SQL删除语句 | ||
const sql = `DELETE FROM ${selectedTable} WHERE ${pkColumn.name} = ${typeof pkValue === 'string' ? `'${pkValue}'` : pkValue}` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚨 issue (security): 在 DELETE 语句中直接内插表名和列名是不安全的。
使用前验证表名和列名,并始终对值使用参数化查询,以防止 SQL 注入。
Original comment in English
🚨 issue (security): Direct interpolation of table and column names in DELETE statement is unsafe.
Validate table and column names before use, and always use parameterized queries for values to prevent SQL injection.
|
||
// 编辑相关状态 | ||
const { isOpen, onOpen, onClose } = useDisclosure() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): 切换表时未清除查询结果状态。
当所选表更改时,清除 queryResult
以防止显示过时的结果。
// 编辑相关状态 | |
const { isOpen, onOpen, onClose } = useDisclosure() | |
useEffect(() => { | |
setQueryResult(null) | |
}, [selectedTable]) | |
// 编辑相关状态 | |
const { isOpen, onOpen, onClose } = useDisclosure() |
Original comment in English
suggestion (bug_risk): Query result state is not cleared when switching tables.
Clear queryResult
when the selected table changes to prevent displaying outdated results.
// 编辑相关状态 | |
const { isOpen, onOpen, onClose } = useDisclosure() | |
useEffect(() => { | |
setQueryResult(null) | |
}, [selectedTable]) | |
// 编辑相关状态 | |
const { isOpen, onOpen, onClose } = useDisclosure() |
|
||
const getCount = (): Promise<number> => { | ||
return new Promise((resolve, reject) => { | ||
db.get(`SELECT COUNT(*) as count FROM ${tableName} ${where ? 'WHERE ' + where : ''}`, (err, row: { count: number }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
security (opengrep-rules.javascript.express.security.injection.tainted-sql-string): 检测到用于手动构造 SQL 字符串的用户输入。这通常是不好的做法,因为手动构造可能会意外导致 SQL 注入。攻击者可以使用 SQL 注入来窃取或修改数据库的内容。请改用参数化查询,大多数数据库引擎默认提供该查询。或者,考虑使用对象关系映射器 (ORM),例如 Sequelize,它将保护您的查询。
Source: opengrep
Original comment in English
security (opengrep-rules.javascript.express.security.injection.tainted-sql-string): Detected user input used to manually construct a SQL string. This is usually bad practice because manual construction could accidentally result in a SQL injection. An attacker could use a SQL injection to steal or modify contents of the database. Instead, use a parameterized query which is available by default in most database engines. Alternatively, consider using an object-relational mapper (ORM) such as Sequelize which will protect your queries.
Source: opengrep
const getData = (offset: number): Promise<any[]> => { | ||
return new Promise((resolve, reject) => { | ||
db.all( | ||
`SELECT * FROM ${tableName} ${where ? 'WHERE ' + where : ''} LIMIT ${pageSize} OFFSET ${offset}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
security (opengrep-rules.javascript.express.security.injection.tainted-sql-string): 检测到用于手动构造 SQL 字符串的用户输入。这通常是不好的做法,因为手动构造可能会意外导致 SQL 注入。攻击者可以使用 SQL 注入来窃取或修改数据库的内容。请改用参数化查询,大多数数据库引擎默认提供该查询。或者,考虑使用对象关系映射器 (ORM),例如 Sequelize,它将保护您的查询。
Source: opengrep
Original comment in English
security (opengrep-rules.javascript.express.security.injection.tainted-sql-string): Detected user input used to manually construct a SQL string. This is usually bad practice because manual construction could accidentally result in a SQL injection. An attacker could use a SQL injection to steal or modify contents of the database. Instead, use a parameterized query which is available by default in most database engines. Alternatively, consider using an object-relational mapper (ORM) such as Sequelize which will protect your queries.
Source: opengrep
#371
好的,这是翻译成中文的 pull request 总结:
Sourcery 总结
实现全栈 SQLite 管理,通过暴露新的服务器处理程序并添加一个专门的前端页面,用于交互式数据库浏览和操作。
新功能:
增强功能:
Original summary in English
Summary by Sourcery
Implement full-stack SQLite management by exposing new server handlers and adding a dedicated frontend page for interactive database browsing and manipulation
New Features:
Enhancements: