codesift-mcp 0.5.30 → 0.8.2

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.
Files changed (207) hide show
  1. package/README.md +7 -2
  2. package/dist/cli/commands.d.ts.map +1 -1
  3. package/dist/cli/commands.js +25 -3
  4. package/dist/cli/commands.js.map +1 -1
  5. package/dist/cli/git-hooks-installer.d.ts +2 -0
  6. package/dist/cli/git-hooks-installer.d.ts.map +1 -1
  7. package/dist/cli/git-hooks-installer.js +47 -7
  8. package/dist/cli/git-hooks-installer.js.map +1 -1
  9. package/dist/cli/hooks.d.ts.map +1 -1
  10. package/dist/cli/hooks.js +53 -0
  11. package/dist/cli/hooks.js.map +1 -1
  12. package/dist/cli/setup.d.ts +5 -0
  13. package/dist/cli/setup.d.ts.map +1 -1
  14. package/dist/cli/setup.js +31 -5
  15. package/dist/cli/setup.js.map +1 -1
  16. package/dist/config.d.ts +2 -1
  17. package/dist/config.d.ts.map +1 -1
  18. package/dist/config.js +10 -1
  19. package/dist/config.js.map +1 -1
  20. package/dist/instructions.d.ts +1 -1
  21. package/dist/instructions.d.ts.map +1 -1
  22. package/dist/instructions.js +6 -1
  23. package/dist/instructions.js.map +1 -1
  24. package/dist/parser/extractors/_shared.js +2 -2
  25. package/dist/parser/extractors/_shared.js.map +1 -1
  26. package/dist/parser/extractors/hono.d.ts.map +1 -1
  27. package/dist/parser/extractors/hono.js +60 -15
  28. package/dist/parser/extractors/hono.js.map +1 -1
  29. package/dist/parser/extractors/php.d.ts +12 -0
  30. package/dist/parser/extractors/php.d.ts.map +1 -1
  31. package/dist/parser/extractors/php.js +440 -26
  32. package/dist/parser/extractors/php.js.map +1 -1
  33. package/dist/parser/extractors/python.js +4 -5
  34. package/dist/parser/extractors/python.js.map +1 -1
  35. package/dist/parser/extractors/typescript.d.ts.map +1 -1
  36. package/dist/parser/extractors/typescript.js +70 -22
  37. package/dist/parser/extractors/typescript.js.map +1 -1
  38. package/dist/register-tool-loaders.d.ts +22 -0
  39. package/dist/register-tool-loaders.d.ts.map +1 -1
  40. package/dist/register-tool-loaders.js +38 -0
  41. package/dist/register-tool-loaders.js.map +1 -1
  42. package/dist/register-tools.d.ts +3 -1
  43. package/dist/register-tools.d.ts.map +1 -1
  44. package/dist/register-tools.js +527 -7
  45. package/dist/register-tools.js.map +1 -1
  46. package/dist/retrieval/codebase-retrieval.d.ts.map +1 -1
  47. package/dist/retrieval/codebase-retrieval.js +22 -0
  48. package/dist/retrieval/codebase-retrieval.js.map +1 -1
  49. package/dist/retrieval/retrieval-schemas.d.ts +4 -0
  50. package/dist/retrieval/retrieval-schemas.d.ts.map +1 -1
  51. package/dist/retrieval/semantic-handlers.js +1 -1
  52. package/dist/retrieval/semantic-handlers.js.map +1 -1
  53. package/dist/search/semantic.d.ts +21 -5
  54. package/dist/search/semantic.d.ts.map +1 -1
  55. package/dist/search/semantic.js +129 -4
  56. package/dist/search/semantic.js.map +1 -1
  57. package/dist/search/tool-ranker.js +1 -1
  58. package/dist/search/tool-ranker.js.map +1 -1
  59. package/dist/server-helpers.js +1 -1
  60. package/dist/server-helpers.js.map +1 -1
  61. package/dist/storage/index-store.d.ts +13 -1
  62. package/dist/storage/index-store.d.ts.map +1 -1
  63. package/dist/storage/index-store.js +34 -33
  64. package/dist/storage/index-store.js.map +1 -1
  65. package/dist/storage/registry.d.ts +28 -4
  66. package/dist/storage/registry.d.ts.map +1 -1
  67. package/dist/storage/registry.js +126 -5
  68. package/dist/storage/registry.js.map +1 -1
  69. package/dist/storage/usage-stats.d.ts +2 -0
  70. package/dist/storage/usage-stats.d.ts.map +1 -1
  71. package/dist/storage/usage-stats.js +6 -0
  72. package/dist/storage/usage-stats.js.map +1 -1
  73. package/dist/tools/_helpers.d.ts +2 -0
  74. package/dist/tools/_helpers.d.ts.map +1 -1
  75. package/dist/tools/_helpers.js +16 -1
  76. package/dist/tools/_helpers.js.map +1 -1
  77. package/dist/tools/astro-audit.d.ts +40 -0
  78. package/dist/tools/astro-audit.d.ts.map +1 -1
  79. package/dist/tools/astro-audit.js +94 -5
  80. package/dist/tools/astro-audit.js.map +1 -1
  81. package/dist/tools/astro-env-validator.d.ts +38 -0
  82. package/dist/tools/astro-env-validator.d.ts.map +1 -0
  83. package/dist/tools/astro-env-validator.js +190 -0
  84. package/dist/tools/astro-env-validator.js.map +1 -0
  85. package/dist/tools/astro-helpers.js +2 -2
  86. package/dist/tools/astro-helpers.js.map +1 -1
  87. package/dist/tools/astro-image-audit.d.ts +35 -0
  88. package/dist/tools/astro-image-audit.d.ts.map +1 -0
  89. package/dist/tools/astro-image-audit.js +129 -0
  90. package/dist/tools/astro-image-audit.js.map +1 -0
  91. package/dist/tools/astro-middleware.d.ts.map +1 -1
  92. package/dist/tools/astro-middleware.js +36 -11
  93. package/dist/tools/astro-middleware.js.map +1 -1
  94. package/dist/tools/astro-migration.d.ts.map +1 -1
  95. package/dist/tools/astro-migration.js +66 -0
  96. package/dist/tools/astro-migration.js.map +1 -1
  97. package/dist/tools/astro-svg-components.d.ts +32 -0
  98. package/dist/tools/astro-svg-components.d.ts.map +1 -0
  99. package/dist/tools/astro-svg-components.js +123 -0
  100. package/dist/tools/astro-svg-components.js.map +1 -0
  101. package/dist/tools/context-tools.d.ts.map +1 -1
  102. package/dist/tools/context-tools.js +45 -7
  103. package/dist/tools/context-tools.js.map +1 -1
  104. package/dist/tools/conversation-tools.js +1 -1
  105. package/dist/tools/conversation-tools.js.map +1 -1
  106. package/dist/tools/index-tools.d.ts +12 -0
  107. package/dist/tools/index-tools.d.ts.map +1 -1
  108. package/dist/tools/index-tools.js +54 -6
  109. package/dist/tools/index-tools.js.map +1 -1
  110. package/dist/tools/insights-tools.d.ts +137 -0
  111. package/dist/tools/insights-tools.d.ts.map +1 -0
  112. package/dist/tools/insights-tools.js +438 -0
  113. package/dist/tools/insights-tools.js.map +1 -0
  114. package/dist/tools/pattern-tools.d.ts +7 -0
  115. package/dist/tools/pattern-tools.d.ts.map +1 -1
  116. package/dist/tools/pattern-tools.js +292 -19
  117. package/dist/tools/pattern-tools.js.map +1 -1
  118. package/dist/tools/php-tools.d.ts +78 -4
  119. package/dist/tools/php-tools.d.ts.map +1 -1
  120. package/dist/tools/php-tools.js +824 -42
  121. package/dist/tools/php-tools.js.map +1 -1
  122. package/dist/tools/php8-compat-tools.d.ts +62 -0
  123. package/dist/tools/php8-compat-tools.d.ts.map +1 -0
  124. package/dist/tools/php8-compat-tools.js +287 -0
  125. package/dist/tools/php8-compat-tools.js.map +1 -0
  126. package/dist/tools/php8-migration-candidates-tools.d.ts +68 -0
  127. package/dist/tools/php8-migration-candidates-tools.d.ts.map +1 -0
  128. package/dist/tools/php8-migration-candidates-tools.js +476 -0
  129. package/dist/tools/php8-migration-candidates-tools.js.map +1 -0
  130. package/dist/tools/phpstan-baseline-tools.d.ts +62 -0
  131. package/dist/tools/phpstan-baseline-tools.d.ts.map +1 -0
  132. package/dist/tools/phpstan-baseline-tools.js +263 -0
  133. package/dist/tools/phpstan-baseline-tools.js.map +1 -0
  134. package/dist/tools/project-tools.d.ts +4 -2
  135. package/dist/tools/project-tools.d.ts.map +1 -1
  136. package/dist/tools/project-tools.js +20 -7
  137. package/dist/tools/project-tools.js.map +1 -1
  138. package/dist/tools/react-tools.d.ts +39 -9
  139. package/dist/tools/react-tools.d.ts.map +1 -1
  140. package/dist/tools/react-tools.js +313 -18
  141. package/dist/tools/react-tools.js.map +1 -1
  142. package/dist/tools/search-tools.d.ts.map +1 -1
  143. package/dist/tools/search-tools.js +35 -5
  144. package/dist/tools/search-tools.js.map +1 -1
  145. package/dist/tools/status-tools.d.ts +1 -0
  146. package/dist/tools/status-tools.d.ts.map +1 -1
  147. package/dist/tools/status-tools.js +1 -0
  148. package/dist/tools/status-tools.js.map +1 -1
  149. package/dist/tools/symbol-tools.d.ts +11 -0
  150. package/dist/tools/symbol-tools.d.ts.map +1 -1
  151. package/dist/tools/symbol-tools.js +107 -6
  152. package/dist/tools/symbol-tools.js.map +1 -1
  153. package/dist/tools/yii-console-tools.d.ts +69 -0
  154. package/dist/tools/yii-console-tools.d.ts.map +1 -0
  155. package/dist/tools/yii-console-tools.js +256 -0
  156. package/dist/tools/yii-console-tools.js.map +1 -0
  157. package/dist/tools/yii-migrations-tools.d.ts +79 -0
  158. package/dist/tools/yii-migrations-tools.d.ts.map +1 -0
  159. package/dist/tools/yii-migrations-tools.js +543 -0
  160. package/dist/tools/yii-migrations-tools.js.map +1 -0
  161. package/dist/tools/yii-modules-tools.d.ts +63 -0
  162. package/dist/tools/yii-modules-tools.d.ts.map +1 -0
  163. package/dist/tools/yii-modules-tools.js +201 -0
  164. package/dist/tools/yii-modules-tools.js.map +1 -0
  165. package/dist/tools/yii-rbac-tools.d.ts +89 -0
  166. package/dist/tools/yii-rbac-tools.d.ts.map +1 -0
  167. package/dist/tools/yii-rbac-tools.js +238 -0
  168. package/dist/tools/yii-rbac-tools.js.map +1 -0
  169. package/dist/tools/yii3-attribute-candidates-tools.d.ts +72 -0
  170. package/dist/tools/yii3-attribute-candidates-tools.d.ts.map +1 -0
  171. package/dist/tools/yii3-attribute-candidates-tools.js +301 -0
  172. package/dist/tools/yii3-attribute-candidates-tools.js.map +1 -0
  173. package/dist/tools/yii3-migration-tools.d.ts +74 -0
  174. package/dist/tools/yii3-migration-tools.d.ts.map +1 -0
  175. package/dist/tools/yii3-migration-tools.js +440 -0
  176. package/dist/tools/yii3-migration-tools.js.map +1 -0
  177. package/dist/types.d.ts +5 -1
  178. package/dist/types.d.ts.map +1 -1
  179. package/dist/utils/constant-file-pattern.d.ts +3 -1
  180. package/dist/utils/constant-file-pattern.d.ts.map +1 -1
  181. package/dist/utils/constant-file-pattern.js +6 -4
  182. package/dist/utils/constant-file-pattern.js.map +1 -1
  183. package/dist/utils/heritage-edges.d.ts +29 -0
  184. package/dist/utils/heritage-edges.d.ts.map +1 -0
  185. package/dist/utils/heritage-edges.js +94 -0
  186. package/dist/utils/heritage-edges.js.map +1 -0
  187. package/dist/utils/source-stripper.d.ts +23 -0
  188. package/dist/utils/source-stripper.d.ts.map +1 -0
  189. package/dist/utils/source-stripper.js +239 -0
  190. package/dist/utils/source-stripper.js.map +1 -0
  191. package/dist/utils/ts-imports.d.ts +7 -1
  192. package/dist/utils/ts-imports.d.ts.map +1 -1
  193. package/dist/utils/ts-imports.js +40 -7
  194. package/dist/utils/ts-imports.js.map +1 -1
  195. package/dist/utils/tsconfig-paths.d.ts +6 -5
  196. package/dist/utils/tsconfig-paths.d.ts.map +1 -1
  197. package/dist/utils/tsconfig-paths.js +52 -14
  198. package/dist/utils/tsconfig-paths.js.map +1 -1
  199. package/dist/utils/wall-clock.d.ts +9 -0
  200. package/dist/utils/wall-clock.d.ts.map +1 -0
  201. package/dist/utils/wall-clock.js +19 -0
  202. package/dist/utils/wall-clock.js.map +1 -0
  203. package/package.json +1 -1
  204. package/rules/codesift.md +16 -6
  205. package/rules/codesift.mdc +10 -3
  206. package/rules/codex.md +10 -3
  207. package/rules/gemini.md +10 -3
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Yii2 module inventory (N1).
3
+ *
4
+ * Scans a Yii2 codebase for classes extending `yii\\base\\Module` and emits
5
+ * a structured per-module summary: controller namespace, controllers, views,
6
+ * migrations path, components, sub-modules. Also resolves URL prefixes by
7
+ * cross-referencing config/web.php urlManager rules + the application's
8
+ * `modules` registration.
9
+ *
10
+ * Why this is its own tool (vs. squeezing into php_project_audit):
11
+ * - Modules are the primary architectural unit of medium/large Yii2 apps
12
+ * (tgm-panel: 11 modules; Mobi 2: similar). Routing, RBAC, and god-model
13
+ * analysis all benefit from being able to scope to a module.
14
+ * - The output is a graph of cross-file references (controllers ↔ views ↔
15
+ * migrations ↔ config), not a flat findings list.
16
+ *
17
+ * Implementation depends on the v2.0.0 PHP extractor — uses `s.extends`
18
+ * (introduced in Sprint 1) to detect Module subclasses structurally.
19
+ * Falls back to a regex check on `s.source` when `extends` is absent so
20
+ * stale indexes don't go silent.
21
+ */
22
+ import { readFile } from "node:fs/promises";
23
+ import { dirname, join, relative } from "node:path";
24
+ import { getCodeIndex } from "./index-tools.js";
25
+ // ---------------------------------------------------------------------------
26
+ // Implementation
27
+ // ---------------------------------------------------------------------------
28
+ const MODULE_BASE_NAMES = new Set(["Module", "BaseModule"]);
29
+ /**
30
+ * Walk a class symbol's extends chain looking for a Yii2 Module ancestor.
31
+ * Mirrors isActiveRecordHierarchy from php-tools but for the Module base.
32
+ * Cycle protection + depth cap. Falls back to source-text regex when extends
33
+ * metadata is absent (legacy index pre-v2.0.0 extractor).
34
+ */
35
+ function isModuleHierarchy(cls, index, visited = new Set(), depth = 0) {
36
+ if (depth > 5)
37
+ return false;
38
+ if (visited.has(cls.name))
39
+ return false;
40
+ visited.add(cls.name);
41
+ const exts = cls.extends ?? [];
42
+ for (const baseFqcn of exts) {
43
+ const last = baseFqcn.split(/[\\\\]+/).pop() ?? baseFqcn;
44
+ if (MODULE_BASE_NAMES.has(last))
45
+ return true;
46
+ const baseSym = index.symbols.find((s) => s.kind === "class" && s.name === last);
47
+ if (baseSym && isModuleHierarchy(baseSym, index, visited, depth + 1)) {
48
+ return true;
49
+ }
50
+ }
51
+ if (!cls.extends && cls.source) {
52
+ return /extends\s+(?:\\?yii\\base\\Module|Module)\b/.test(cls.source);
53
+ }
54
+ return false;
55
+ }
56
+ export async function analyzeYiiModules(repo, options) {
57
+ const index = await getCodeIndex(repo);
58
+ if (!index)
59
+ throw new Error(`Repository "${repo}" not found.`);
60
+ // Find all class symbols whose file basename is exactly Module.php — Yii2
61
+ // canonical convention. We additionally verify the class extends Module
62
+ // (catches cases where a Module.php contains an unrelated helper class).
63
+ const moduleClasses = index.symbols.filter((s) => {
64
+ if (s.kind !== "class")
65
+ return false;
66
+ if (!s.file.endsWith("/Module.php") && s.file !== "Module.php")
67
+ return false;
68
+ if (!isModuleHierarchy(s, index))
69
+ return false;
70
+ return true;
71
+ });
72
+ // Pre-resolve url-manager rules from config — used to attach url_prefixes
73
+ // to each module. Robust against missing config: we read each candidate
74
+ // file and scan with a single regex; failures are non-fatal.
75
+ const urlPrefixesByModule = await resolveUrlPrefixes(index);
76
+ const modules = [];
77
+ for (const cls of moduleClasses) {
78
+ const moduleDir = dirname(cls.file);
79
+ const id = moduleDir.split("/").pop() ?? cls.name.replace(/Module$/, "").toLowerCase();
80
+ if (options?.module_id && id !== options.module_id)
81
+ continue;
82
+ // controllerNamespace: explicit declaration on the Module class.
83
+ // Captured from `public $controllerNamespace = '...';` in source.
84
+ let controllerNamespace = null;
85
+ if (cls.source) {
86
+ const cnMatch = /\$controllerNamespace\s*=\s*['"]([^'"]+)['"]/.exec(cls.source);
87
+ controllerNamespace = cnMatch?.[1] ?? null;
88
+ }
89
+ // Yii2 default: `<module-namespace>\\controllers`. We synthesize this
90
+ // when the module class doesn't override it explicitly.
91
+ const defaultControllerNamespace = computeDefaultControllerNamespace(cls.file);
92
+ const effectiveNs = controllerNamespace ?? defaultControllerNamespace;
93
+ const controllersPath = join(moduleDir, "controllers");
94
+ // Find controller classes living under the module's controllers/ dir.
95
+ const controllerSymbols = index.symbols.filter((s) => s.kind === "class" &&
96
+ s.file.startsWith(controllersPath + "/") &&
97
+ s.name.endsWith("Controller"));
98
+ const controllers = controllerSymbols.map((c) => ({
99
+ class: effectiveNs ? `${effectiveNs}\\${c.name}` : c.name,
100
+ file: c.file,
101
+ actions: index.symbols
102
+ .filter((s) => s.parent === c.id && s.kind === "method" && s.name.startsWith("action"))
103
+ .map((s) => s.name),
104
+ }));
105
+ // Views, migrations, sub-modules — each detected by directory presence.
106
+ const viewsPath = join(moduleDir, "views");
107
+ const viewsCount = index.files.filter((f) => f.path.startsWith(viewsPath + "/")).length;
108
+ const migrationsPath = join(moduleDir, "migrations");
109
+ const migrationsCount = index.files.filter((f) => f.path.startsWith(migrationsPath + "/") && /m\d+_\d+_/.test(f.path)).length;
110
+ // Sub-modules: nested module directories with their own Module.php.
111
+ const submoduleClasses = index.symbols.filter((s) => s.kind === "class" &&
112
+ (s.file.endsWith("/Module.php") || s.file === "Module.php") &&
113
+ s.file !== cls.file &&
114
+ s.file.startsWith(moduleDir + "/"));
115
+ const submodules = submoduleClasses
116
+ .map((sc) => dirname(sc.file).split("/").pop() ?? "")
117
+ .filter(Boolean);
118
+ modules.push({
119
+ id,
120
+ class: effectiveNs ? `${effectiveNs.replace(/\\controllers$/, "")}\\${cls.name}` : cls.name,
121
+ file: cls.file,
122
+ controllerNamespace,
123
+ controllers_path: controllersPath,
124
+ controllers,
125
+ views_path: viewsPath,
126
+ views_count: viewsCount,
127
+ migrations_path: migrationsCount > 0 ? migrationsPath : null,
128
+ migrations_count: migrationsCount,
129
+ submodules,
130
+ url_prefixes: urlPrefixesByModule.get(id) ?? [],
131
+ });
132
+ }
133
+ // Stable order: by id alphabetically — useful when consumers diff this
134
+ // output across audit runs.
135
+ modules.sort((a, b) => a.id.localeCompare(b.id));
136
+ return { repo, total_modules: modules.length, modules };
137
+ }
138
+ // ---------------------------------------------------------------------------
139
+ // Helpers
140
+ // ---------------------------------------------------------------------------
141
+ /**
142
+ * Synthesize the default controllerNamespace for a Module.php file.
143
+ * Yii2 convention: take the Module's PHP namespace and append "\\controllers".
144
+ * We read the file directly because the namespace declaration is at the
145
+ * top of the file, not on the symbol itself.
146
+ *
147
+ * Returns null if the file isn't readable or has no namespace declaration.
148
+ * The caller falls back to bare class names when this is null.
149
+ */
150
+ function computeDefaultControllerNamespace(_file) {
151
+ // Lazy: this is best-effort — if the namespace can't be determined the
152
+ // controllers list still works (we just lose the FQCN wrapping). Reading
153
+ // the file synchronously inside a hot loop would be slow; we accept the
154
+ // null fallback rather than spin up a per-module readFile here.
155
+ return null;
156
+ }
157
+ /**
158
+ * Scan main config files for `urlManager` rules and group resolved URL
159
+ * prefixes by module id. Yii2 url rules of the form
160
+ * '<module-id>/<controller-id>/<action-id>'
161
+ * '<module-id>/<controller-id>'
162
+ * ['class' => 'yii\\rest\\UrlRule', 'controller' => '<module-id>/<ctrl>']
163
+ * all map to a single module-id prefix. Naive matcher — collects literal
164
+ * prefix strings, callers can dedupe further by stripping verbs.
165
+ */
166
+ async function resolveUrlPrefixes(index) {
167
+ const out = new Map();
168
+ const configFiles = index.files.filter((f) => /config\/(?:web|main|api|backend|frontend|common)(?:[-_][\w-]+)?\.php$/.test(f.path));
169
+ for (const cf of configFiles) {
170
+ let source;
171
+ try {
172
+ source = await readFile(join(index.root, cf.path), "utf-8");
173
+ }
174
+ catch {
175
+ continue;
176
+ }
177
+ // Generic pattern: 'GET <prefix>/...' => 'mod/ctrl/act' OR
178
+ // '<prefix>/...' => 'mod/ctrl/act'
179
+ // We capture the route target string (right side) and pull its first
180
+ // segment as module id.
181
+ const ruleRe = /['"](?:GET |POST |PUT |DELETE |PATCH )?[^'"]+['"]\s*=>\s*['"]([\w-]+)\/(?:[\w-]+\/?)*['"]/g;
182
+ let m;
183
+ while ((m = ruleRe.exec(source)) !== null) {
184
+ const prefix = m[1];
185
+ // Filter common false positives: top-level controller names that look
186
+ // like prefixes but aren't modules (site, debug, gii). Caller can
187
+ // ignore these by intersecting with the actual module list.
188
+ if (!out.has(prefix))
189
+ out.set(prefix, []);
190
+ out.get(prefix).push(prefix);
191
+ }
192
+ }
193
+ // Dedupe per-key
194
+ for (const [k, v] of out.entries()) {
195
+ out.set(k, Array.from(new Set(v)));
196
+ }
197
+ return out;
198
+ }
199
+ // Re-export relative for tests that want to assert on path shapes.
200
+ export { relative as _relativePathForTests };
201
+ //# sourceMappingURL=yii-modules-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yii-modules-tools.js","sourceRoot":"","sources":["../../src/tools/yii-modules-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Yii2 RBAC audit (N3).
3
+ *
4
+ * Builds a permission graph by cross-referencing two surfaces:
5
+ *
6
+ * 1. DEFINITIONS — permissions/roles created in seed migrations or
7
+ * RBAC seeder commands. The Yii2 idiom is:
8
+ *
9
+ * $auth = Yii::$app->authManager;
10
+ * $auth->createPermission('viewUser'); // permission def
11
+ * $auth->createRole('admin'); // role def
12
+ * $auth->add($p); // register
13
+ * $auth->addChild($admin, $p); // attach to role
14
+ *
15
+ * 2. RUNTIME CHECKS — `Yii::$app->user->can('viewUser')` calls in
16
+ * controllers/views, plus `AccessControl` rules in behaviors().
17
+ *
18
+ * Cross-referencing the two surfaces produces:
19
+ *
20
+ * - orphan_checks: permissions checked at runtime but never defined.
21
+ * Indicates dead code OR a typo.
22
+ * - unused_definitions: permissions defined but never checked. Indicates
23
+ * dead seed code OR a missing access guard.
24
+ * - controllers_without_access_control: classes that look like Controllers
25
+ * (web, REST) without any access
26
+ * restriction (no AccessControl in
27
+ * behaviors(), no can() calls).
28
+ * - dynamic_creates: createPermission($var) calls — name is computed at
29
+ * runtime, can't statically resolve. Surfaced
30
+ * separately so callers can flag them as "review
31
+ * manually".
32
+ *
33
+ * The tool deliberately avoids opinions about which finding category is
34
+ * "correct" — the consumer (audit skill, CTO, code reviewer) decides
35
+ * which orphans matter and which were intentional.
36
+ */
37
+ export interface RbacDefinitionRef {
38
+ name: string;
39
+ kind: "permission" | "role";
40
+ file: string;
41
+ line: number;
42
+ }
43
+ export interface RbacCheckRef {
44
+ name: string;
45
+ /** "code" — Yii::$app->user->can(...)
46
+ * "access-control" — AccessControl behavior `permissions => […]` */
47
+ source: "code" | "access-control";
48
+ file: string;
49
+ line: number;
50
+ }
51
+ export interface RbacControllerRef {
52
+ class: string;
53
+ file: string;
54
+ /** Reason it was flagged. */
55
+ reason: "no-behaviors" | "no-access-control-in-behaviors" | "no-can-calls";
56
+ }
57
+ export interface YiiRbacAudit {
58
+ repo: string;
59
+ /** Resolved (statically determinable) permission/role definitions. */
60
+ definitions: RbacDefinitionRef[];
61
+ /** Runtime can() calls + AccessControl rule references. */
62
+ checks: RbacCheckRef[];
63
+ /** Permissions checked at runtime but not in definitions. */
64
+ orphan_checks: string[];
65
+ /** Permissions defined but never checked. */
66
+ unused_definitions: string[];
67
+ /** Controller classes with no AccessControl behavior or can() calls. */
68
+ controllers_without_access_control: RbacControllerRef[];
69
+ /** Sites where the permission name was a variable / function call —
70
+ * unresolvable statically. Flagged so the auditor reviews manually. */
71
+ dynamic_creates: Array<{
72
+ file: string;
73
+ line: number;
74
+ snippet: string;
75
+ }>;
76
+ /** Aggregate counts. */
77
+ summary: {
78
+ total_permissions: number;
79
+ total_roles: number;
80
+ total_checks: number;
81
+ orphan_check_count: number;
82
+ unused_definition_count: number;
83
+ unsafe_controller_count: number;
84
+ };
85
+ }
86
+ export declare function analyzeYiiRbac(repo: string, options?: {
87
+ include_vendor?: boolean;
88
+ }): Promise<YiiRbacAudit>;
89
+ //# sourceMappingURL=yii-rbac-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yii-rbac-tools.d.ts","sourceRoot":"","sources":["../../src/tools/yii-rbac-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAUH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,YAAY,GAAG,MAAM,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb;yEACqE;IACrE,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,MAAM,EAAE,cAAc,GAAG,gCAAgC,GAAG,cAAc,CAAC;CAC5E;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,2DAA2D;IAC3D,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,6DAA6D;IAC7D,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,6CAA6C;IAC7C,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,wEAAwE;IACxE,kCAAkC,EAAE,iBAAiB,EAAE,CAAC;IACxD;4EACwE;IACxE,eAAe,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxE,wBAAwB;IACxB,OAAO,EAAE;QACP,iBAAiB,EAAE,MAAM,CAAC;QAC1B,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,uBAAuB,EAAE,MAAM,CAAC;QAChC,uBAAuB,EAAE,MAAM,CAAC;KACjC,CAAC;CACH;AAQD,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,GACrC,OAAO,CAAC,YAAY,CAAC,CAiFvB"}
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Yii2 RBAC audit (N3).
3
+ *
4
+ * Builds a permission graph by cross-referencing two surfaces:
5
+ *
6
+ * 1. DEFINITIONS — permissions/roles created in seed migrations or
7
+ * RBAC seeder commands. The Yii2 idiom is:
8
+ *
9
+ * $auth = Yii::$app->authManager;
10
+ * $auth->createPermission('viewUser'); // permission def
11
+ * $auth->createRole('admin'); // role def
12
+ * $auth->add($p); // register
13
+ * $auth->addChild($admin, $p); // attach to role
14
+ *
15
+ * 2. RUNTIME CHECKS — `Yii::$app->user->can('viewUser')` calls in
16
+ * controllers/views, plus `AccessControl` rules in behaviors().
17
+ *
18
+ * Cross-referencing the two surfaces produces:
19
+ *
20
+ * - orphan_checks: permissions checked at runtime but never defined.
21
+ * Indicates dead code OR a typo.
22
+ * - unused_definitions: permissions defined but never checked. Indicates
23
+ * dead seed code OR a missing access guard.
24
+ * - controllers_without_access_control: classes that look like Controllers
25
+ * (web, REST) without any access
26
+ * restriction (no AccessControl in
27
+ * behaviors(), no can() calls).
28
+ * - dynamic_creates: createPermission($var) calls — name is computed at
29
+ * runtime, can't statically resolve. Surfaced
30
+ * separately so callers can flag them as "review
31
+ * manually".
32
+ *
33
+ * The tool deliberately avoids opinions about which finding category is
34
+ * "correct" — the consumer (audit skill, CTO, code reviewer) decides
35
+ * which orphans matter and which were intentional.
36
+ */
37
+ import { readFile } from "node:fs/promises";
38
+ import { join } from "node:path";
39
+ import { getCodeIndex } from "./index-tools.js";
40
+ // ---------------------------------------------------------------------------
41
+ // Implementation
42
+ // ---------------------------------------------------------------------------
43
+ const VENDOR_RE = /(^|\/)(?:vendor|node_modules|runtime|tests\/_data)(\/|$)/;
44
+ export async function analyzeYiiRbac(repo, options) {
45
+ const index = await getCodeIndex(repo);
46
+ if (!index)
47
+ throw new Error(`Repository "${repo}" not found.`);
48
+ const includeVendor = options?.include_vendor ?? false;
49
+ const phpFiles = index.files.filter((f) => {
50
+ if (!f.path.endsWith(".php"))
51
+ return false;
52
+ if (!includeVendor && VENDOR_RE.test(f.path))
53
+ return false;
54
+ return true;
55
+ });
56
+ const definitions = [];
57
+ const checks = [];
58
+ const dynamicCreates = [];
59
+ // Single-pass file read — every file scanned for all three surfaces.
60
+ await Promise.all(phpFiles.map(async (f) => {
61
+ let content;
62
+ try {
63
+ content = await readFile(join(index.root, f.path), "utf-8");
64
+ }
65
+ catch {
66
+ return;
67
+ }
68
+ collectDefinitions(content, f.path, definitions, dynamicCreates);
69
+ collectChecks(content, f.path, checks);
70
+ }));
71
+ // Build name sets for orphan/unused diff. Definitions and checks are kept
72
+ // separately by kind — only "permission" definitions are matched against
73
+ // can() checks, since checking a role name with can() is a semantic error
74
+ // (Yii2's UserComponent::can resolves both, but the audit categorizes
75
+ // them distinctly).
76
+ const definedPermissionNames = new Set(definitions.filter((d) => d.kind === "permission").map((d) => d.name));
77
+ const definedRoleNames = new Set(definitions.filter((d) => d.kind === "role").map((d) => d.name));
78
+ const allDefinedNames = new Set([
79
+ ...definedPermissionNames,
80
+ ...definedRoleNames,
81
+ ]);
82
+ const checkedNames = new Set(checks.map((c) => c.name));
83
+ const orphanChecks = [...checkedNames]
84
+ .filter((n) => !allDefinedNames.has(n))
85
+ .sort();
86
+ // Unused definitions: only permissions matter — unused roles often exist
87
+ // intentionally (e.g., a `superadmin` role with no can() check, but
88
+ // assigned via authManager->assign()). We surface only permissions to
89
+ // keep noise down; consumers can look at the full definitions[] list
90
+ // when they want role-level analysis.
91
+ const unusedDefinitions = [...definedPermissionNames]
92
+ .filter((n) => !checkedNames.has(n))
93
+ .sort();
94
+ // Controllers without access control. We need to read each Controller
95
+ // class symbol's source for behaviors() body presence + can() calls.
96
+ const controllers = scanControllersWithoutAccessControl(index);
97
+ return {
98
+ repo,
99
+ definitions,
100
+ checks,
101
+ orphan_checks: orphanChecks,
102
+ unused_definitions: unusedDefinitions,
103
+ controllers_without_access_control: controllers,
104
+ dynamic_creates: dynamicCreates,
105
+ summary: {
106
+ total_permissions: definedPermissionNames.size,
107
+ total_roles: definedRoleNames.size,
108
+ total_checks: checkedNames.size,
109
+ orphan_check_count: orphanChecks.length,
110
+ unused_definition_count: unusedDefinitions.length,
111
+ unsafe_controller_count: controllers.length,
112
+ },
113
+ };
114
+ }
115
+ // ---------------------------------------------------------------------------
116
+ // Surface 1: definitions
117
+ // ---------------------------------------------------------------------------
118
+ function collectDefinitions(content, file, definitions, dynamicCreates) {
119
+ // Pattern A: ->createPermission('name') / ->createRole('name')
120
+ // Match $auth->createPermission(...) and $authManager->createPermission(...)
121
+ // as well as Yii::$app->authManager->createPermission(...)
122
+ const createRe = /->(createPermission|createRole)\s*\(\s*(['"]([^'"]+)['"]|\$\w+|[A-Z_][\w]*::[\w]+)/g;
123
+ let m;
124
+ while ((m = createRe.exec(content)) !== null) {
125
+ const verb = m[1];
126
+ const literal = m[3];
127
+ const line = countLines(content, m.index);
128
+ if (literal !== undefined) {
129
+ definitions.push({
130
+ name: literal,
131
+ kind: verb === "createPermission" ? "permission" : "role",
132
+ file,
133
+ line,
134
+ });
135
+ }
136
+ else {
137
+ dynamicCreates.push({
138
+ file,
139
+ line,
140
+ snippet: extractLine(content, m.index),
141
+ });
142
+ }
143
+ }
144
+ }
145
+ // ---------------------------------------------------------------------------
146
+ // Surface 2: runtime checks
147
+ // ---------------------------------------------------------------------------
148
+ function collectChecks(content, file, checks) {
149
+ // Pattern A: Yii::$app->user->can('name') OR \Yii::$app->user->can('name')
150
+ // OR shortcut Yii::$app->can('name') (rare but legal in custom UserComponents)
151
+ const canRe = /(?:Yii|\\Yii)::\$app->user->can\s*\(\s*['"]([^'"]+)['"]/g;
152
+ let m;
153
+ while ((m = canRe.exec(content)) !== null) {
154
+ checks.push({
155
+ name: m[1],
156
+ source: "code",
157
+ file,
158
+ line: countLines(content, m.index),
159
+ });
160
+ }
161
+ // Pattern B: AccessControl rule with 'permissions' => ['name1', 'name2']
162
+ // We capture the entire array body and then split out individual names.
163
+ // Bounded 1000-char window after the keyword to avoid runaway matches on
164
+ // huge controllers. The rule can contain arbitrary other keys; we just
165
+ // need the permissions list.
166
+ const acRe = /['"]permissions['"]\s*=>\s*\[([^\]]{0,2000})\]/g;
167
+ while ((m = acRe.exec(content)) !== null) {
168
+ const inner = m[1];
169
+ const perRe = /['"]([\w-]+)['"]/g;
170
+ let pm;
171
+ while ((pm = perRe.exec(inner)) !== null) {
172
+ checks.push({
173
+ name: pm[1],
174
+ source: "access-control",
175
+ file,
176
+ line: countLines(content, m.index + pm.index),
177
+ });
178
+ }
179
+ }
180
+ }
181
+ function scanControllersWithoutAccessControl(index) {
182
+ const out = [];
183
+ // Find class symbols whose name ends in Controller AND that aren't in
184
+ // vendor/. We don't try to walk the inheritance tree to identify "real"
185
+ // Yii2 Controllers — a class named *Controller in the user's own code is
186
+ // 99% a real Yii2 controller, and the false-positive cost is one extra
187
+ // line in the report.
188
+ const controllers = index.symbols.filter((s) => {
189
+ if (s.kind !== "class")
190
+ return false;
191
+ if (!s.name.endsWith("Controller"))
192
+ return false;
193
+ if (!s.file.endsWith(".php"))
194
+ return false;
195
+ if (VENDOR_RE.test(s.file))
196
+ return false;
197
+ if (!s.source)
198
+ return false;
199
+ return true;
200
+ });
201
+ for (const ctrl of controllers) {
202
+ const src = ctrl.source;
203
+ const hasBehaviors = /\bfunction\s+behaviors\s*\(/.test(src);
204
+ const hasAccessControl = /AccessControl::class|['"]access['"]\s*=>\s*\[\s*['"]class['"]\s*=>\s*[^\]]*AccessControl/.test(src);
205
+ const hasCan = /->can\s*\(/.test(src);
206
+ if (!hasBehaviors) {
207
+ out.push({ class: ctrl.name, file: ctrl.file, reason: "no-behaviors" });
208
+ continue;
209
+ }
210
+ if (!hasAccessControl && !hasCan) {
211
+ out.push({
212
+ class: ctrl.name,
213
+ file: ctrl.file,
214
+ reason: "no-access-control-in-behaviors",
215
+ });
216
+ continue;
217
+ }
218
+ }
219
+ return out;
220
+ }
221
+ // ---------------------------------------------------------------------------
222
+ // Helpers
223
+ // ---------------------------------------------------------------------------
224
+ function countLines(source, idx) {
225
+ let line = 1;
226
+ for (let i = 0; i < idx; i++) {
227
+ if (source.charCodeAt(i) === 10)
228
+ line++;
229
+ }
230
+ return line;
231
+ }
232
+ function extractLine(source, idx) {
233
+ const start = source.lastIndexOf("\n", idx) + 1;
234
+ const end = source.indexOf("\n", idx);
235
+ const line = source.slice(start, end === -1 ? source.length : end);
236
+ return line.trim().slice(0, 200);
237
+ }
238
+ //# sourceMappingURL=yii-rbac-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yii-rbac-tools.js","sourceRoot":"","sources":["../../src/tools/yii-rbac-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Yii3 attribute conversion candidates (M2).
3
+ *
4
+ * Yii3 is natively PHP 8 attribute-based. The mechanical mapping from
5
+ * Yii2 array-config idioms to Yii3 attributes is well-defined, but
6
+ * applying it across thousands of files is the bottleneck for any
7
+ * Yii2→Yii3 migration. This tool surfaces every site that's a candidate
8
+ * for the conversion + a proposed attribute form, capped by sample limit
9
+ * so the consumer can triage at their own pace.
10
+ *
11
+ * Conversions covered:
12
+ *
13
+ * behaviors-to-attributes
14
+ * behaviors() returning an array with TimestampBehavior /
15
+ * BlameableBehavior / SluggableBehavior / etc → #[Behavior(class)] on
16
+ * the class. We surface the entire behaviors() body in `current_form`
17
+ * so the auditor sees the array-shape that needs conversion.
18
+ *
19
+ * rules-to-attributes
20
+ * rules() returning array of [['field'], 'validator'] tuples →
21
+ * #[Required], #[Email], #[StringLength(min: 1)] on the property.
22
+ * Each tuple becomes one or more proposed attributes.
23
+ *
24
+ * urlmanager-rule-to-route
25
+ * 'GET api/users/<id>' => 'user/view' (in urlManager rules) →
26
+ * #[Route(method: 'GET', path: '/api/users/{id}')] on the controller
27
+ * action. We resolve the controller/action target from the rule's
28
+ * right side. {param} placeholders are emitted but type constraints
29
+ * are dropped (Yii2 supports inline regex like <id:\\d+>; Yii3 uses
30
+ * route attributes like #[Route('/{id<int>}')]).
31
+ *
32
+ * Output shape:
33
+ * - candidates[] — flat list of all conversions
34
+ * - by_rule[] — grouped + sample-capped, sorted by count desc
35
+ * - summary — total + per-rule counts
36
+ *
37
+ * Like M1, this tool never auto-applies. Each candidate ships:
38
+ * current_form (the source as written today)
39
+ * suggested_replacement (the attribute equivalent)
40
+ * confidence ("high" | "medium" | "low")
41
+ * blockers[] (string reasons the conversion may be unsafe —
42
+ * e.g. "rule references a Closure validator that
43
+ * can't be lifted to an attribute")
44
+ */
45
+ export type Yii3AttributeRuleId = "behaviors-to-attributes" | "rules-to-attributes" | "urlmanager-rule-to-route";
46
+ export interface Yii3AttributeCandidate {
47
+ rule_id: Yii3AttributeRuleId;
48
+ file: string;
49
+ line: number;
50
+ current_form: string;
51
+ suggested_replacement: string;
52
+ confidence: "high" | "medium" | "low";
53
+ blockers: string[];
54
+ }
55
+ export interface Yii3AttributeCandidates {
56
+ repo: string;
57
+ scanned_files: number;
58
+ total_candidates: number;
59
+ by_rule: Array<{
60
+ rule_id: Yii3AttributeRuleId;
61
+ count: number;
62
+ samples: Yii3AttributeCandidate[];
63
+ }>;
64
+ candidates: Yii3AttributeCandidate[];
65
+ }
66
+ export declare function findYii3AttributeCandidates(repo: string, options?: {
67
+ file_pattern?: string;
68
+ rules?: Yii3AttributeRuleId[];
69
+ max_samples_per_rule?: number;
70
+ include_vendor?: boolean;
71
+ }): Promise<Yii3AttributeCandidates>;
72
+ //# sourceMappingURL=yii3-attribute-candidates-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yii3-attribute-candidates-tools.d.ts","sourceRoot":"","sources":["../../src/tools/yii3-attribute-candidates-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAUH,MAAM,MAAM,mBAAmB,GAC3B,yBAAyB,GACzB,qBAAqB,GACrB,0BAA0B,CAAC;AAE/B,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,mBAAmB,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,KAAK,CAAC;QACb,OAAO,EAAE,mBAAmB,CAAC;QAC7B,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,sBAAsB,EAAE,CAAC;KACnC,CAAC,CAAC;IACH,UAAU,EAAE,sBAAsB,EAAE,CAAC;CACtC;AAwBD,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IACR,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAC9B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,GACA,OAAO,CAAC,uBAAuB,CAAC,CAmElC"}