@aicqtools/guardrail 1.0.0-alpha.12 → 1.0.0-alpha.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs/render-rule-md.d.ts.map +1 -1
- package/dist/docs/render-rule-md.js +80 -0
- package/dist/docs/render-rule-md.js.map +1 -1
- package/dist/matcher/yaml-rule.d.ts +13 -0
- package/dist/matcher/yaml-rule.d.ts.map +1 -1
- package/dist/matcher/yaml-rule.js +32 -0
- package/dist/matcher/yaml-rule.js.map +1 -1
- package/dist/rules-default/camelcase-migration-column.d.ts.map +1 -1
- package/dist/rules-default/camelcase-migration-column.js +26 -2
- package/dist/rules-default/camelcase-migration-column.js.map +1 -1
- package/dist/rules-default/mask-pii-in-ai-prompt.d.ts.map +1 -1
- package/dist/rules-default/mask-pii-in-ai-prompt.js +44 -7
- package/dist/rules-default/mask-pii-in-ai-prompt.js.map +1 -1
- package/dist/rules-default/no-console-log.d.ts.map +1 -1
- package/dist/rules-default/no-console-log.js +22 -2
- package/dist/rules-default/no-console-log.js.map +1 -1
- package/dist/rules-default/no-empty-catch.d.ts +6 -0
- package/dist/rules-default/no-empty-catch.d.ts.map +1 -1
- package/dist/rules-default/no-empty-catch.js +42 -2
- package/dist/rules-default/no-empty-catch.js.map +1 -1
- package/dist/rules-default/no-fstring-sql.d.ts.map +1 -1
- package/dist/rules-default/no-fstring-sql.js +51 -2
- package/dist/rules-default/no-fstring-sql.js.map +1 -1
- package/dist/rules-default/no-magic-number.d.ts.map +1 -1
- package/dist/rules-default/no-magic-number.js +24 -4
- package/dist/rules-default/no-magic-number.js.map +1 -1
- package/dist/rules-default/no-print-in-prod.yaml +8 -0
- package/dist/runner/apply-rule-config.d.ts +43 -12
- package/dist/runner/apply-rule-config.d.ts.map +1 -1
- package/dist/runner/apply-rule-config.js +115 -41
- package/dist/runner/apply-rule-config.js.map +1 -1
- package/dist/runner/context.d.ts +7 -0
- package/dist/runner/context.d.ts.map +1 -1
- package/dist/runner/context.js +2 -0
- package/dist/runner/context.js.map +1 -1
- package/dist/runner/index.d.ts +4 -2
- package/dist/runner/index.d.ts.map +1 -1
- package/dist/runner/index.js +2 -1
- package/dist/runner/index.js.map +1 -1
- package/dist/runner/resolve-rule-options.d.ts +28 -0
- package/dist/runner/resolve-rule-options.d.ts.map +1 -0
- package/dist/runner/resolve-rule-options.js +27 -0
- package/dist/runner/resolve-rule-options.js.map +1 -0
- package/dist/runner/run-file.d.ts +16 -2
- package/dist/runner/run-file.d.ts.map +1 -1
- package/dist/runner/run-file.js +49 -7
- package/dist/runner/run-file.js.map +1 -1
- package/dist/runner/run-project.d.ts +20 -0
- package/dist/runner/run-project.d.ts.map +1 -1
- package/dist/runner/run-project.js +28 -5
- package/dist/runner/run-project.js.map +1 -1
- package/dist/runner/suppressions.d.ts +46 -1
- package/dist/runner/suppressions.d.ts.map +1 -1
- package/dist/runner/suppressions.js +101 -15
- package/dist/runner/suppressions.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-rule-md.d.ts","sourceRoot":"","sources":["../../src/docs/render-rule-md.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"render-rule-md.d.ts","sourceRoot":"","sources":["../../src/docs/render-rule-md.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAuHhD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAwF1E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAYpF"}
|
|
@@ -17,6 +17,84 @@ function ruleKindLabel(rule, locale) {
|
|
|
17
17
|
return locale === 'ko' ? 'YAML 패턴' : 'YAML pattern';
|
|
18
18
|
return locale === 'ko' ? 'JS/TS 함수' : 'JS/TS function';
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Alpha.14 — render the rule's `options` zod schema as a markdown table. Best-effort
|
|
22
|
+
* introspection: works for `z.object({ key: z.array(z.string()).default(...) })` shapes and
|
|
23
|
+
* similar. Anything we can't introspect cleanly falls back to "see source for shape".
|
|
24
|
+
*/
|
|
25
|
+
function describeZodType(schema) {
|
|
26
|
+
const def = schema._def;
|
|
27
|
+
switch (def?.typeName) {
|
|
28
|
+
case 'ZodString':
|
|
29
|
+
return 'string';
|
|
30
|
+
case 'ZodNumber':
|
|
31
|
+
return 'number';
|
|
32
|
+
case 'ZodBoolean':
|
|
33
|
+
return 'boolean';
|
|
34
|
+
case 'ZodArray': {
|
|
35
|
+
const inner = def.type;
|
|
36
|
+
return inner ? `array<${describeZodType(inner)}>` : 'array';
|
|
37
|
+
}
|
|
38
|
+
case 'ZodDefault': {
|
|
39
|
+
const inner = def.innerType;
|
|
40
|
+
return inner ? describeZodType(inner) : 'unknown';
|
|
41
|
+
}
|
|
42
|
+
case 'ZodOptional': {
|
|
43
|
+
const inner = def.innerType;
|
|
44
|
+
return inner ? `${describeZodType(inner)}?` : 'unknown?';
|
|
45
|
+
}
|
|
46
|
+
case 'ZodEnum': {
|
|
47
|
+
const values = def.values;
|
|
48
|
+
return values ? values.map((v) => `'${v}'`).join(' | ') : 'enum';
|
|
49
|
+
}
|
|
50
|
+
case 'ZodUnion':
|
|
51
|
+
return 'union';
|
|
52
|
+
case 'ZodObject':
|
|
53
|
+
return 'object';
|
|
54
|
+
default:
|
|
55
|
+
return 'unknown';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function renderOptionsTable(schema, defaults, locale) {
|
|
59
|
+
const def = schema._def;
|
|
60
|
+
if (def?.typeName !== 'ZodObject' || typeof def.shape !== 'function') {
|
|
61
|
+
return locale === 'ko' ? '_옵션 스키마 구조 확인은 룰 소스를 참고하세요._' : '_See rule source for option schema shape._';
|
|
62
|
+
}
|
|
63
|
+
const shape = def.shape();
|
|
64
|
+
const keys = Object.keys(shape);
|
|
65
|
+
if (keys.length === 0)
|
|
66
|
+
return null;
|
|
67
|
+
const header = locale === 'ko'
|
|
68
|
+
? '| 키 | 타입 | 기본값 |\n|----|------|--------|'
|
|
69
|
+
: '| Key | Type | Default |\n|-----|------|---------|';
|
|
70
|
+
const rows = keys.map((key) => {
|
|
71
|
+
const fieldSchema = shape[key];
|
|
72
|
+
const type = describeZodType(fieldSchema);
|
|
73
|
+
const def = defaults[key];
|
|
74
|
+
const defaultStr = def === undefined ? '—' : '`' + JSON.stringify(def) + '`';
|
|
75
|
+
return `| \`${key}\` | \`${type}\` | ${defaultStr} |`;
|
|
76
|
+
});
|
|
77
|
+
return [header, ...rows].join('\n');
|
|
78
|
+
}
|
|
79
|
+
function renderOptionsSection(rule, locale) {
|
|
80
|
+
if (!rule.options)
|
|
81
|
+
return [];
|
|
82
|
+
const heading = locale === 'ko' ? '## 옵션 (alpha.14)' : '## Options (alpha.14)';
|
|
83
|
+
const intro = locale === 'ko'
|
|
84
|
+
? '`aicq.config.yaml`에서 `rules.<룰 id>.options`로 재정의 가능. 잘못된 키/타입은 stderr 경고 후 기본값으로 복귀.'
|
|
85
|
+
: 'Override under `rules.<rule id>.options` in `aicq.config.yaml`. Invalid keys/types trigger a stderr warning and fall back to defaults.';
|
|
86
|
+
const table = renderOptionsTable(rule.options.schema, rule.options.defaults, locale);
|
|
87
|
+
const lines = [heading, '', intro, ''];
|
|
88
|
+
if (table)
|
|
89
|
+
lines.push(table, '');
|
|
90
|
+
const example = locale === 'ko' ? '예시:' : 'Example:';
|
|
91
|
+
lines.push(example, '', '```yaml', 'modules:', ' guardrail:', ' rules:', ` ${rule.id}:`, ' options:');
|
|
92
|
+
for (const [k, v] of Object.entries(rule.options.defaults)) {
|
|
93
|
+
lines.push(` ${k}: ${JSON.stringify(v)}`);
|
|
94
|
+
}
|
|
95
|
+
lines.push('```', '');
|
|
96
|
+
return lines;
|
|
97
|
+
}
|
|
20
98
|
export function renderRuleMarkdown(rule, locale) {
|
|
21
99
|
const messages = locale === 'ko' && rule.messageKo ? rule.messageKo : rule.message;
|
|
22
100
|
const severityLabel = locale === 'ko' ? SEVERITY_LABEL_KO[rule.severity] : SEVERITY_LABEL_EN[rule.severity];
|
|
@@ -33,6 +111,7 @@ export function renderRuleMarkdown(rule, locale) {
|
|
|
33
111
|
'',
|
|
34
112
|
messages,
|
|
35
113
|
'',
|
|
114
|
+
...renderOptionsSection(rule, 'ko'),
|
|
36
115
|
'## 위반 예시',
|
|
37
116
|
'',
|
|
38
117
|
'```typescript',
|
|
@@ -73,6 +152,7 @@ export function renderRuleMarkdown(rule, locale) {
|
|
|
73
152
|
'',
|
|
74
153
|
messages,
|
|
75
154
|
'',
|
|
155
|
+
...renderOptionsSection(rule, 'en'),
|
|
76
156
|
'## Violation example',
|
|
77
157
|
'',
|
|
78
158
|
'```typescript',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-rule-md.js","sourceRoot":"","sources":["../../src/docs/render-rule-md.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"render-rule-md.js","sourceRoot":"","sources":["../../src/docs/render-rule-md.ts"],"names":[],"mappings":"AAIA,MAAM,iBAAiB,GAA6B;IAClD,KAAK,EAAE,UAAU;IACjB,OAAO,EAAE,YAAY;IACrB,IAAI,EAAE,SAAS;CAChB,CAAC;AAEF,MAAM,iBAAiB,GAA6B;IAClD,KAAK,EAAE,OAAO;IACd,OAAO,EAAE,OAAO;IAChB,IAAI,EAAE,OAAO;CACd,CAAC;AAEF,SAAS,aAAa,CAAC,IAAU;IAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7E,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,aAAa,CAAC,IAAU,EAAE,MAAmB;IACpD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC;IACjF,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,gBAAgB,CAAC;AACzD,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,MAAkB;IACzC,MAAM,GAAG,GAAI,MAA2C,CAAC,IAAI,CAAC;IAC9D,QAAQ,GAAG,EAAE,QAAQ,EAAE,CAAC;QACtB,KAAK,WAAW;YACd,OAAO,QAAQ,CAAC;QAClB,KAAK,WAAW;YACd,OAAO,QAAQ,CAAC;QAClB,KAAK,YAAY;YACf,OAAO,SAAS,CAAC;QACnB,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,KAAK,GAAI,GAA6B,CAAC,IAAI,CAAC;YAClD,OAAO,KAAK,CAAC,CAAC,CAAC,SAAS,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;QAC9D,CAAC;QACD,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,MAAM,KAAK,GAAI,GAAkC,CAAC,SAAS,CAAC;YAC5D,OAAO,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACpD,CAAC;QACD,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,KAAK,GAAI,GAAkC,CAAC,SAAS,CAAC;YAC5D,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC;QAC3D,CAAC;QACD,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,MAAM,GAAI,GAAsC,CAAC,MAAM,CAAC;YAC9D,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QACnE,CAAC;QACD,KAAK,UAAU;YACb,OAAO,OAAO,CAAC;QACjB,KAAK,WAAW;YACd,OAAO,QAAQ,CAAC;QAClB;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CACzB,MAAkB,EAClB,QAA2C,EAC3C,MAAmB;IAEnB,MAAM,GAAG,GAAI,MAAqF,CAAC,IAAI,CAAC;IACxG,IAAI,GAAG,EAAE,QAAQ,KAAK,WAAW,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;QACrE,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,4CAA4C,CAAC;IACzG,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,EAAE,CAAC;IAC1B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,MAAM,GACV,MAAM,KAAK,IAAI;QACb,CAAC,CAAC,0CAA0C;QAC5C,CAAC,CAAC,oDAAoD,CAAC;IAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QAC5B,MAAM,WAAW,GAAG,KAAK,CAAC,GAAG,CAAE,CAAC;QAChC,MAAM,IAAI,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC1B,MAAM,UAAU,GACd,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;QAC5D,OAAO,OAAO,GAAG,UAAU,IAAI,QAAQ,UAAU,IAAI,CAAC;IACxD,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAU,EAAE,MAAmB;IAC3D,IAAI,CAAC,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,uBAAuB,CAAC;IAC/E,MAAM,KAAK,GACT,MAAM,KAAK,IAAI;QACb,CAAC,CAAC,sFAAsF;QACxF,CAAC,CAAC,wIAAwI,CAAC;IAC/I,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACrF,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;IACvC,IAAI,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACjC,MAAM,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC;IACrD,KAAK,CAAC,IAAI,CACR,OAAO,EACP,EAAE,EACF,SAAS,EACT,UAAU,EACV,cAAc,EACd,YAAY,EACZ,SAAS,IAAI,CAAC,EAAE,GAAG,EACnB,kBAAkB,CACnB,CAAC;IACF,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACtB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAU,EAAE,MAAmB;IAChE,MAAM,QAAQ,GAAG,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;IACnF,MAAM,aAAa,GAAG,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC5G,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAE7D,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO;YACL,KAAK,IAAI,CAAC,EAAE,EAAE;YACd,EAAE;YACF,iBAAiB,aAAa,IAAI;YAClC,kBAAkB,aAAa,CAAC,IAAI,CAAC,IAAI;YACzC,aAAa,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,QAAQ,EAAE;YACnD,EAAE;YACF,OAAO;YACP,EAAE;YACF,QAAQ;YACR,EAAE;YACF,GAAG,oBAAoB,CAAC,IAAI,EAAE,IAAI,CAAC;YACnC,UAAU;YACV,EAAE;YACF,eAAe;YACf,iCAAiC;YACjC,KAAK;YACL,EAAE;YACF,UAAU;YACV,EAAE;YACF,eAAe;YACf,oBAAoB;YACpB,KAAK;YACL,EAAE;YACF,WAAW;YACX,EAAE;YACF,uEAAuE;YACvE,EAAE;YACF,SAAS;YACT,EAAE;YACF,yCAAyC;YACzC,EAAE;YACF,SAAS;YACT,UAAU;YACV,cAAc;YACd,YAAY;YACZ,SAAS,IAAI,CAAC,EAAE,OAAO;YACvB,KAAK;YACL,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,OAAO;QACL,KAAK,IAAI,CAAC,EAAE,EAAE;QACd,EAAE;QACF,iBAAiB,aAAa,IAAI;QAClC,kBAAkB,aAAa,CAAC,IAAI,CAAC,IAAI;QACzC,aAAa,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,QAAQ,EAAE;QACnD,EAAE;QACF,gBAAgB;QAChB,EAAE;QACF,QAAQ;QACR,EAAE;QACF,GAAG,oBAAoB,CAAC,IAAI,EAAE,IAAI,CAAC;QACnC,sBAAsB;QACtB,EAAE;QACF,eAAe;QACf,+DAA+D;QAC/D,KAAK;QACL,EAAE;QACF,oBAAoB;QACpB,EAAE;QACF,eAAe;QACf,uCAAuC;QACvC,KAAK;QACL,EAAE;QACF,sBAAsB;QACtB,EAAE;QACF,2GAA2G;QAC3G,EAAE;QACF,wBAAwB;QACxB,EAAE;QACF,wBAAwB;QACxB,EAAE;QACF,SAAS;QACT,UAAU;QACV,cAAc;QACd,YAAY;QACZ,SAAS,IAAI,CAAC,EAAE,OAAO;QACvB,KAAK;QACL,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAsB,EAAE,MAAmB;IAC1E,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACnE,MAAM,MAAM,GACV,MAAM,KAAK,IAAI;QACb,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,sGAAsG;QAC7H,CAAC,CAAC,cAAc,KAAK,CAAC,MAAM,iIAAiI,CAAC;IAClK,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC7E,MAAM,GAAG,GAAG,CAAC,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACxG,OAAO,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,QAAQ,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC/E,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -9,12 +9,22 @@ declare const yamlRuleSchema: z.ZodObject<{
|
|
|
9
9
|
docs: z.ZodOptional<z.ZodString>;
|
|
10
10
|
pathExclude: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
11
11
|
query: z.ZodString;
|
|
12
|
+
options: z.ZodOptional<z.ZodObject<{
|
|
13
|
+
defaults: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
14
|
+
}, "strict", z.ZodTypeAny, {
|
|
15
|
+
defaults: Record<string, unknown>;
|
|
16
|
+
}, {
|
|
17
|
+
defaults: Record<string, unknown>;
|
|
18
|
+
}>>;
|
|
12
19
|
}, "strip", z.ZodTypeAny, {
|
|
13
20
|
language: "typescript" | "javascript" | "tsx" | "python" | ("typescript" | "javascript" | "tsx" | "python")[];
|
|
14
21
|
query: string;
|
|
15
22
|
id: string;
|
|
16
23
|
message: string;
|
|
17
24
|
severity: "error" | "warning" | "info";
|
|
25
|
+
options?: {
|
|
26
|
+
defaults: Record<string, unknown>;
|
|
27
|
+
} | undefined;
|
|
18
28
|
messageKo?: string | undefined;
|
|
19
29
|
docs?: string | undefined;
|
|
20
30
|
pathExclude?: string[] | undefined;
|
|
@@ -24,6 +34,9 @@ declare const yamlRuleSchema: z.ZodObject<{
|
|
|
24
34
|
id: string;
|
|
25
35
|
message: string;
|
|
26
36
|
severity: "error" | "warning" | "info";
|
|
37
|
+
options?: {
|
|
38
|
+
defaults: Record<string, unknown>;
|
|
39
|
+
} | undefined;
|
|
27
40
|
messageKo?: string | undefined;
|
|
28
41
|
docs?: string | undefined;
|
|
29
42
|
pathExclude?: string[] | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"yaml-rule.d.ts","sourceRoot":"","sources":["../../src/matcher/yaml-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"yaml-rule.d.ts","sourceRoot":"","sources":["../../src/matcher/yaml-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAmB,MAAM,KAAK,CAAC;AAEzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAmBvD,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUlB,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAiB3D,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAuBzD"}
|
|
@@ -2,6 +2,18 @@ import { z } from 'zod';
|
|
|
2
2
|
import { parse as parseYaml } from 'yaml';
|
|
3
3
|
const languageSchema = z.enum(['typescript', 'javascript', 'tsx', 'python']);
|
|
4
4
|
const severitySchema = z.enum(['error', 'warning', 'info']);
|
|
5
|
+
/**
|
|
6
|
+
* Alpha.18 — YAML PatternRule 옵션 framework 편입. YAML rule이 `options.defaults` (Record)
|
|
7
|
+
* 만 적으면 runtime에 그 키 set으로 strict object schema를 자동 합성. 사용자가 config의
|
|
8
|
+
* `rules.<id>.options`로 override 가능 (알파.14 framework 통과). YAML 작성자가 zod schema를
|
|
9
|
+
* 직접 적을 수 없으므로 값 타입 검증은 `z.unknown()`까지만 — strict object가 typo 키만 캐치.
|
|
10
|
+
* Query 동적 substitution은 v1.0+ 후속 사이클.
|
|
11
|
+
*/
|
|
12
|
+
const yamlOptionsSchema = z
|
|
13
|
+
.object({
|
|
14
|
+
defaults: z.record(z.unknown()),
|
|
15
|
+
})
|
|
16
|
+
.strict();
|
|
5
17
|
const yamlRuleSchema = z.object({
|
|
6
18
|
id: z.string().min(1),
|
|
7
19
|
language: z.union([languageSchema, z.array(languageSchema).min(1)]),
|
|
@@ -11,9 +23,28 @@ const yamlRuleSchema = z.object({
|
|
|
11
23
|
docs: z.string().url().optional(),
|
|
12
24
|
pathExclude: z.array(z.string().min(1)).optional(),
|
|
13
25
|
query: z.string().min(1),
|
|
26
|
+
options: yamlOptionsSchema.optional(),
|
|
14
27
|
});
|
|
28
|
+
/**
|
|
29
|
+
* Build a strict zod object schema from a defaults record. Each key gets `z.unknown()` so YAML
|
|
30
|
+
* rule writers can declare option shapes without a zod DSL — typo keys are still caught at run
|
|
31
|
+
* time by the `.strict()` modifier when users override the option in `aicq.config.yaml`.
|
|
32
|
+
*/
|
|
33
|
+
function buildOptionsSchemaFromDefaults(defaults) {
|
|
34
|
+
const shape = {};
|
|
35
|
+
for (const key of Object.keys(defaults)) {
|
|
36
|
+
shape[key] = z.unknown();
|
|
37
|
+
}
|
|
38
|
+
return z.object(shape).strict();
|
|
39
|
+
}
|
|
15
40
|
export function parseYamlRule(source) {
|
|
16
41
|
const parsed = yamlRuleSchema.parse(parseYaml(source));
|
|
42
|
+
const options = parsed.options
|
|
43
|
+
? {
|
|
44
|
+
schema: buildOptionsSchemaFromDefaults(parsed.options.defaults),
|
|
45
|
+
defaults: { ...parsed.options.defaults },
|
|
46
|
+
}
|
|
47
|
+
: undefined;
|
|
17
48
|
const rule = {
|
|
18
49
|
kind: 'pattern',
|
|
19
50
|
id: parsed.id,
|
|
@@ -26,6 +57,7 @@ export function parseYamlRule(source) {
|
|
|
26
57
|
...(parsed.pathExclude !== undefined && parsed.pathExclude.length > 0
|
|
27
58
|
? { pathExclude: parsed.pathExclude }
|
|
28
59
|
: {}),
|
|
60
|
+
...(options !== undefined ? { options } : {}),
|
|
29
61
|
};
|
|
30
62
|
return rule;
|
|
31
63
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"yaml-rule.js","sourceRoot":"","sources":["../../src/matcher/yaml-rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"yaml-rule.js","sourceRoot":"","sources":["../../src/matcher/yaml-rule.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"camelcase-migration-column.d.ts","sourceRoot":"","sources":["../../src/rules-default/camelcase-migration-column.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"camelcase-migration-column.d.ts","sourceRoot":"","sources":["../../src/rules-default/camelcase-migration-column.ts"],"names":[],"mappings":";AAuCA,wBAwCG"}
|
|
@@ -1,6 +1,22 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import { defineRule } from '@aicqtools/rule-sdk';
|
|
2
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Default Sequelize migration functions whose first object-shaped argument we scan for
|
|
5
|
+
* snake_case column keys. Alpha.16 promotes this to `options.migrationFunctions` so users
|
|
6
|
+
* on Knex / TypeORM / custom ORMs can swap in their own function names (e.g. Knex
|
|
7
|
+
* `['create_table', 'add_column']`). Default keeps alpha.15 behavior bit-for-bit identical.
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_MIGRATION_FUNCTIONS = [
|
|
10
|
+
'createTable',
|
|
11
|
+
'addColumn',
|
|
12
|
+
'changeColumn',
|
|
13
|
+
];
|
|
3
14
|
const SNAKE_CASE_PATTERN = /^[a-z][a-z0-9]*(_[a-z0-9]+)+$/;
|
|
15
|
+
const optionsSchema = z
|
|
16
|
+
.object({
|
|
17
|
+
migrationFunctions: z.array(z.string()).default([...DEFAULT_MIGRATION_FUNCTIONS]),
|
|
18
|
+
})
|
|
19
|
+
.strict();
|
|
4
20
|
function findColumnDefObject(callNode) {
|
|
5
21
|
const args = callNode.childForFieldName('arguments');
|
|
6
22
|
if (!args)
|
|
@@ -20,6 +36,10 @@ export default defineRule({
|
|
|
20
36
|
message: 'Sequelize migration column should use camelCase to match model attribute (avoid `underscored: true` divergence).',
|
|
21
37
|
messageKo: 'Sequelize 마이그레이션 컬럼명은 모델과 일치하도록 camelCase 사용 (snake_case는 underscored 옵션 충돌 위험).',
|
|
22
38
|
docs: 'https://github.com/aicqtools/aicqtools/blob/main/docs/rules/camelcase-migration-column.md',
|
|
39
|
+
options: {
|
|
40
|
+
schema: optionsSchema,
|
|
41
|
+
defaults: { migrationFunctions: [...DEFAULT_MIGRATION_FUNCTIONS] },
|
|
42
|
+
},
|
|
23
43
|
visitors: {
|
|
24
44
|
call_expression(node, ctx) {
|
|
25
45
|
const fn = node.childForFieldName('function');
|
|
@@ -32,7 +52,11 @@ export default defineRule({
|
|
|
32
52
|
// Only inside `queryInterface.createTable(...)` etc.
|
|
33
53
|
if (ctx.textOf(obj) !== 'queryInterface')
|
|
34
54
|
return;
|
|
35
|
-
|
|
55
|
+
const opts = ctx.options ?? {
|
|
56
|
+
migrationFunctions: DEFAULT_MIGRATION_FUNCTIONS,
|
|
57
|
+
};
|
|
58
|
+
const fnSet = new Set(opts.migrationFunctions);
|
|
59
|
+
if (!fnSet.has(ctx.textOf(prop)))
|
|
36
60
|
return;
|
|
37
61
|
const colObj = findColumnDefObject(node);
|
|
38
62
|
if (!colObj)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"camelcase-migration-column.js","sourceRoot":"","sources":["../../src/rules-default/camelcase-migration-column.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,
|
|
1
|
+
{"version":3,"file":"camelcase-migration-column.js","sourceRoot":"","sources":["../../src/rules-default/camelcase-migration-column.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mask-pii-in-ai-prompt.d.ts","sourceRoot":"","sources":["../../src/rules-default/mask-pii-in-ai-prompt.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"mask-pii-in-ai-prompt.d.ts","sourceRoot":"","sources":["../../src/rules-default/mask-pii-in-ai-prompt.ts"],"names":[],"mappings":";AAuDA,wBA4BG"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import { defineRule } from '@aicqtools/rule-sdk';
|
|
2
3
|
/**
|
|
3
4
|
* FSC AI guideline: personal information (RRN, card number) must be masked
|
|
@@ -5,14 +6,40 @@ import { defineRule } from '@aicqtools/rule-sdk';
|
|
|
5
6
|
* (\d{6}-\d{7}) or 16-digit card numbers in string/template arguments of
|
|
6
7
|
* AI SDK calls.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
9
|
+
* Alpha.16: `options.piiPatterns: string[]` (regex source array) — default is the alpha.15
|
|
10
|
+
* RRN + card-number pair. Users can extend (passport numbers, account numbers) or replace
|
|
11
|
+
* the set. The AI SDK call pattern (`AI_CALL`) stays a fixed default this cycle — exposing
|
|
12
|
+
* it as an option is deferred to a future release.
|
|
13
|
+
*
|
|
14
|
+
* Limitation: false negatives if PII enters via interpolated variables; false positives if a
|
|
15
|
+
* legitimate test fixture contains a digit run. User-supplied regex source strings are not
|
|
16
|
+
* safety-checked (catastrophic backtracking is the user's responsibility).
|
|
10
17
|
*/
|
|
11
|
-
const
|
|
12
|
-
|
|
18
|
+
const DEFAULT_PII_PATTERNS = [
|
|
19
|
+
'\\b\\d{6}-\\d{7}\\b',
|
|
20
|
+
'\\b(?:\\d[ -]?){15,16}\\b',
|
|
21
|
+
];
|
|
13
22
|
const AI_CALL = /\b(openai|anthropic|aiClient)\.\w+/;
|
|
14
|
-
|
|
15
|
-
|
|
23
|
+
const optionsSchema = z
|
|
24
|
+
.object({
|
|
25
|
+
piiPatterns: z.array(z.string()).default([...DEFAULT_PII_PATTERNS]),
|
|
26
|
+
})
|
|
27
|
+
.strict();
|
|
28
|
+
// Module-scope compile cache — when the same source array repeats across calls (common case:
|
|
29
|
+
// one `applyRuleConfig` per run), we skip the `new RegExp` cost. Reference equality on the
|
|
30
|
+
// source array key is sufficient for resolve-rule-options call patterns.
|
|
31
|
+
let cachedSource = null;
|
|
32
|
+
let cachedCompiled = null;
|
|
33
|
+
function compilePiiPatterns(source) {
|
|
34
|
+
if (cachedSource === source && cachedCompiled !== null)
|
|
35
|
+
return cachedCompiled;
|
|
36
|
+
const compiled = source.map((s) => new RegExp(s));
|
|
37
|
+
cachedSource = source;
|
|
38
|
+
cachedCompiled = compiled;
|
|
39
|
+
return compiled;
|
|
40
|
+
}
|
|
41
|
+
function inspectArgsString(text, patterns) {
|
|
42
|
+
return patterns.some((re) => re.test(text));
|
|
16
43
|
}
|
|
17
44
|
export default defineRule({
|
|
18
45
|
id: 'mask-pii-in-ai-prompt',
|
|
@@ -21,6 +48,10 @@ export default defineRule({
|
|
|
21
48
|
message: 'AI prompt contains unmasked PII (Korean RRN or card number) — mask before sending (FSC AI guideline — privacy protection).',
|
|
22
49
|
messageKo: 'AI 프롬프트에 마스킹되지 않은 개인정보(주민번호/카드번호)가 포함되어 있습니다 — AI 호출 전 마스킹 필수 (금감원 AI 가이드라인 — 개인정보 보호).',
|
|
23
50
|
docs: 'https://github.com/aicqtools/aicqtools/blob/main/docs/rules/mask-pii-in-ai-prompt.md',
|
|
51
|
+
options: {
|
|
52
|
+
schema: optionsSchema,
|
|
53
|
+
defaults: { piiPatterns: [...DEFAULT_PII_PATTERNS] },
|
|
54
|
+
},
|
|
24
55
|
visitors: {
|
|
25
56
|
call_expression(node, ctx) {
|
|
26
57
|
const fnNode = node.childForFieldName('function');
|
|
@@ -33,7 +64,13 @@ export default defineRule({
|
|
|
33
64
|
if (!args)
|
|
34
65
|
return;
|
|
35
66
|
const argsText = ctx.textOf(args);
|
|
36
|
-
|
|
67
|
+
const opts = ctx.options ?? {
|
|
68
|
+
piiPatterns: DEFAULT_PII_PATTERNS,
|
|
69
|
+
};
|
|
70
|
+
if (opts.piiPatterns.length === 0)
|
|
71
|
+
return;
|
|
72
|
+
const compiled = compilePiiPatterns(opts.piiPatterns);
|
|
73
|
+
if (inspectArgsString(argsText, compiled))
|
|
37
74
|
ctx.report({ node });
|
|
38
75
|
},
|
|
39
76
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mask-pii-in-ai-prompt.js","sourceRoot":"","sources":["../../src/rules-default/mask-pii-in-ai-prompt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGjD
|
|
1
|
+
{"version":3,"file":"mask-pii-in-ai-prompt.js","sourceRoot":"","sources":["../../src/rules-default/mask-pii-in-ai-prompt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGjD;;;;;;;;;;;;;;GAcG;AACH,MAAM,oBAAoB,GAAsB;IAC9C,qBAAqB;IACrB,2BAA2B;CACnB,CAAC;AAEX,MAAM,OAAO,GAAG,oCAAoC,CAAC;AAErD,MAAM,aAAa,GAAG,CAAC;KACpB,MAAM,CAAC;IACN,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,oBAAoB,CAAC,CAAC;CACpE,CAAC;KACD,MAAM,EAAE,CAAC;AAMZ,6FAA6F;AAC7F,2FAA2F;AAC3F,yEAAyE;AACzE,IAAI,YAAY,GAA6B,IAAI,CAAC;AAClD,IAAI,cAAc,GAA6B,IAAI,CAAC;AAEpD,SAAS,kBAAkB,CAAC,MAAyB;IACnD,IAAI,YAAY,KAAK,MAAM,IAAI,cAAc,KAAK,IAAI;QAAE,OAAO,cAAc,CAAC;IAC9E,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,YAAY,GAAG,MAAM,CAAC;IACtB,cAAc,GAAG,QAAQ,CAAC;IAC1B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAY,EAAE,QAA2B;IAClE,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,uBAAuB;IAC3B,QAAQ,EAAE,CAAC,YAAY,EAAE,KAAK,CAAC;IAC/B,QAAQ,EAAE,OAAO;IACjB,OAAO,EAAE,4HAA4H;IACrI,SAAS,EAAE,yFAAyF;IACpG,IAAI,EAAE,sFAAsF;IAC5F,OAAO,EAAE;QACP,MAAM,EAAE,aAAa;QACrB,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,GAAG,oBAAoB,CAAC,EAAE;KACrD;IACD,QAAQ,EAAE;QACR,eAAe,CAAC,IAAuB,EAAE,GAAgB;YACvD,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;YAClD,IAAI,CAAC,MAAM;gBAAE,OAAO;YACpB,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAClC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;gBAAE,OAAO;YAClC,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;YACjD,IAAI,CAAC,IAAI;gBAAE,OAAO;YAClB,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,IAAI,GAAI,GAAG,CAAC,OAAgD,IAAI;gBACpE,WAAW,EAAE,oBAAoB;aAClC,CAAC;YACF,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YAC1C,MAAM,QAAQ,GAAG,kBAAkB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACtD,IAAI,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC;gBAAE,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,CAAC;KACF;CACF,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-console-log.d.ts","sourceRoot":"","sources":["../../src/rules-default/no-console-log.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"no-console-log.d.ts","sourceRoot":"","sources":["../../src/rules-default/no-console-log.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,eAAO,MAAM,YAAY,QAAkC,CAAC;;AAmB5D,wBAwBG"}
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import { defineRule } from '@aicqtools/rule-sdk';
|
|
2
|
+
import { z } from 'zod';
|
|
2
3
|
/**
|
|
3
4
|
* Skip build/utility script directories where `console.log` is the intended I/O channel
|
|
4
5
|
* (`scripts/`, `tools/`, `bin/` — same convention as alpha.8's `seeders/`+`migrations/`
|
|
5
6
|
* skip for `no-magic-number`). The rule continues to fire in application source.
|
|
6
7
|
*/
|
|
7
8
|
export const SKIP_FILE_RE = /[/\\](scripts|tools|bin)[/\\]/;
|
|
9
|
+
/**
|
|
10
|
+
* Default detection set — alpha.13 behavior was "flag `console.log` only". Alpha.15 promotes
|
|
11
|
+
* this to a configurable allowlist via `options.flagMethods` so users can extend detection to
|
|
12
|
+
* `console.debug`, `console.warn`, etc. (or shrink to `[]` for an intentional mute).
|
|
13
|
+
*/
|
|
14
|
+
const DEFAULT_FLAG_METHODS = ['log'];
|
|
15
|
+
const optionsSchema = z
|
|
16
|
+
.object({
|
|
17
|
+
flagMethods: z.array(z.string()).default([...DEFAULT_FLAG_METHODS]),
|
|
18
|
+
})
|
|
19
|
+
.strict();
|
|
8
20
|
export default defineRule({
|
|
9
21
|
id: 'no-console-log',
|
|
10
22
|
language: ['typescript', 'javascript', 'tsx'],
|
|
@@ -12,15 +24,23 @@ export default defineRule({
|
|
|
12
24
|
message: 'Avoid console.log in production code.',
|
|
13
25
|
messageKo: '운영 코드에서 console.log 사용을 피하세요.',
|
|
14
26
|
skipPatterns: [SKIP_FILE_RE],
|
|
27
|
+
options: {
|
|
28
|
+
schema: optionsSchema,
|
|
29
|
+
defaults: { flagMethods: [...DEFAULT_FLAG_METHODS] },
|
|
30
|
+
},
|
|
15
31
|
visitors: {
|
|
16
32
|
call_expression(node, ctx) {
|
|
17
|
-
if (SKIP_FILE_RE.test(ctx.filePath))
|
|
33
|
+
if (!ctx.skipBuiltinSkips && SKIP_FILE_RE.test(ctx.filePath))
|
|
18
34
|
return;
|
|
19
35
|
const fn = node.childForFieldName('function');
|
|
20
36
|
if (!fn)
|
|
21
37
|
return;
|
|
22
38
|
const text = ctx.textOf(fn);
|
|
23
|
-
|
|
39
|
+
const opts = ctx.options ?? {
|
|
40
|
+
flagMethods: DEFAULT_FLAG_METHODS,
|
|
41
|
+
};
|
|
42
|
+
const flagged = new Set(opts.flagMethods.map((m) => 'console.' + m));
|
|
43
|
+
if (flagged.has(text))
|
|
24
44
|
ctx.report({ node });
|
|
25
45
|
},
|
|
26
46
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-console-log.js","sourceRoot":"","sources":["../../src/rules-default/no-console-log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"no-console-log.js","sourceRoot":"","sources":["../../src/rules-default/no-console-log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,+BAA+B,CAAC;AAE5D;;;;GAIG;AACH,MAAM,oBAAoB,GAAsB,CAAC,KAAK,CAAU,CAAC;AAEjE,MAAM,aAAa,GAAG,CAAC;KACpB,MAAM,CAAC;IACN,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,oBAAoB,CAAC,CAAC;CACpE,CAAC;KACD,MAAM,EAAE,CAAC;AAMZ,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,gBAAgB;IACpB,QAAQ,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,KAAK,CAAC;IAC7C,QAAQ,EAAE,SAAS;IACnB,OAAO,EAAE,uCAAuC;IAChD,SAAS,EAAE,+BAA+B;IAC1C,YAAY,EAAE,CAAC,YAAY,CAAC;IAC5B,OAAO,EAAE;QACP,MAAM,EAAE,aAAa;QACrB,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,GAAG,oBAAoB,CAAC,EAAE;KACrD;IACD,QAAQ,EAAE;QACR,eAAe,CAAC,IAAI,EAAE,GAAG;YACvB,IAAI,CAAC,GAAG,CAAC,gBAAgB,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAAE,OAAO;YACrE,MAAM,EAAE,GAAG,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;YAC9C,IAAI,CAAC,EAAE;gBAAE,OAAO;YAChB,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC5B,MAAM,IAAI,GAAI,GAAG,CAAC,OAA2C,IAAI;gBAC/D,WAAW,EAAE,oBAAoB;aAClC,CAAC;YACF,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC;YACrE,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,CAAC;KACF;CACF,CAAC,CAAC"}
|
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
* Skip: Capacitor's `native-bridge.{js,ts}` and PWA `service-worker.{js,ts}` regularly
|
|
6
6
|
* swallow exceptions on purpose (the bridge/SW must never crash the host). The rule still
|
|
7
7
|
* fires elsewhere on the same files via other lints.
|
|
8
|
+
*
|
|
9
|
+
* Alpha.15: `SKIP_FILE_RE`'s pattern is promoted to a configurable option
|
|
10
|
+
* (`options.skipFilePatterns`) so users can extend / replace the skip set. The named export
|
|
11
|
+
* is preserved for the alpha.12 meta-vs-code equality guard — `skipPatterns: [SKIP_FILE_RE]`
|
|
12
|
+
* reflects the built-in default only. If the user overrides `skipFilePatterns`, runtime
|
|
13
|
+
* matching diverges from the meta (intentional — `aicq rules suggest` keeps showing defaults).
|
|
8
14
|
*/
|
|
9
15
|
export declare const SKIP_FILE_RE: RegExp;
|
|
10
16
|
declare const _default: import("@aicqtools/rule-sdk").FunctionRule;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-empty-catch.d.ts","sourceRoot":"","sources":["../../src/rules-default/no-empty-catch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"no-empty-catch.d.ts","sourceRoot":"","sources":["../../src/rules-default/no-empty-catch.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,YAAY,QAAkD,CAAC;;AA8B5E,wBAmCG"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineRule } from '@aicqtools/rule-sdk';
|
|
2
|
+
import { z } from 'zod';
|
|
2
3
|
/**
|
|
3
4
|
* Forbids empty catch blocks. At minimum, log or rethrow.
|
|
4
5
|
* Catches the silent-failure anti-pattern that masks bugs.
|
|
@@ -6,8 +7,35 @@ import { defineRule } from '@aicqtools/rule-sdk';
|
|
|
6
7
|
* Skip: Capacitor's `native-bridge.{js,ts}` and PWA `service-worker.{js,ts}` regularly
|
|
7
8
|
* swallow exceptions on purpose (the bridge/SW must never crash the host). The rule still
|
|
8
9
|
* fires elsewhere on the same files via other lints.
|
|
10
|
+
*
|
|
11
|
+
* Alpha.15: `SKIP_FILE_RE`'s pattern is promoted to a configurable option
|
|
12
|
+
* (`options.skipFilePatterns`) so users can extend / replace the skip set. The named export
|
|
13
|
+
* is preserved for the alpha.12 meta-vs-code equality guard — `skipPatterns: [SKIP_FILE_RE]`
|
|
14
|
+
* reflects the built-in default only. If the user overrides `skipFilePatterns`, runtime
|
|
15
|
+
* matching diverges from the meta (intentional — `aicq rules suggest` keeps showing defaults).
|
|
9
16
|
*/
|
|
10
17
|
export const SKIP_FILE_RE = /[/\\](native-bridge|service-worker)\.[jt]sx?$/;
|
|
18
|
+
const DEFAULT_SKIP_PATTERNS = [
|
|
19
|
+
'[/\\\\](native-bridge|service-worker)\\.[jt]sx?$',
|
|
20
|
+
];
|
|
21
|
+
const optionsSchema = z
|
|
22
|
+
.object({
|
|
23
|
+
skipFilePatterns: z.array(z.string()).default([...DEFAULT_SKIP_PATTERNS]),
|
|
24
|
+
})
|
|
25
|
+
.strict();
|
|
26
|
+
// Module-scope compile cache — when the same option source array repeats across calls (the
|
|
27
|
+
// common case: one `applyRuleConfig` per run), we skip the `new RegExp` cost. Cache invalidates
|
|
28
|
+
// when source array reference changes (good enough for the resolve-rule-options call pattern).
|
|
29
|
+
let cachedSource = null;
|
|
30
|
+
let cachedCompiled = null;
|
|
31
|
+
function compileSkipPatterns(source) {
|
|
32
|
+
if (cachedSource === source && cachedCompiled !== null)
|
|
33
|
+
return cachedCompiled;
|
|
34
|
+
const compiled = source.map((s) => new RegExp(s));
|
|
35
|
+
cachedSource = source;
|
|
36
|
+
cachedCompiled = compiled;
|
|
37
|
+
return compiled;
|
|
38
|
+
}
|
|
11
39
|
export default defineRule({
|
|
12
40
|
id: 'no-empty-catch',
|
|
13
41
|
language: ['typescript', 'javascript', 'tsx'],
|
|
@@ -15,10 +43,22 @@ export default defineRule({
|
|
|
15
43
|
message: 'catch block must not be empty — log or rethrow.',
|
|
16
44
|
messageKo: 'catch 블록은 비어 있을 수 없습니다 — log하거나 rethrow하세요.',
|
|
17
45
|
skipPatterns: [SKIP_FILE_RE],
|
|
46
|
+
options: {
|
|
47
|
+
schema: optionsSchema,
|
|
48
|
+
defaults: { skipFilePatterns: [...DEFAULT_SKIP_PATTERNS] },
|
|
49
|
+
},
|
|
18
50
|
visitors: {
|
|
19
51
|
catch_clause(node, ctx) {
|
|
20
|
-
|
|
21
|
-
|
|
52
|
+
// Alpha.13 escape hatch: `skipBuiltinSkips=true` bypasses BOTH the built-in skip and any
|
|
53
|
+
// user-supplied `skipFilePatterns` (global short-circuit, plan decision #4).
|
|
54
|
+
if (!ctx.skipBuiltinSkips) {
|
|
55
|
+
const opts = ctx.options ?? {
|
|
56
|
+
skipFilePatterns: DEFAULT_SKIP_PATTERNS,
|
|
57
|
+
};
|
|
58
|
+
const compiled = compileSkipPatterns(opts.skipFilePatterns);
|
|
59
|
+
if (compiled.some((re) => re.test(ctx.filePath)))
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
22
62
|
const body = node.childForFieldName('body');
|
|
23
63
|
if (!body)
|
|
24
64
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-empty-catch.js","sourceRoot":"","sources":["../../src/rules-default/no-empty-catch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"no-empty-catch.js","sourceRoot":"","sources":["../../src/rules-default/no-empty-catch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,+CAA+C,CAAC;AAE5E,MAAM,qBAAqB,GAAsB;IAC/C,kDAAkD;CAC1C,CAAC;AAEX,MAAM,aAAa,GAAG,CAAC;KACpB,MAAM,CAAC;IACN,gBAAgB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,qBAAqB,CAAC,CAAC;CAC1E,CAAC;KACD,MAAM,EAAE,CAAC;AAMZ,2FAA2F;AAC3F,gGAAgG;AAChG,+FAA+F;AAC/F,IAAI,YAAY,GAA6B,IAAI,CAAC;AAClD,IAAI,cAAc,GAA6B,IAAI,CAAC;AAEpD,SAAS,mBAAmB,CAAC,MAAyB;IACpD,IAAI,YAAY,KAAK,MAAM,IAAI,cAAc,KAAK,IAAI;QAAE,OAAO,cAAc,CAAC;IAC9E,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,YAAY,GAAG,MAAM,CAAC;IACtB,cAAc,GAAG,QAAQ,CAAC;IAC1B,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,gBAAgB;IACpB,QAAQ,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,KAAK,CAAC;IAC7C,QAAQ,EAAE,OAAO;IACjB,OAAO,EAAE,iDAAiD;IAC1D,SAAS,EAAE,6CAA6C;IACxD,YAAY,EAAE,CAAC,YAAY,CAAC;IAC5B,OAAO,EAAE;QACP,MAAM,EAAE,aAAa;QACrB,QAAQ,EAAE,EAAE,gBAAgB,EAAE,CAAC,GAAG,qBAAqB,CAAC,EAAE;KAC3D;IACD,QAAQ,EAAE;QACR,YAAY,CAAC,IAAI,EAAE,GAAG;YACpB,yFAAyF;YACzF,6EAA6E;YAC7E,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;gBAC1B,MAAM,IAAI,GAAI,GAAG,CAAC,OAA2C,IAAI;oBAC/D,gBAAgB,EAAE,qBAAqB;iBACxC,CAAC;gBACF,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;gBAC5D,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;oBAAE,OAAO;YAC3D,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,CAAC,IAAI;gBAAE,OAAO;YAClB,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBACjC,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBACtC,OAAO,GAAG,IAAI,CAAC;oBACf,MAAM;gBACR,CAAC;YACH,CAAC;YACD,IAAI,CAAC,OAAO;gBAAE,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACrC,CAAC;KACF;CACF,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-fstring-sql.d.ts","sourceRoot":"","sources":["../../src/rules-default/no-fstring-sql.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"no-fstring-sql.d.ts","sourceRoot":"","sources":["../../src/rules-default/no-fstring-sql.ts"],"names":[],"mappings":";AAkDA,wBA0BG"}
|
|
@@ -1,18 +1,67 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import { defineRule } from '@aicqtools/rule-sdk';
|
|
2
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Default SQL keywords flagged inside Python f-strings. Alpha.16 exposes this list as
|
|
5
|
+
* `options.sqlKeywords` so projects can shrink it (e.g. `['SELECT']` only) or extend it
|
|
6
|
+
* (e.g. add `'CREATE TABLE'`). Default keeps alpha.15 behavior bit-for-bit identical.
|
|
7
|
+
*
|
|
8
|
+
* Multi-token keywords like `'CREATE TABLE'` work via `\b(...)\b` with the literal space
|
|
9
|
+
* preserved — note that double spaces or newlines between tokens won't match, and meta
|
|
10
|
+
* characters are escaped via `escapeRegex` to keep user-supplied keywords safe.
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_SQL_KEYWORDS = [
|
|
13
|
+
'SELECT',
|
|
14
|
+
'INSERT',
|
|
15
|
+
'UPDATE',
|
|
16
|
+
'DELETE',
|
|
17
|
+
'FROM',
|
|
18
|
+
'WHERE',
|
|
19
|
+
'JOIN',
|
|
20
|
+
'VALUES',
|
|
21
|
+
];
|
|
22
|
+
const optionsSchema = z
|
|
23
|
+
.object({
|
|
24
|
+
sqlKeywords: z.array(z.string()).default([...DEFAULT_SQL_KEYWORDS]),
|
|
25
|
+
})
|
|
26
|
+
.strict();
|
|
27
|
+
function escapeRegex(s) {
|
|
28
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
29
|
+
}
|
|
30
|
+
// Module-scope compile cache — keyed by source array reference equality (same trade-off as
|
|
31
|
+
// alpha.15 `no-empty-catch.compileSkipPatterns`).
|
|
32
|
+
let cachedSource = null;
|
|
33
|
+
let cachedPattern = null;
|
|
34
|
+
function compileSqlKeywordPattern(source) {
|
|
35
|
+
if (cachedSource === source && cachedPattern !== null)
|
|
36
|
+
return cachedPattern;
|
|
37
|
+
const pattern = new RegExp('\\b(' + source.map(escapeRegex).join('|') + ')\\b', 'i');
|
|
38
|
+
cachedSource = source;
|
|
39
|
+
cachedPattern = pattern;
|
|
40
|
+
return pattern;
|
|
41
|
+
}
|
|
3
42
|
export default defineRule({
|
|
4
43
|
id: 'no-fstring-sql',
|
|
5
44
|
language: 'python',
|
|
6
45
|
severity: 'error',
|
|
7
46
|
message: 'SQL inside f-string is a SQL injection vector — use parameterized queries.',
|
|
8
47
|
messageKo: 'f-string으로 SQL 조합은 SQL 주입 위험 — 파라미터 바인딩을 사용하세요.',
|
|
48
|
+
options: {
|
|
49
|
+
schema: optionsSchema,
|
|
50
|
+
defaults: { sqlKeywords: [...DEFAULT_SQL_KEYWORDS] },
|
|
51
|
+
},
|
|
9
52
|
visitors: {
|
|
10
53
|
string(node, ctx) {
|
|
11
54
|
const text = ctx.textOf(node);
|
|
12
55
|
// Python f-string starts with f" or f' (or rf", fr", etc.)
|
|
13
56
|
if (!/^[a-zA-Z]*[fF][a-zA-Z]*['"]/.test(text))
|
|
14
57
|
return;
|
|
15
|
-
|
|
58
|
+
const opts = ctx.options ?? {
|
|
59
|
+
sqlKeywords: DEFAULT_SQL_KEYWORDS,
|
|
60
|
+
};
|
|
61
|
+
if (opts.sqlKeywords.length === 0)
|
|
62
|
+
return;
|
|
63
|
+
const pattern = compileSqlKeywordPattern(opts.sqlKeywords);
|
|
64
|
+
if (!pattern.test(text))
|
|
16
65
|
return;
|
|
17
66
|
// Has interpolation `{...}`?
|
|
18
67
|
if (!/\{[^{}]+\}/.test(text))
|