Análisis de seguridad de TestGenAI con ESLint Security y GitHub Actions

本文为原文前 6,000 字符的节选翻译,完整内容请查看原文。

Análisis de seguridad de TestGenAI con ESLint Security y GitHub Actions

Author: Milton H. Flores Chino. TestGenAI is our Software Quality and Testing project: a web application to organize requirements, generate specifications and test cases, review their results, and maintain traceability. In this review, I incorporated ESLint Security to analyze TypeScript code with a tool different from Sonar, Semgrep, and Snyk, which were used in the course labs. The purpose was to obtain reproducible results, review what the alerts meant, and verify that the fixes maintained the system’s behavior.

作者:Milton H. Flores Chino。TestGenAI 是我们的软件质量与测试项目:一个用于组织需求、生成规范和测试用例、审查结果并保持可追溯性的 Web 应用程序。在本次审查中,我引入了 ESLint Security 来分析 TypeScript 代码,所使用的工具不同于课程实验中使用的 Sonar、Semgrep 和 Snyk。其目的是获得可复现的结果,审查警报的含义,并验证修复措施是否保持了系统的行为。

The application and its architecture. The frontend uses HTML, CSS, and modular JavaScript. The backend exposes an API with Express and TypeScript, and stores information in PostgreSQL using Prisma. The system includes a deterministic specification and test design engine, as well as optional adapters for Gemini and OpenAI. Case generation does not replace human review. Requirements, cases, reviews, sessions, and executions are stored using twelve Prisma models. The dashboard calculates requirement coverage and status distribution based on project data.

应用程序及其架构。前端使用 HTML、CSS 和模块化 JavaScript。后端通过 Express 和 TypeScript 公开 API,并使用 Prisma 将信息存储在 PostgreSQL 中。该系统包含一个确定性的规范和测试设计引擎,以及用于 Gemini 和 OpenAI 的可选适配器。用例生成不能替代人工审查。需求、用例、审查、会话和执行通过十二个 Prisma 模型进行存储。仪表板根据项目数据计算需求覆盖率和状态分布。

A different tool for static analysis. ESLint Security incorporates rules to flag patterns that may require review: dynamic object access, potentially problematic regular expressions, the use of eval, and other sensitive Node.js operations. The plugin documentation warns that many results are false positives and require human classification. Source: https://github.com/eslint-community/eslint-plugin-security. I installed the plugin as a development dependency and used a separate configuration with the TypeScript parser and recommended rules. The configuration retains the alerts; it does not remove the rules that detected results.

用于静态分析的另一种工具。ESLint Security 包含了一些规则,用于标记可能需要审查的模式:动态对象访问、潜在有问题的正则表达式、eval 的使用以及其他敏感的 Node.js 操作。插件文档警告称,许多结果是误报,需要人工分类。来源:https://github.com/eslint-community/eslint-plugin-security。我将该插件安装为开发依赖项,并使用带有 TypeScript 解析器和推荐规则的独立配置。该配置保留了警报;它不会删除检测到结果的规则。

Reproducible commands from backend: npm ci, npm run prisma:generate, npm run security:scan, npm test. The scope of the security analysis is backend/src. Tests, installed dependencies, and compiled files are not included in that count.

后端可复现的命令:npm ci、npm run prisma:generate、npm run security:scan、npm test。安全分析的范围是 backend/src。测试、已安装的依赖项和编译后的文件不包含在该统计中。

What the analysis found. The initial analysis found 50 security alerts: 39 dynamic object accesses, 10 regular expressions flagged as potentially insecure, and one non-literal RegExp constructor. These results do not equate to fifty confirmed vulnerabilities. Accessing an array with a controlled index or a limited-length expression can trigger a rule without demonstrating an exploitable path. The review must verify who controls the input, what limits exist, and how the result is used. The first execution also revealed three configuration errors due to TypeScript rule comments. I corrected the configuration by loading the corresponding plugin; those errors are not part of the security alert count.

分析发现了什么。初步分析发现了 50 个安全警报:39 个动态对象访问、10 个被标记为潜在不安全的正则表达式以及一个非字面量 RegExp 构造函数。这些结果并不等同于五十个已确认的漏洞。使用受控索引访问数组或使用长度受限的表达式可能会触发规则,但这并不证明存在可利用的路径。审查必须验证谁控制输入、存在哪些限制以及结果如何使用。首次执行还揭示了三个由于 TypeScript 规则注释导致的配置错误。我通过加载相应的插件纠正了配置;这些错误不属于安全警报统计的一部分。

Correction of the card pattern. One alert corresponded to the pattern used to mask card numbers before sending text to an AI provider. The pattern used a nested repetition of four-digit blocks. I rewrote that part by listing the four groups and their optional separators, keeping the alternative for fifteen-digit numbers. The transformation removes the static alert for that pattern and preserves the formats the application already accepted. This does not constitute a demonstration that the original expression was exploitable. I added tests for numbers with spaces, hyphens, and fifteen-digit numbers. I also verified that the inverse function restores the original data after masking. Subsequent analysis reports 49 alerts: 39 dynamic accesses, 9 regular expressions, and one non-literal RegExp. The remaining ones require review. I do not present this result as an application free of vulnerabilities.

卡片模式的修正。其中一个警报对应于在向 AI 提供商发送文本之前用于掩码卡号的模式。该模式使用了四位数字块的嵌套重复。我重写了那部分代码,列出了四个组及其可选的分隔符,并保留了针对十五位数字的替代方案。该转换消除了该模式的静态警报,并保留了应用程序已经接受的格式。这并不构成原始表达式可被利用的证明。我为带有空格、连字符和十五位数字的号码添加了测试。我还验证了反函数在掩码后能恢复原始数据。后续分析报告了 49 个警报:39 个动态访问、9 个正则表达式和一个非字面量 RegExp。其余的需要审查。我并不认为此结果代表应用程序没有漏洞。

Dependency auditing as a complementary control. Code analysis and dependency auditing answer different questions. npm audit initially identified 12 known vulnerabilities in the installed tree: 3 moderate, 5 high, and 4 critical. I updated Vitest and the coverage tool to 4.1.11, lint-staged to 17.6.0, and compatible dependencies. The subsequent npm audit query reported zero known vulnerabilities. This result is limited to the tree and the audit information at the time of execution. The CI and Docker configuration now uses Node.js 24, compatible with the updated tools.

作为补充控制的依赖项审计。代码分析和依赖项审计回答的是不同的问题。npm audit 最初在已安装的树中识别出 12 个已知漏洞:3 个中度、5 个高度和 4 个严重。我将 Vitest 和覆盖率工具更新至 4.1.11,将 lint-staged 更新至 17.6.0,并更新了兼容的依赖项。随后的 npm audit 查询报告零已知漏洞。此结果仅限于执行时的树和审计信息。CI 和 Docker 配置现在使用 Node.js 24,与更新后的工具兼容。

Automation and evidence. GitHub Actions executes the reproducible installation, the ESLint Security analysis, and the dependency audit. The workflow saves the analysis JSON as an artifact and adds a summary with the number of files and alerts. Execution errors cause failure. Alerts from recommended rules are kept for review and do not automatically equate to a deployment block. That distinction avoids confusing a successful job with the resolution of all problems. I also corrected errors that prevented the previous pipeline: the SQLite connection when the backend requires PostgreSQL, the mock provider that no longer accepts the configuration, and the reference to a non-existent Docker Compose file.

自动化与证据。GitHub Actions 执行可复现的安装、ESLint Security 分析和依赖项审计。工作流将分析 JSON 保存为工件,并添加包含文件数和警报数的摘要。执行错误会导致失败。推荐规则产生的警报会被保留以供审查,并不自动等同于阻止部署。这种区分避免了将成功的作业与所有问题的解决混为一谈。我还纠正了阻碍先前流水线的错误:后端需要 PostgreSQL 时出现的 SQLite 连接问题、不再接受该配置的模拟提供商,以及对不存在的 Docker Compose 文件的引用。

The subsequent local suite passed 59 tests. The previous coverage report, with 52 tests, measured 16.43% of lines. General coverage remains limited; counting passed tests is not enough to verify the entire backend. As a complementary control, the Semgrep execution found an AES-GCM configuration without an explicit tag length in CryptoVault. I specified an authTagLength of 16 bytes in both encryption and decryption. Two tests verify the restoration of the text and the rejection of altered tags or incomplete payloads. The new Semgrep execution finished with zero findings from the selected JavaScript and TypeScript rules; this does not resolve the 49 independent ESLint Security alerts. SonarQube Community was also executed on a temporary runner service. The first

随后的本地测试套件通过了 59 个测试。之前的覆盖率报告(包含 52 个测试)测量了 16.43% 的代码行。总体覆盖率仍然有限;仅计算通过的测试不足以验证整个后端。作为补充控制,Semgrep 的执行在 CryptoVault 中发现了一个没有显式标签长度的 AES-GCM 配置。我在加密和解密中都指定了 16 字节的 authTagLength。两个测试验证了文本的恢复以及对篡改标签或不完整有效载荷的拒绝。新的 Semgrep 执行结束时,所选的 JavaScript 和 TypeScript 规则未发现任何问题;但这并不能解决 49 个独立的 ESLint Security 警报。SonarQube Community 也在临时运行器服务上执行。第一个