no-conn-outside-root.test.js
3.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import { RuleTester } from 'eslint'
import { describe, it } from 'vitest'
import { noConnOutsideRoot } from '../no-conn-outside-root.js'
const ruleTester = new RuleTester({
languageOptions: { ecmaVersion: 2023, sourceType: 'module' },
})
const ROOT = '/repo/apps/server/src/main.js'
const ROUTE = '/repo/apps/server/src/routes/drama.js'
const PKG = '/repo/packages/tts/src/doubao.js'
const SCRIPT = '/repo/tools/scripts/visual-game-dev-user.js'
// 目录前缀豁免('tools/scripts/')+ 单文件豁免(main.js)共存。
const opts = [{ connExemptFiles: [ROOT, 'tools/scripts/'] }]
describe('no-conn-outside-root (规则 C)', () => {
it('valid/invalid + 误伤防回归', () => {
ruleTester.run('no-conn-outside-root', noConnOutsideRoot, {
valid: [
// 白名单 main.js:顶层连接放行。
{
code: "const url = process.env.DB_URL\nconst db = new Knex({ url })",
filename: ROOT,
options: opts,
},
// 业务模块可以在使用点直接读取运行参数。
{
code: 'const k = process.env.SECRET',
filename: ROUTE,
options: opts,
},
// 误伤防回归:非连接的普通类顶层 new → 放行。
{
code: 'const m = new Map()\nclass Foo {}\nconst f = new Foo()',
filename: ROUTE,
options: opts,
},
// 误伤防回归:连接类在函数体内 new(工厂按注入参数建连接)→ 放行。
{
code: 'export function createDb({ url }) { return new Knex({ url }) }',
filename: PKG,
options: opts,
},
// 误伤防回归:new ApiClient() 是业务类,精确名单不含 → 放行(旧后缀匹配会误伤)。
{
code: 'class ApiClient {}\nconst c = new ApiClient()',
filename: PKG,
options: opts,
},
// 误伤防回归:函数体内 knex('表名') 是查询构建(全仓几百处)→ 放行。
{
code: "export function load(knex) { return knex('orders').where({ id: 1 }) }",
filename: PKG,
options: opts,
},
// 白名单 main.js:顶层 knex(cfg) 建连接放行。
{
code: 'const db = knex(cfg)',
filename: ROOT,
options: opts,
},
// 目录前缀豁免:tools/scripts/ 下的可执行脚本读 env、顶层建连接均放行。
{
code: "const url = process.env.DB_URL\nconst db = knex({ url })",
filename: SCRIPT,
options: opts,
},
],
invalid: [
// 顶层 knex(cfg) 建连接(函数调用分支)→ 报错。
{
code: 'const db = knex(cfg)',
filename: PKG,
options: opts,
errors: [{ messageId: 'connCall' }],
},
// 顶层成员调用 mysql.createConnection(...) → 报错。
{
code: 'import mysql from "mysql2/promise"\nconst conn = mysql.createConnection(url)',
filename: ROUTE,
options: opts,
errors: [{ messageId: 'connCall' }],
},
// 非 main.js 顶层建连接 → 报错。
{
code: 'const db = new Knex({})',
filename: PKG,
options: opts,
errors: [{ messageId: 'conn' }],
},
// 后缀匹配:new S3Client() 命中 Client 模式。
{
code: 'const s3 = new S3Client({})',
filename: PKG,
options: opts,
errors: [{ messageId: 'conn' }],
},
// 成员形式 new pg.Pool() 顶层 → 报错。
{
code: 'import pg from "pg"\nconst pool = new pg.Pool({})',
filename: PKG,
options: opts,
errors: [{ messageId: 'conn' }],
},
],
})
})
})