@mocoto/mahoraga 0.14.1 → 0.14.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 (268) hide show
  1. package/dist/analysts/architects/analyst-structure.js +1 -1
  2. package/dist/analysts/architects/archetypes-custom.js +8 -13
  3. package/dist/analysts/architects/detector-archetypes.js +39 -35
  4. package/dist/analysts/architects/orchestrator-archetypes.js +3 -3
  5. package/dist/analysts/architects/signals-project-advanced.js +2 -2
  6. package/dist/analysts/architects/signals-project.js +1 -1
  7. package/dist/analysts/azure-pipelines/analysts/analyst-azure.js +20 -21
  8. package/dist/analysts/azure-pipelines/detectors/detector-azure-structure.js +0 -3
  9. package/dist/analysts/circleci/analysts/analyst-circleci.js +22 -24
  10. package/dist/analysts/circleci/detectors/detector-circleci-structure.js +4 -7
  11. package/dist/analysts/converters/convert-registry.js +1 -1
  12. package/dist/analysts/converters/index.js +1 -1
  13. package/dist/analysts/corrections/auto-fix-engine.js +1 -1
  14. package/dist/analysts/corrections/corrector-structure.js +8 -7
  15. package/dist/analysts/corrections/map-reversion.js +2 -2
  16. package/dist/analysts/corrections/reorganize-barrels.js +3 -3
  17. package/dist/analysts/corrections/rewrite-testes-aliases.js +23 -22
  18. package/dist/analysts/corrections/type-safety/type-analyzer.js +3 -3
  19. package/dist/analysts/corrections/type-safety/type-validator.js +2 -2
  20. package/dist/analysts/corrections/type-safety/usage-analyzer.js +3 -3
  21. package/dist/analysts/css/analysts/analyst-css.js +27 -28
  22. package/dist/analysts/css-in-js/detectors/detector-css-in-js-best-practices.js +8 -15
  23. package/dist/analysts/detectors/detector-architecture.js +2 -2
  24. package/dist/analysts/detectors/detector-bugs-ml.js +8 -8
  25. package/dist/analysts/detectors/detector-code-fragile.js +11 -10
  26. package/dist/analysts/detectors/detector-dependencies.js +5 -5
  27. package/dist/analysts/detectors/detector-duplications.js +5 -8
  28. package/dist/analysts/detectors/detector-monorepo.js +10 -9
  29. package/dist/analysts/detectors/detector-performance.js +2 -1
  30. package/dist/analysts/detectors/detector-recommendation-architecture.js +1 -1
  31. package/dist/analysts/detectors/detector-security.js +1 -1
  32. package/dist/analysts/detectors/detector-structure.js +3 -3
  33. package/dist/analysts/github-actions/analysts/analyst-github-actions.js +5 -8
  34. package/dist/analysts/github-actions/detectors/detector-workflow-accessibility.js +1 -9
  35. package/dist/analysts/github-actions/detectors/detector-workflow-structure.js +1 -4
  36. package/dist/analysts/github-actions/detectors/detector-workflow-trigger-unsafe.js +6 -5
  37. package/dist/analysts/gitlab-ci/analysts/analyst-gitlab-ci.js +2 -2
  38. package/dist/analysts/gitlab-ci/detectors/detector-gitlab-ci-structure.js +0 -3
  39. package/dist/analysts/go/analysts/analyst-best-practices.js +2 -2
  40. package/dist/analysts/go/analysts/analyst-concurrency.js +1 -1
  41. package/dist/analysts/go/analysts/analyst-security.js +1 -1
  42. package/dist/analysts/go/detectors/detector-conventions.js +1 -1
  43. package/dist/analysts/html/analysts/analyst-html.js +10 -12
  44. package/dist/analysts/html/detectors/detector-html.js +3 -3
  45. package/dist/analysts/js-ts/analysts/analyst-commands-cli.js +22 -22
  46. package/dist/analysts/js-ts/analysts/analyst-functions-long.js +5 -5
  47. package/dist/analysts/js-ts/analysts/analyst-patterns-usage.js +6 -2
  48. package/dist/analysts/js-ts/detectors/detector-anti-patterns-async.js +81 -9
  49. package/dist/analysts/js-ts/detectors/detector-constructions-syntactic.js +35 -6
  50. package/dist/analysts/js-ts/detectors/detector-context-smart.js +6 -6
  51. package/dist/analysts/js-ts/detectors/detector-leaks-memory.js +1 -1
  52. package/dist/analysts/js-ts/detectors/detector-types-unsafe.js +33 -2
  53. package/dist/analysts/php/analysts/analyst-php.js +3 -3
  54. package/dist/analysts/php/detectors/detector-php-best-practices.js +5 -5
  55. package/dist/analysts/php/detectors/detector-php-security.js +5 -5
  56. package/dist/analysts/php/scorers/scorer-php.js +2 -2
  57. package/dist/analysts/plugins/detector-documentation.js +5 -6
  58. package/dist/analysts/plugins/detector-markdown.js +7 -7
  59. package/dist/analysts/python/analysts/analyst-python.js +2 -5
  60. package/dist/analysts/react/analysts/analyst-react-hooks.js +63 -8
  61. package/dist/analysts/react/analysts/analyst-react.js +45 -1
  62. package/dist/analysts/react/corrections/correction-react.js +40 -0
  63. package/dist/analysts/react/detectors/detector-react-best-practices.js +4 -4
  64. package/dist/analysts/react/scorers/scorer-react.js +36 -1
  65. package/dist/analysts/rust/analysts/analyst-best-practices.js +42 -3
  66. package/dist/analysts/rust/analysts/analyst-memory.js +2 -2
  67. package/dist/analysts/rust/analysts/analyst-rust.js +52 -5
  68. package/dist/analysts/rust/analysts/analyst-security.js +29 -1
  69. package/dist/analysts/scorers/scorer.js +5 -5
  70. package/dist/analysts/shell/analysts/analyst-shell.js +81 -5
  71. package/dist/analysts/shell/corrections/correction-shell.js +34 -1
  72. package/dist/analysts/shell/detectors/detector-shell-security.js +88 -0
  73. package/dist/analysts/shell/scorers/scorer-shell.js +58 -1
  74. package/dist/analysts/strategists/operator-structure.js +5 -7
  75. package/dist/analysts/strategists/suggestions-contextuais.js +5 -2
  76. package/dist/analysts/svg/analysts/analyst-svg.js +1 -0
  77. package/dist/analysts/tailwind/analysts/analyst-tailwind.js +79 -55
  78. package/dist/analysts/tailwind/corrections/correction-tailwind.js +116 -9
  79. package/dist/analysts/tailwind/detectors/detector-tailwind-best-practices.js +122 -7
  80. package/dist/analysts/tailwind/scorers/scorer-tailwind.js +62 -10
  81. package/dist/analysts/xml/analysts/analyst-xml.js +2 -2
  82. package/dist/app/index.js +1 -0
  83. package/dist/bin/cli-bootstrap.js +6 -4
  84. package/dist/bin/index.js +6 -4
  85. package/dist/caretakers/scoring.js +1 -0
  86. package/dist/cli/commands/command-analysts.js +1 -1
  87. package/dist/cli/commands/command-azure.js +1 -1
  88. package/dist/cli/commands/command-circleci.js +1 -1
  89. package/dist/cli/commands/command-compliance.js +1 -1
  90. package/dist/cli/commands/command-diagnose.js +10 -13
  91. package/dist/cli/commands/command-fix-types.js +9 -32
  92. package/dist/cli/commands/command-formatters.js +2 -2
  93. package/dist/cli/commands/command-gitlab-ci.js +1 -1
  94. package/dist/cli/commands/command-guardian.js +1 -1
  95. package/dist/cli/commands/command-imports.js +6 -6
  96. package/dist/cli/commands/command-jenkins.js +1 -1
  97. package/dist/cli/commands/command-licenses.js +1 -1
  98. package/dist/cli/commands/command-marketplace.js +3 -3
  99. package/dist/cli/commands/command-names.js +3 -8
  100. package/dist/cli/commands/command-perf.js +7 -9
  101. package/dist/cli/commands/command-plugins.js +4 -4
  102. package/dist/cli/diagnostic/auto-fix.js +2 -2
  103. package/dist/cli/diagnostic/exporters/json-exporter.js +1 -1
  104. package/dist/cli/diagnostic/exporters/sharded-exporter.js +0 -3
  105. package/dist/cli/diagnostic/exporters/svg-optimization-exporter.js +1 -1
  106. package/dist/cli/diagnostic/handlers/archetype-handler.js +3 -6
  107. package/dist/cli/diagnostic/handlers/auto-fix-handler.js +4 -4
  108. package/dist/cli/diagnostic/handlers/guardian-handler.js +1 -1
  109. package/dist/cli/diagnostic/processing-diagnostic.js +31 -35
  110. package/dist/cli/diagnostic/processing-helpers.js +2 -2
  111. package/dist/cli/handlers/fix-types-exporter.js +1 -4
  112. package/dist/cli/helpers/display-frame.js +5 -5
  113. package/dist/core/config/auto/auto-fix-config.js +0 -6
  114. package/dist/core/config/chalk-safe.js +2 -2
  115. package/dist/core/config/config.js +6 -8
  116. package/dist/core/config/conventions.js +1 -1
  117. package/dist/core/config/security.js +2 -2
  118. package/dist/core/execution/analysis-cache.js +5 -8
  119. package/dist/core/execution/executor.js +8 -19
  120. package/dist/core/execution/inquisitor.js +24 -21
  121. package/dist/core/execution/results.js +1 -0
  122. package/dist/core/execution/scanner.js +3 -3
  123. package/dist/core/messages/en/analysts/analyst-github-actions-messages.js +1 -1
  124. package/dist/core/messages/en/analysts/analyst-patterns-usage-messages.js +1 -0
  125. package/dist/core/messages/en/analysts/analyst-shell-messages.js +15 -0
  126. package/dist/core/messages/en/analysts/detector-constructions-syntactic-messages.js +6 -0
  127. package/dist/core/messages/en/analysts/detector-js-ts-messages.js +27 -0
  128. package/dist/core/messages/en/analysts/detector-react-messages.js +8 -0
  129. package/dist/core/messages/en/analysts/detector-shell-messages.js +17 -0
  130. package/dist/core/messages/en/analysts/detector-tailwind-messages.js +3 -3
  131. package/dist/core/messages/en/core/plugin-messages.js +31 -3
  132. package/dist/core/messages/en/log/log.js +4 -4
  133. package/dist/core/messages/en/ui/filter-config.js +1 -5
  134. package/dist/core/messages/i18n-system.js +3 -3
  135. package/dist/core/messages/index.js +3 -3
  136. package/dist/core/messages/ja/analysts/analyst-github-actions-messages.js +1 -1
  137. package/dist/core/messages/ja/analysts/analyst-patterns-usage-messages.js +1 -0
  138. package/dist/core/messages/ja/analysts/analyst-shell-messages.js +15 -0
  139. package/dist/core/messages/ja/analysts/detector-constructions-syntactic-messages.js +6 -0
  140. package/dist/core/messages/ja/analysts/detector-js-ts-messages.js +27 -0
  141. package/dist/core/messages/ja/analysts/detector-react-messages.js +8 -0
  142. package/dist/core/messages/ja/analysts/detector-shell-messages.js +17 -0
  143. package/dist/core/messages/ja/analysts/detector-tailwind-messages.js +3 -3
  144. package/dist/core/messages/ja/core/plugin-messages.js +31 -3
  145. package/dist/core/messages/ja/log/log.js +4 -4
  146. package/dist/core/messages/ja/ui/filter-config.js +1 -1
  147. package/dist/core/messages/pt/analysts/analyst-github-actions-messages.js +1 -1
  148. package/dist/core/messages/pt/analysts/analyst-patterns-usage-messages.js +1 -0
  149. package/dist/core/messages/pt/analysts/analyst-shell-messages.js +15 -0
  150. package/dist/core/messages/pt/analysts/detector-constructions-syntactic-messages.js +6 -0
  151. package/dist/core/messages/pt/analysts/detector-js-ts-messages.js +27 -0
  152. package/dist/core/messages/pt/analysts/detector-react-messages.js +8 -0
  153. package/dist/core/messages/pt/analysts/detector-shell-messages.js +17 -0
  154. package/dist/core/messages/pt/analysts/detector-tailwind-messages.js +3 -3
  155. package/dist/core/messages/pt/core/plugin-messages.js +31 -3
  156. package/dist/core/messages/pt/log/log-engine.js +6 -6
  157. package/dist/core/messages/pt/log/log.js +3 -3
  158. package/dist/core/messages/pt/ui/filter-config.js +1 -1
  159. package/dist/core/messages/shared/icons.js +3 -3
  160. package/dist/core/messages/zh/analysts/analyst-github-actions-messages.js +1 -1
  161. package/dist/core/messages/zh/analysts/analyst-patterns-usage-messages.js +1 -0
  162. package/dist/core/messages/zh/analysts/analyst-shell-messages.js +15 -0
  163. package/dist/core/messages/zh/analysts/detector-constructions-syntactic-messages.js +6 -0
  164. package/dist/core/messages/zh/analysts/detector-js-ts-messages.js +27 -0
  165. package/dist/core/messages/zh/analysts/detector-react-messages.js +8 -0
  166. package/dist/core/messages/zh/analysts/detector-shell-messages.js +17 -0
  167. package/dist/core/messages/zh/analysts/detector-tailwind-messages.js +3 -3
  168. package/dist/core/messages/zh/core/plugin-messages.js +31 -3
  169. package/dist/core/messages/zh/log/log.js +3 -3
  170. package/dist/core/messages/zh/ui/filter-config.js +1 -5
  171. package/dist/core/parsing/filters.js +3 -3
  172. package/dist/core/parsing/langs/json.js +1 -1
  173. package/dist/core/parsing/langs/python.js +1 -1
  174. package/dist/core/parsing/langs/typescript.js +1 -1
  175. package/dist/core/parsing/parser.js +2 -2
  176. package/dist/core/registry/file-registry.js +3 -2
  177. package/dist/core/registry/paths.js +3 -3
  178. package/dist/core/reporting/default-reporter.js +2 -2
  179. package/dist/core/schema/version.js +6 -6
  180. package/dist/core/workers/worker-executor.js +3 -3
  181. package/dist/core/workers/worker-pool.js +0 -0
  182. package/dist/guardian/baseline.js +1 -1
  183. package/dist/guardian/gpg.js +3 -1
  184. package/dist/guardian/integrity.js +2 -7
  185. package/dist/guardian/records.js +6 -1
  186. package/dist/guardian/result.js +1 -1
  187. package/dist/guardian/sentinel.js +6 -1
  188. package/dist/guardian/watcher-hidden.js +5 -1
  189. package/dist/licenses/fs-utils.js +4 -1
  190. package/dist/licenses/generate-notices.js +1 -1
  191. package/dist/licenses/header-options.js +0 -1
  192. package/dist/licenses/index.js +1 -0
  193. package/dist/licenses/normalizer.js +10 -9
  194. package/dist/node.loader.js +224 -4
  195. package/dist/reports/analise-async-patterns.js +1 -1
  196. package/dist/reports/filter-smart.js +2 -5
  197. package/dist/reports/generator-report.js +4 -4
  198. package/dist/reports/report-archetypes.js +10 -10
  199. package/dist/reports/report-caretaker-health.js +2 -1
  200. package/dist/sdk/analyzer.js +1 -1
  201. package/dist/shared/data-processing/fragment-report.js +10 -10
  202. package/dist/shared/data-processing/json.js +1 -1
  203. package/dist/shared/data-processing/occurrences.js +3 -2
  204. package/dist/shared/formatters/code-min-formatter.js +1 -1
  205. package/dist/shared/formatters/engines/prettier.js +1 -1
  206. package/dist/shared/formatters/engines/stylelint.js +6 -9
  207. package/dist/shared/formatters/formatter.js +8 -8
  208. package/dist/shared/formatters/formatters/code.js +3 -3
  209. package/dist/shared/formatters/formatters/css.js +2 -10
  210. package/dist/shared/formatters/formatters/dotfiles.js +1 -1
  211. package/dist/shared/formatters/formatters/html.js +1 -5
  212. package/dist/shared/formatters/formatters/json.js +0 -3
  213. package/dist/shared/formatters/formatters/markdown.js +18 -23
  214. package/dist/shared/formatters/formatters/properties.js +2 -2
  215. package/dist/shared/formatters/formatters/shell.js +1 -4
  216. package/dist/shared/formatters/formatters/sql.js +0 -3
  217. package/dist/shared/formatters/formatters/xml.js +5 -7
  218. package/dist/shared/formatters/formatters/yaml.js +2 -7
  219. package/dist/shared/formatters/index.js +1 -1
  220. package/dist/shared/helpers/framework-detector.js +1 -1
  221. package/dist/shared/helpers/json.js +1 -1
  222. package/dist/shared/helpers/magic-constants-whitelist.js +1 -1
  223. package/dist/shared/helpers/masking.js +17 -17
  224. package/dist/shared/helpers/reader-report.js +0 -12
  225. package/dist/shared/helpers/structural-hash.js +3 -2
  226. package/dist/shared/helpers/structure.js +1 -1
  227. package/dist/shared/marketplace/registry.js +1 -1
  228. package/dist/shared/marketplace/remote.js +4 -4
  229. package/dist/shared/persistence/persistence.js +0 -1
  230. package/dist/shared/plugins/core-plugin.js +3 -1
  231. package/dist/shared/plugins/registry.js +3 -2
  232. package/dist/types/caretakers/imports-barrel.js +1 -0
  233. package/dist/types/caretakers/index.js +4 -0
  234. package/dist/types/caretakers/map-reversion.js +1 -0
  235. package/dist/types/caretakers/pruning.js +1 -0
  236. package/dist/types/caretakers/scoring.js +1 -0
  237. package/dist/types/common/analysts.js +1 -1
  238. package/dist/types/core/config/filters.js +1 -0
  239. package/dist/types/core/config/index.js +2 -0
  240. package/dist/types/core/execution/ambiente.js +1 -0
  241. package/dist/types/core/execution/linguagens.js +1 -0
  242. package/dist/types/core/execution/parse-errors.js +1 -0
  243. package/dist/types/core/execution/results.js +1 -0
  244. package/dist/types/core/execution/structure-json.js +1 -0
  245. package/dist/types/core/index.js +1 -0
  246. package/dist/types/core/messages/index.js +1 -0
  247. package/dist/types/core/messages/log.js +1 -0
  248. package/dist/types/core/parsing/babel-narrow.js +1 -0
  249. package/dist/types/core/parsing/index.js +1 -0
  250. package/dist/types/core/runtime.js +1 -0
  251. package/dist/types/guardian/index.js +2 -0
  252. package/dist/types/guardian/integrity.js +7 -0
  253. package/dist/types/guardian/snapshot.js +1 -0
  254. package/dist/types/index.js +4 -4
  255. package/dist/types/licenses/index.js +2 -0
  256. package/dist/types/processing/display.js +16 -13
  257. package/dist/types/processing/filters.js +3 -7
  258. package/dist/types/reports/compliance.js +1 -0
  259. package/dist/types/reports/streaming.js +1 -0
  260. package/dist/types/scripts/index.js +1 -0
  261. package/dist/types/scripts/migrar-aliases.js +1 -0
  262. package/dist/types/sdk/index.js +1 -0
  263. package/dist/types/shared/index.js +2 -0
  264. package/dist/types/vulnerabilities/index.js +2 -0
  265. package/dist/vulnerabilities/npm-audit.js +0 -1
  266. package/dist/vulnerabilities/scanner.js +2 -1
  267. package/dist/vulnerabilities/vulnerabilities.js +0 -1
  268. package/package.json +11 -12
@@ -34,35 +34,33 @@ function collectCssIssues(src, relPath) {
34
34
  });
35
35
  }
36
36
  const current = stack[stack.length - 1];
37
- if (current) {
38
- const propMatch = /^([a-zA-Z-]+)\s*:\s*([^;]+)?/.exec(trimmed);
39
- if (propMatch) {
40
- const prop = propMatch[1].toLowerCase();
41
- const value = (propMatch[2] || '').trim();
42
- const prev = current.props[prop];
43
- if (prev) {
44
- const ctx = current.context ? {
45
- currentAtRule: current.context
46
- } : undefined;
47
- if (isLikelyIntentionalDuplicate(prop, prev.value, value, ctx)) {
48
- // Duplicata legítima (fallback vendor, viewport, cor, etc)
49
- }
50
- else if (prev.value === value) {
51
- // Mesma propriedade, mesmo valor = erro claro
52
- ocorrencias.push(warn(messages.CssMensagens.duplicatePropertySame(prop), relPath, line));
53
- }
54
- else {
55
- // Valores diferentes sem padrão claro = possível erro
56
- ocorrencias.push(warn(messages.CssMensagens.duplicatePropertyDifferent(prop, prev.value, value), relPath, line));
57
- }
37
+ const propMatch = /^([a-zA-Z-]+)\s*:\s*([^;]+)?/.exec(trimmed);
38
+ if (propMatch) {
39
+ const prop = propMatch[1].toLowerCase();
40
+ const value = (propMatch[2] || '').trim();
41
+ const prev = current.props[prop];
42
+ if (prop in current.props) {
43
+ const ctx = {
44
+ currentAtRule: current.context
45
+ };
46
+ if (isLikelyIntentionalDuplicate(prop, prev.value, value, ctx)) {
47
+ // Duplicata legítima (fallback vendor, viewport, cor, etc)
48
+ }
49
+ else if (prev.value === value) {
50
+ // Mesma propriedade, mesmo valor = erro claro
51
+ ocorrencias.push(warn(messages.CssMensagens.duplicatePropertySame(prop), relPath, line));
58
52
  }
59
53
  else {
60
- current.props[prop] = {
61
- line,
62
- value
63
- };
54
+ // Valores diferentes sem padrão claro = possível erro
55
+ ocorrencias.push(warn(messages.CssMensagens.duplicatePropertyDifferent(prop, prev.value, value), relPath, line));
64
56
  }
65
57
  }
58
+ else {
59
+ current.props[prop] = {
60
+ line,
61
+ value
62
+ };
63
+ }
66
64
  }
67
65
  // !important
68
66
  if (/!important/.test(trimmed)) {
@@ -125,7 +123,8 @@ function splitSimpleSelectors(selectorText) {
125
123
  function parseWithPostCssRoots(src, relPath) {
126
124
  const isScss = /\.scss$/i.test(relPath);
127
125
  const isSass = /\.sass$/i.test(relPath);
128
- const syntax = isScss ? postcssScss : isSass ? postcssSass : undefined;
126
+ const rawSyntax = isScss ? postcssScss : isSass ? postcssSass : undefined;
127
+ const syntax = rawSyntax;
129
128
  try {
130
129
  // Importante: `postcss.parse(..., { syntax })` ignora `syntax`.
131
130
  // Para aplicar parser SCSS/SASS corretamente, usamos `postcss().process`.
@@ -242,7 +241,7 @@ function collectCssIssuesFromPostCssAst(root, relPath) {
242
241
  const value = normalizeCssValue(`${(decl.value || '')}${decl.important ? ' !important' : ''}`);
243
242
  const line = getNodeLine(decl);
244
243
  const prev = props[propChave];
245
- if (prev) {
244
+ if (propChave in props) {
246
245
  const ctx = inFontFace ? {
247
246
  currentAtRule: 'font-face'
248
247
  } : undefined;
@@ -300,7 +299,7 @@ function collectCssIssuesFromPostCssAst(root, relPath) {
300
299
  });
301
300
  });
302
301
  // Detecta regras vazias (ignora @apply do Tailwind CSS)
303
- const hasApplyDirective = rule.nodes.some(node => node.type === 'atrule' && (node).name === 'apply') ?? false;
302
+ const hasApplyDirective = rule.nodes.some(node => node.type === 'atrule' && (node).name === 'apply');
304
303
  if (decls.length === 0 && !hasApplyDirective) {
305
304
  pushOnce(warn(messages.CssMensagens.emptyRule, relPath, getNodeLine(rule)));
306
305
  }
@@ -6,7 +6,6 @@ import { getMessages } from '../../../core/messages/index.js';
6
6
  import { splitLines } from '../../../shared/helpers/index.js';
7
7
  import { criarOcorrencia } from '../../../types/index.js';
8
8
  const STYLED_PATTERN = /styled\s*(\.|\()`|css\s*`|css\(|StyleSheet\.create|makeStyles|useStyles|createStyles|withStyles/;
9
- const THEME_PATTERN = /useTheme|ThemeProvider|withTheme|theme\s*\.|createTheme/;
10
9
  const PSEUDO_CLASSES = [':hover', ':focus', ':active', ':visited', ':disabled', ':checked', ':focus-within', ':focus-visible'];
11
10
  const BREAKPOINT_PATTERN = /\b(768|1024|1280|480|320)\s*px\b/;
12
11
  export const detectorCssInJsBestPractices = {
@@ -24,12 +23,8 @@ export const detectorCssInJsBestPractices = {
24
23
  if (!/styled-components|styled-jsx|emotion|@emotion|css\s*`|css\(/i.test(src)) {
25
24
  return ocorrencias;
26
25
  }
27
- let hasThemeAccess = false;
28
26
  linhas.forEach((linha, index) => {
29
27
  const numeroLinha = index + 1;
30
- if (THEME_PATTERN.test(linha)) {
31
- hasThemeAccess = true;
32
- }
33
28
  if (/createGlobalStyle|<style[^>]*global/i.test(linha)) {
34
29
  ocorrencias.push(criarOcorrencia({
35
30
  tipo: 'css-in-js-global-styles',
@@ -126,16 +121,14 @@ export const detectorCssInJsBestPractices = {
126
121
  }));
127
122
  }
128
123
  });
129
- if (!hasThemeAccess && STYLED_PATTERN.test(src)) {
130
- ocorrencias.push(criarOcorrencia({
131
- tipo: 'css-in-js-missing-theme-access',
132
- nivel: 'info',
133
- mensagem: m.CssInJsMensagens.missingThemeAccess,
134
- relPath,
135
- linha: 1,
136
- sugestao: m.CssInJsMensagens.missingThemeAccessSugestao
137
- }));
138
- }
124
+ ocorrencias.push(criarOcorrencia({
125
+ tipo: 'css-in-js-missing-theme-access',
126
+ nivel: 'info',
127
+ mensagem: m.CssInJsMensagens.missingThemeAccess,
128
+ relPath,
129
+ linha: 1,
130
+ sugestao: m.CssInJsMensagens.missingThemeAccessSugestao
131
+ }));
139
132
  const dynamicStyleCount = (src.match(/\$\{.*\}/g) ?? []).length;
140
133
  if (dynamicStyleCount > 10) {
141
134
  ocorrencias.push(criarOcorrencia({
@@ -232,10 +232,10 @@ function analisarArquivo(ast, relPath, _src) {
232
232
  tipo: 'named'
233
233
  });
234
234
  }
235
- else if ('declarations' in decl && decl.declarations) {
235
+ else if ('declarations' in decl) {
236
236
  // export const a = ..., b = ...
237
237
  for (const declarator of decl.declarations) {
238
- if (declarator.id && 'name' in declarator.id) {
238
+ if ('name' in declarator.id) {
239
239
  exports.push({
240
240
  nome: declarator.id.name,
241
241
  tipo: 'named'
@@ -21,7 +21,7 @@ const BUG_PATTERNS = [
21
21
  for (const m of matches) {
22
22
  return {
23
23
  confidence: 0.85,
24
- line: contarLinhasAtePos(content, m.index ?? 0),
24
+ line: contarLinhasAtePos(content, m.index),
25
25
  message: `Possível null pointer dereference: ${m[0].slice(0, MAX_MESSAGE_PREVIEW_LENGTH)}`,
26
26
  suggestion: 'Verificar se o resultado é null/undefined antes de encadear métodos',
27
27
  };
@@ -31,12 +31,12 @@ const BUG_PATTERNS = [
31
31
  let match;
32
32
  while ((match = optionalChainPattern.exec(content)) !== null) {
33
33
  const [, varName, funcName] = match;
34
- const line = contarLinhasAtePos(content, match.index);
35
34
  const beforeContent = content.slice(Math.max(0, match.index - 120), match.index);
36
35
  // pop() é seguro em qualquer array - retorna undefined se vazio, nunca lança
37
36
  if (funcName === 'pop') {
38
37
  continue;
39
38
  }
39
+ const line = contarLinhasAtePos(content, match.index);
40
40
  // Verificar se a variável é garantidamente um array (const = [] ou const = [...])
41
41
  const varDeclaration = content.slice(Math.max(0, match.index - 200), match.index);
42
42
  if (new RegExp(`(const|let|var)\\s+${varName}\\s*=\\s*\\[`).test(varDeclaration)) {
@@ -75,7 +75,7 @@ const BUG_PATTERNS = [
75
75
  for (const pattern of patterns) {
76
76
  const matches = content.matchAll(pattern);
77
77
  for (const m of matches) {
78
- const line = contarLinhasAtePos(content, m.index ?? 0);
78
+ const line = contarLinhasAtePos(content, m.index);
79
79
  if (!hasCleanup(content, line)) {
80
80
  return {
81
81
  confidence: 0.82,
@@ -104,7 +104,7 @@ const BUG_PATTERNS = [
104
104
  for (const pattern of asyncPatterns) {
105
105
  const matches = content.matchAll(pattern);
106
106
  for (const m of matches) {
107
- const line = contarLinhasAtePos(content, m.index ?? 0);
107
+ const line = contarLinhasAtePos(content, m.index);
108
108
  if (hasSharedState(content, line)) {
109
109
  return {
110
110
  confidence: 0.78,
@@ -133,7 +133,7 @@ const BUG_PATTERNS = [
133
133
  for (const pattern of patterns) {
134
134
  const matches = content.matchAll(pattern);
135
135
  for (const m of matches) {
136
- const line = contarLinhasAtePos(content, m.index ?? 0);
136
+ const line = contarLinhasAtePos(content, m.index);
137
137
  if (!hasValidation(content, line)) {
138
138
  return {
139
139
  confidence: 0.72,
@@ -157,7 +157,7 @@ const BUG_PATTERNS = [
157
157
  const pattern = /(?<![.\w])new\s+Promise\s*\([^)]*\)(?![^]*\.(then|catch|finally)\s*\()/g;
158
158
  const matches = content.matchAll(pattern);
159
159
  for (const m of matches) {
160
- const line = contarLinhasAtePos(content, m.index ?? 0);
160
+ const line = contarLinhasAtePos(content, m.index);
161
161
  const beforeContent = content.slice(Math.max(0, m.index - 20), m.index);
162
162
  if (/\b(await|return)\s+/.test(beforeContent)) {
163
163
  continue;
@@ -220,7 +220,7 @@ const BUG_PATTERNS = [
220
220
  const matches = content.matchAll(magicNumPattern);
221
221
  const results = [];
222
222
  for (const m of matches) {
223
- const line = contarLinhasAtePos(content, m.index ?? 0);
223
+ const line = contarLinhasAtePos(content, m.index);
224
224
  const lineContent = content.split('\n')[line - 1] || '';
225
225
  if (!lineContent.includes('const') && !lineContent.includes('let') && !lineContent.includes('var') && !lineContent.includes('//') && !lineContent.includes('/*')) {
226
226
  results.push({
@@ -252,7 +252,7 @@ const BUG_PATTERNS = [
252
252
  for (const pattern of patterns) {
253
253
  const matches = content.matchAll(pattern);
254
254
  for (const m of matches) {
255
- const line = contarLinhasAtePos(content, m.index ?? 0);
255
+ const line = contarLinhasAtePos(content, m.index);
256
256
  return {
257
257
  confidence: 0.88,
258
258
  line,
@@ -45,7 +45,7 @@ const visitorFragil = {
45
45
  const fragilidades = visitorState.resultados.get('codigo-fragil') ?? [];
46
46
  let depth = 0;
47
47
  let current = path;
48
- while (current) {
48
+ while (current.node.type !== 'Program') {
49
49
  if (current.isIfStatement()) {
50
50
  depth++;
51
51
  }
@@ -65,9 +65,9 @@ const visitorFragil = {
65
65
  FunctionDeclaration(path, state) {
66
66
  const visitorState = state;
67
67
  const fragilidades = visitorState.resultados.get('codigo-fragil') ?? [];
68
- const maxLinhas = config.ANALISE_LIMITES.CODIGO_FRAGIL.MAX_LINHAS_FUNCAO ?? LIMITES.LINHAS_FUNCAO;
68
+ const maxLinhas = config.ANALISE_LIMITES.CODIGO_FRAGIL.MAX_LINHAS_FUNCAO;
69
69
  const node = path.node;
70
- if (node.body.type === 'BlockStatement') {
70
+ {
71
71
  const inicio = node.loc?.start.line ?? 0;
72
72
  const fim = node.loc?.end.line ?? 0;
73
73
  const numLinhas = fim - inicio;
@@ -90,7 +90,7 @@ const visitorFragil = {
90
90
  if (isInVariableDeclarator(path) || isInArrayIndex(path)) {
91
91
  return;
92
92
  }
93
- frameworksDetectados ??= detectarFrameworks(process.cwd()).map(arquivo => arquivo.name);
93
+ frameworksDetectados = detectarFrameworks(process.cwd()).map(arquivo => arquivo.name);
94
94
  if (isWhitelistedConstant(value, frameworksDetectados)) {
95
95
  return;
96
96
  }
@@ -109,9 +109,9 @@ export const analistaCodigoFragil = {
109
109
  categoria: 'qualidade',
110
110
  descricao: 'Detecta padrões de código que podem levar a problemas futuros',
111
111
  limites: {
112
- maxLinhasFuncao: config.ANALISE_LIMITES.CODIGO_FRAGIL.MAX_LINHAS_FUNCAO ?? LIMITES.LINHAS_FUNCAO,
113
- maxParametros: config.ANALISE_LIMITES.CODIGO_FRAGIL.MAX_PARAMETROS ?? LIMITES.PARAMETROS_FUNCAO,
114
- maxNestedCallbacks: config.ANALISE_LIMITES.CODIGO_FRAGIL.MAX_NESTED_CALLBACKS ?? LIMITES.CALLBACKS_ANINHADOS
112
+ maxLinhasFuncao: config.ANALISE_LIMITES.CODIGO_FRAGIL.MAX_LINHAS_FUNCAO,
113
+ maxParametros: config.ANALISE_LIMITES.CODIGO_FRAGIL.MAX_PARAMETROS,
114
+ maxNestedCallbacks: config.ANALISE_LIMITES.CODIGO_FRAGIL.MAX_NESTED_CALLBACKS
115
115
  },
116
116
  visitor: visitorFragil,
117
117
  test: (relPath) => {
@@ -156,11 +156,12 @@ export const analistaCodigoFragil = {
156
156
  });
157
157
  }
158
158
  // Processamento AST: Otimizado (preColetado) ou Fallback
159
- if (ast.preColetado) {
160
- fragilidades.push(...ast.preColetado);
159
+ const astMeta = ast;
160
+ if (astMeta?.preColetado) {
161
+ fragilidades.push(...astMeta.preColetado);
161
162
  }
162
163
  else {
163
- const nodeToTraverse = ast.node ?? ast;
164
+ const nodeToTraverse = astMeta?.node ?? ast;
164
165
  const stateMock = {
165
166
  resultados: new Map([['codigo-fragil', []]])
166
167
  };
@@ -14,7 +14,7 @@ function ehImportSomenteTipo(node) {
14
14
  if (node.importKind === 'type') {
15
15
  return true;
16
16
  }
17
- const specifiers = node.specifiers || [];
17
+ const specifiers = node.specifiers;
18
18
  if (specifiers.length === 0) {
19
19
  return false;
20
20
  }
@@ -185,12 +185,12 @@ function processarImportDeclaration(caminho, state) {
185
185
  }
186
186
  const val = caminho.node.source.value;
187
187
  if (somenteTipo) {
188
- const specifiers = caminho.node.specifiers || [];
188
+ const specifiers = caminho.node.specifiers;
189
189
  for (const s of specifiers) {
190
190
  if (s.type === 'ImportDefaultSpecifier' || s.type === 'ImportSpecifier') {
191
191
  state.importsDeclarados.add(s.local.name);
192
192
  }
193
- else if (s.type === 'ImportNamespaceSpecifier') {
193
+ else {
194
194
  state.importsDeclarados.add(s.local.name);
195
195
  }
196
196
  }
@@ -240,12 +240,12 @@ function processarImportDeclaration(caminho, state) {
240
240
  });
241
241
  }
242
242
  }
243
- const specifiers = caminho.node.specifiers || [];
243
+ const specifiers = caminho.node.specifiers;
244
244
  for (const s of specifiers) {
245
245
  if (s.type === 'ImportDefaultSpecifier' || s.type === 'ImportSpecifier') {
246
246
  state.importsDeclarados.add(s.local.name);
247
247
  }
248
- else if (s.type === 'ImportNamespaceSpecifier') {
248
+ else {
249
249
  state.importsDeclarados.add(s.local.name);
250
250
  }
251
251
  }
@@ -13,7 +13,7 @@ export const analistaDuplicacoes = {
13
13
  test: (relPath) => {
14
14
  return /\.(js|jsx|ts|tsx|mjs|cjs)$/.test(relPath);
15
15
  },
16
- aplicar: async (src, relPath, ast, fullCaminho, contexto) => {
16
+ aplicar: (src, relPath, ast, fullCaminho, contexto) => {
17
17
  if (!ast || !src || !contexto) {
18
18
  return [];
19
19
  }
@@ -125,12 +125,12 @@ function extrairInfoFuncao(path, caminho, tipoFuncao, linhas) {
125
125
  if ('id' in node && node.id && 'name' in node.id) {
126
126
  nome = node.id.name;
127
127
  }
128
- else if ('key' in node && node.key && 'name' in node.key) {
128
+ else if ('key' in node && 'name' in node.key) {
129
129
  nome = node.key.name;
130
130
  }
131
131
  else if (path.parentPath.isVariableDeclarator()) {
132
132
  const parent = path.parentPath.node;
133
- nome = 'id' in parent && parent.id && 'name' in parent.id ? parent.id.name : 'assigned';
133
+ nome = 'id' in parent && 'name' in parent.id ? parent.id.name : 'assigned';
134
134
  }
135
135
  // Extrair parâmetros
136
136
  const parametros = ('params' in node && Array.isArray(node.params) ? node.params : []).map((param) => {
@@ -138,7 +138,7 @@ function extrairInfoFuncao(path, caminho, tipoFuncao, linhas) {
138
138
  if (param.type === 'Identifier' && 'name' in param) {
139
139
  return param.name;
140
140
  } // Identifier node
141
- if (param.type === 'RestElement' && 'argument' in param && param.argument && 'name' in param.argument) {
141
+ if (param.type === 'RestElement' && 'argument' in param && 'name' in param.argument) {
142
142
  return `...${param.argument.name}`; // RestElement node
143
143
  }
144
144
  return param.type;
@@ -257,10 +257,7 @@ function agruparPorTipoSimilaridade(duplicacoes) {
257
257
  return duplicacoes.reduce((acc, dup) => {
258
258
  const tipo = dup.tipoSimilaridade;
259
259
  if (tipo) {
260
- if (!acc[tipo]) {
261
- acc[tipo] = [];
262
- }
263
- acc[tipo].push(dup);
260
+ (acc[tipo] ??= []).push(dup);
264
261
  }
265
262
  return acc;
266
263
  }, {});
@@ -51,11 +51,12 @@ export function detectarMonorepo(baseDir) {
51
51
  info.type = 'nx';
52
52
  try {
53
53
  const nx = JSON.parse(fs.readFileSync(nxPath, 'utf-8'));
54
- if (nx.workspaceLayout?.appsDir) {
55
- workspaceGlobs.push(nx.workspaceLayout.appsDir);
54
+ const nxLayout = nx.workspaceLayout;
55
+ if (nxLayout?.appsDir) {
56
+ workspaceGlobs.push(nxLayout.appsDir);
56
57
  }
57
- if (nx.workspaceLayout?.libsDir) {
58
- workspaceGlobs.push(nx.workspaceLayout.libsDir);
58
+ if (nxLayout?.libsDir) {
59
+ workspaceGlobs.push(nxLayout.libsDir);
59
60
  }
60
61
  }
61
62
  catch { }
@@ -82,7 +83,7 @@ export function detectarMonorepo(baseDir) {
82
83
  if (Array.isArray(rootPkg.workspaces)) {
83
84
  workspaceGlobs = rootPkg.workspaces;
84
85
  }
85
- else if (typeof rootPkg.workspaces === 'object' && rootPkg.workspaces !== null) {
86
+ else if (typeof rootPkg.workspaces === 'object') {
86
87
  const ws = rootPkg.workspaces;
87
88
  if (Array.isArray(ws.packages)) {
88
89
  workspaceGlobs = ws.packages;
@@ -132,9 +133,9 @@ function resolverWorkspaces(rootDir, globs) {
132
133
  name: pkg.name || entry.name,
133
134
  dir: resolved,
134
135
  packageJson: pkg,
135
- dependencies: pkg.dependencies || {},
136
- devDependencies: pkg.devDependencies || {},
137
- peerDependencies: pkg.peerDependencies || {},
136
+ dependencies: pkg.dependencies,
137
+ devDependencies: pkg.devDependencies,
138
+ peerDependencies: pkg.peerDependencies,
138
139
  });
139
140
  }
140
141
  }
@@ -173,7 +174,7 @@ export const detectorMonorepo = {
173
174
  // Check for missing root scripts
174
175
  const rootPkg = lerPackageJson(baseDir);
175
176
  if (rootPkg) {
176
- const scripts = rootPkg.scripts || {};
177
+ const scripts = rootPkg.scripts;
177
178
  const expectedScripts = ['build', 'test', 'lint'];
178
179
  for (const script of expectedScripts) {
179
180
  if (!scripts[script]) {
@@ -174,7 +174,7 @@ function detectarProblemasPerformanceAST(ast, problemas, relPath) {
174
174
  const isLoop = (type) => ['ForStatement', 'WhileStatement', 'DoWhileStatement', 'ForInStatement', 'ForOfStatement'].includes(type);
175
175
  const checkNesting = (path) => {
176
176
  let parent = path.parentPath;
177
- while (parent) {
177
+ while ((parent.node).type !== 'Program') {
178
178
  if (isLoop(parent.type)) {
179
179
  problemas.push({
180
180
  tipo: 'inefficient-loop',
@@ -226,6 +226,7 @@ function mapearImpactoParaNivel(impacto) {
226
226
  case 'medio':
227
227
  return 'aviso';
228
228
  case 'baixo':
229
+ return 'info';
229
230
  case undefined: {
230
231
  throw new Error('Not implemented yet: undefined case');
231
232
  }
@@ -210,7 +210,7 @@ export const analistaRecomendacaoArquitetura = {
210
210
  }
211
211
  // MVC para projetos web tradicionais
212
212
  if (temFrontend && temControllers && !temEntities) {
213
- const scoreMvc = (temControllers ? 15 : 0) + (temFrontend ? 15 : 0);
213
+ const scoreMvc = 30;
214
214
  if (scoreMvc > melhorScore && !complexidadeBaixa) {
215
215
  arquiteturaRecomendada = 'MVC (Model-View-Controller)';
216
216
  melhorScore = scoreMvc;
@@ -377,7 +377,7 @@ function detectarProblemasAST(ast, problemas) {
377
377
  if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier' && ['query', 'execute', 'raw', 'queryRaw', 'executeRaw'].includes(callee.property.name)) {
378
378
  const firstArg = args[0];
379
379
  // Se o primeiro argumento é uma TemplateLiteral com expressões ou uma BinaryExpression (+)
380
- const isUnsafe = firstArg && (firstArg.type === 'TemplateLiteral' && firstArg.expressions.length > 0 || firstArg.type === 'BinaryExpression' && firstArg.operator === '+');
380
+ const isUnsafe = firstArg.type === 'TemplateLiteral' && firstArg.expressions.length > 0 || firstArg.type === 'BinaryExpression' && firstArg.operator === '+';
381
381
  if (isUnsafe) {
382
382
  // Verificar se parece usar parâmetros de qualquer forma
383
383
  const code = firstArg.type === 'TemplateLiteral' ? firstArg.quasis.map(delimitadorString => delimitadorString.value.raw).join('') : '';
@@ -24,8 +24,8 @@ export const detectorEstrutura = {
24
24
  // Conjuntos para buscas O(1) e deduplicação implícita
25
25
  const setCaminhos = new Set(caminhos);
26
26
  // Detecta contexto geral do projeto para análise inteligente
27
- const arquivoPrincipal = caminhos.find(caminho => /package\.json$/.test(caminho)) ?? caminhos[0] ?? '';
28
- const conteudoPrincipal = contexto.arquivos.find(arquivo => arquivo.relPath === arquivoPrincipal)?.content ?? '';
27
+ const arquivoPrincipal = caminhos?.find(caminho => /package\.json$/.test(caminho)) ?? caminhos?.[0];
28
+ const conteudoPrincipal = contexto.arquivos?.find(arquivo => arquivo.relPath === arquivoPrincipal)?.content ?? '';
29
29
  const contextoProjeto = detectarContextoProjeto({
30
30
  arquivo: arquivoPrincipal,
31
31
  conteudo: conteudoPrincipal,
@@ -161,7 +161,7 @@ export const detectorEstrutura = {
161
161
  // Lista de entrypoints potencialmente grande em repositórios com dependências; filtra ignorados
162
162
  const entrypointsAll = caminhosNorm.filter(caminho => /(^|[\\/])(cli|index|main)\.(ts|js)$/.test(caminho));
163
163
  const ignoredDyn = config.INCLUDE_EXCLUDE_RULES.globalExcludeGlob ?? [];
164
- const entrypoints = entrypointsAll.filter(caminho => !micromatch.isMatch(caminho, ignoredDyn || [])).sort();
164
+ const entrypoints = entrypointsAll.filter(caminho => !micromatch.isMatch(caminho, ignoredDyn)).sort();
165
165
  if (entrypoints.length > 1) {
166
166
  const MAX_LIST = 20;
167
167
  if (entrypoints.length > MAX_LIST) {
@@ -82,8 +82,8 @@ export const analistaGithubActionsGlobal = {
82
82
  });
83
83
  }
84
84
  // README
85
- const readme = ctx.arquivos.find(arquivo => /README\.md/i.test(arquivo.relPath ?? ''));
86
- const content = readme ? readme.content ?? '' : '';
85
+ const readme = ctx.arquivos?.find(arquivo => /README\.md/i.test(arquivo.relPath));
86
+ const content = readme?.content ?? '';
87
87
  if (readme && !/Code of Conduct|Código de Conduta/i.test(content)) {
88
88
  ores.push({
89
89
  tipo: 'GITHUB_ACTIONS_DOC_GOVERNANCE_MISSING',
@@ -272,7 +272,7 @@ function analisarEstrutura(wf, probs) {
272
272
  }
273
273
  // Detect Secrets in generic maps (for environment snippets in tests)
274
274
  for (const [chave, valor] of Object.entries(wf)) {
275
- if (/(KEY|TOKEN|SECRET|PASSWORD)/i.test(chave) && valor && typeof valor !== 'object' && !String(valor).includes('${{')) {
275
+ if (/(KEY|TOKEN|SECRET|PASSWORD)/i.test(chave) && valor && typeof valor !== 'object' && (typeof valor !== 'string' || !valor.includes('${{'))) {
276
276
  probs.push({
277
277
  tipo: 'env-sensivel',
278
278
  descricao: `Secret hardcoded em ${chave}`,
@@ -344,9 +344,6 @@ function analisarEstrutura(wf, probs) {
344
344
  }
345
345
  }
346
346
  function analisarStep(string, probs) {
347
- if (!string) {
348
- return;
349
- }
350
347
  // Pinning - only for non-verified orgs
351
348
  if (string.uses && /@v\d+/.test(string.uses)) {
352
349
  const actionRef = string.uses.split('@')[0];
@@ -369,7 +366,7 @@ function analisarStep(string, probs) {
369
366
  // Env
370
367
  if (string.env) {
371
368
  for (const [chave, valor] of Object.entries(string.env)) {
372
- if (/(KEY|TOKEN|SECRET|PASSWORD)/i.test(chave) && valor && typeof valor !== 'object' && !String(valor).includes('${{')) {
369
+ if (/(KEY|TOKEN|SECRET|PASSWORD)/i.test(chave) && valor && typeof valor !== 'object' && (typeof valor !== 'string' || !valor.includes('${{'))) {
373
370
  probs.push({
374
371
  tipo: 'env-sensivel',
375
372
  descricao: `Env ${chave} hardcoded`,
@@ -451,7 +448,7 @@ const ACTIONS_AUTO_EXPLICATIVAS = [
451
448
  */
452
449
  function analisarStepsSemNome(steps, probs) {
453
450
  for (const string of steps) {
454
- if (!string || string.name) {
451
+ if (string.name) {
455
452
  continue;
456
453
  }
457
454
  if (string.uses || string.run) {
@@ -19,12 +19,7 @@ export const detectorWorkflowAcessibilidade = {
19
19
  }
20
20
  const ocorrencias = [];
21
21
  const linhas = src.split('\n');
22
- let hasTimeout = false;
23
- linhas.forEach((linha) => {
24
- if (/timeout-minutes/.test(linha)) {
25
- hasTimeout = true;
26
- }
27
- });
22
+ const hasTimeout = linhas.some(linha => /timeout-minutes/.test(linha));
28
23
  if (!hasTimeout) {
29
24
  ocorrencias.push(criarOcorrencia({
30
25
  tipo: 'workflow-sem-timeout',
@@ -46,9 +41,6 @@ export function detectarProblemasAcessibilidade(workflow) {
46
41
  }
47
42
  let hasTimeout = wf['timeout-minutes'] !== undefined;
48
43
  for (const [, job] of Object.entries(wf.jobs)) {
49
- if (!job) {
50
- continue;
51
- }
52
44
  if (job['timeout-minutes']) {
53
45
  hasTimeout = true;
54
46
  }
@@ -56,15 +56,12 @@ export const detectorWorkflowEstrutura = {
56
56
  try {
57
57
  const doc = parseDocument(src);
58
58
  const workflow = doc.toJS();
59
- if (!workflow || typeof workflow !== 'object') {
59
+ if (!workflow) {
60
60
  return ocorrencias;
61
61
  }
62
62
  const jobs = workflow.jobs ?? {};
63
63
  const jobNames = Object.keys(jobs);
64
64
  for (const [jobId, job] of Object.entries(jobs)) {
65
- if (!job) {
66
- continue;
67
- }
68
65
  // --- 1. Jobs órfãos (needs referencia job inexistente) ---
69
66
  if (job.needs) {
70
67
  const needsList = Array.isArray(job.needs) ? job.needs : [job.needs];
@@ -28,7 +28,7 @@ export const detectorWorkflowTriggerInseguro = {
28
28
  try {
29
29
  const doc = parseDocument(src);
30
30
  const workflow = doc.toJS();
31
- if (!workflow || typeof workflow !== 'object') {
31
+ if (!workflow) {
32
32
  return ocorrencias;
33
33
  }
34
34
  const onConfig = workflow.on;
@@ -51,7 +51,7 @@ export const detectorWorkflowTriggerInseguro = {
51
51
  // Workflow disparado por tags, não por branches - não aplicar esta regra
52
52
  }
53
53
  else {
54
- const branches = typeof pushConf === 'object' && pushConf?.branches ? Array.isArray(pushConf.branches) ? pushConf.branches : [pushConf.branches] : typeof pushConf === 'object' && pushConf?.branches_ignore ? Array.isArray(pushConf.branches_ignore) ? pushConf.branches_ignore : [pushConf.branches_ignore] : undefined;
54
+ const branches = typeof pushConf === 'object' && pushConf.branches ? Array.isArray(pushConf.branches) ? pushConf.branches : [pushConf.branches] : typeof pushConf === 'object' && pushConf.branches_ignore ? Array.isArray(pushConf.branches_ignore) ? pushConf.branches_ignore : [pushConf.branches_ignore] : undefined;
55
55
  // Detectar push sem filtro de branches (triggeria em todos os branches)
56
56
  if (typeof pushConf === 'object' && !pushConf.branches && !pushConf.branches_ignore && !pushConf.tags || Array.isArray(pushConf) || typeof pushConf === 'string') {
57
57
  ocorrencias.push(criarOcorrencia({
@@ -124,7 +124,7 @@ export const detectorWorkflowTriggerInseguro = {
124
124
  // --- 4. Schedule em branches sensíveis ---
125
125
  if (onConfig.schedule) {
126
126
  const pushConf = onConfig.push;
127
- const targetBranches = typeof pushConf === 'object' && pushConf?.branches ? Array.isArray(pushConf.branches) ? pushConf.branches : [pushConf.branches] : undefined;
127
+ const targetBranches = typeof pushConf === 'object' && pushConf.branches ? Array.isArray(pushConf.branches) ? pushConf.branches : [pushConf.branches] : undefined;
128
128
  if (targetBranches) {
129
129
  for (const branch of targetBranches) {
130
130
  if (typeof branch === 'string' && branch.toLowerCase() === 'production') {
@@ -154,15 +154,16 @@ export const detectorWorkflowTriggerInseguro = {
154
154
  }
155
155
  // --- 6. Verificar se workflow executa actions perigosas em triggers de push amplo ---
156
156
  if (onConfig.push && !onConfig.pull_request) {
157
+ const sectionPush = onConfig.push;
157
158
  // Se push tem apenas tags, não é push em branches
158
- const isPushToTags = onConfig.push.tags && !onConfig.push.branches && !onConfig.push['branches-ignore'];
159
+ const isPushToTags = sectionPush.tags && !sectionPush.branches && !sectionPush['branches-ignore'];
159
160
  if (!isPushToTags) {
160
161
  const linhas = splitLines(src);
161
162
  const hasDangerousAction = linhas.some(linha => {
162
163
  const usesMatch = linha.match(/uses:\s*(.+)/);
163
164
  return usesMatch && DANGEROUS_ACTIONS.some(arquivo => linha.includes(arquivo));
164
165
  });
165
- const isPushToAllBranches = !onConfig.push.branches && !onConfig.push['branches-ignore'];
166
+ const isPushToAllBranches = !sectionPush.branches && !sectionPush['branches-ignore'];
166
167
  if (isPushToAllBranches && hasDangerousAction) {
167
168
  ocorrencias.push(criarOcorrencia({
168
169
  tipo: 'trigger-push-ampla-com-actions-perigosas',
@@ -23,7 +23,7 @@ export const analistaGitLabCI = {
23
23
  // 2. Análise Estrutural via YAML AST (para detecções mais complexas)
24
24
  const doc = parseDocument(conteudo);
25
25
  const pipeline = doc.toJS();
26
- if (pipeline) {
26
+ {
27
27
  // Exemplo: Detecção de jobs sem stage definido
28
28
  const stages = pipeline.stages ?? ['build', 'test', 'deploy'];
29
29
  void stages; // Reservado para futuras detecções de stages inválidos
@@ -34,7 +34,7 @@ export const analistaGitLabCI = {
34
34
  }
35
35
  // Se o valor for um objeto com 'script', provavelmente é um job
36
36
  const job = value;
37
- if (typeof job === 'object' && job !== null && (job.script || job.trigger || job.extends)) {
37
+ if (job.script || job.trigger || job.extends) {
38
38
  // Detecção de job muito longo (ex: mais de 15 linhas de script)
39
39
  if (Array.isArray(job.script) && job.script.length > 15) {
40
40
  ocorrencias.push({
@@ -15,9 +15,6 @@ export const detectorGitLabCIEstrutura = {
15
15
  try {
16
16
  const doc = parseDocument(src);
17
17
  const pipeline = doc.toJS();
18
- if (!pipeline) {
19
- return ocorrencias;
20
- }
21
18
  const stages = pipeline.stages;
22
19
  if (stages && stages.length > 5) {
23
20
  ocorrencias.push(criarOcorrencia({