@mocoto/mahoraga 0.14.1 → 0.14.3

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 (306) hide show
  1. package/README.md +0 -34
  2. package/dist/analysts/architects/analyst-structure.js +20 -20
  3. package/dist/analysts/architects/archetypes-custom.js +16 -19
  4. package/dist/analysts/architects/detector-archetypes.js +43 -36
  5. package/dist/analysts/architects/orchestrator-archetypes.js +3 -3
  6. package/dist/analysts/architects/signals-project-advanced.js +26 -28
  7. package/dist/analysts/architects/signals-project.js +2 -2
  8. package/dist/analysts/architects/strategist-structure.js +47 -66
  9. package/dist/analysts/azure-pipelines/analysts/analyst-azure.js +41 -35
  10. package/dist/analysts/azure-pipelines/corrections/correction-azure.js +22 -19
  11. package/dist/analysts/azure-pipelines/detectors/detector-azure-performance.js +21 -18
  12. package/dist/analysts/azure-pipelines/detectors/detector-azure-security.js +25 -20
  13. package/dist/analysts/azure-pipelines/detectors/detector-azure-structure.js +39 -40
  14. package/dist/analysts/azure-pipelines/scorers/scorer-azure.js +10 -7
  15. package/dist/analysts/circleci/analysts/analyst-circleci.js +33 -27
  16. package/dist/analysts/circleci/corrections/correction-circleci.js +22 -20
  17. package/dist/analysts/circleci/detectors/detector-circleci-performance.js +23 -19
  18. package/dist/analysts/circleci/detectors/detector-circleci-security.js +78 -82
  19. package/dist/analysts/circleci/detectors/detector-circleci-structure.js +22 -22
  20. package/dist/analysts/converters/convert-registry.js +1 -1
  21. package/dist/analysts/converters/engines/converter-gha-to-gitlab.js +17 -15
  22. package/dist/analysts/converters/index.js +1 -1
  23. package/dist/analysts/corrections/analyst-quick-fixes.js +25 -28
  24. package/dist/analysts/corrections/analyst-scoring.js +24 -21
  25. package/dist/analysts/corrections/auto-fix-engine.js +1 -1
  26. package/dist/analysts/corrections/corrector-structure.js +43 -42
  27. package/dist/analysts/corrections/fix-alias-imports.js +10 -6
  28. package/dist/analysts/corrections/fix-md-fences.js +23 -23
  29. package/dist/analysts/corrections/map-reversion.js +56 -53
  30. package/dist/analysts/corrections/pruning.js +19 -16
  31. package/dist/analysts/corrections/quick-fixes/fix-any-to-proper-type.js +57 -61
  32. package/dist/analysts/corrections/quick-fixes/fix-unknown-to-specific-type.js +30 -30
  33. package/dist/analysts/corrections/reorganize-barrels.js +13 -13
  34. package/dist/analysts/corrections/rewrite-testes-aliases.js +23 -22
  35. package/dist/analysts/corrections/scoring-shared.js +19 -18
  36. package/dist/analysts/corrections/type-safety/context-analyzer.js +15 -10
  37. package/dist/analysts/corrections/type-safety/type-analyzer.js +3 -3
  38. package/dist/analysts/corrections/type-safety/type-validator.js +4 -3
  39. package/dist/analysts/corrections/type-safety/usage-analyzer.js +39 -42
  40. package/dist/analysts/css/analysts/analyst-css.js +42 -42
  41. package/dist/analysts/css/corrections/correction-css.js +19 -11
  42. package/dist/analysts/css/detectors/detector-css-best-practices.js +129 -118
  43. package/dist/analysts/css/scorers/scorer-css.js +23 -18
  44. package/dist/analysts/css-in-js/analysts/analyst-css-in-js.js +24 -17
  45. package/dist/analysts/css-in-js/corrections/correction-css-in-js.js +31 -16
  46. package/dist/analysts/css-in-js/detectors/detector-css-in-js-best-practices.js +28 -30
  47. package/dist/analysts/css-in-js/scorers/scorer-css-in-js.js +79 -54
  48. package/dist/analysts/css-in-js/shared.js +7 -0
  49. package/dist/analysts/detectors/detector-architecture.js +91 -85
  50. package/dist/analysts/detectors/detector-bugs-ml.js +29 -27
  51. package/dist/analysts/detectors/detector-code-fragile.js +41 -36
  52. package/dist/analysts/detectors/detector-dependencies.js +5 -5
  53. package/dist/analysts/detectors/detector-duplications.js +26 -26
  54. package/dist/analysts/detectors/detector-monorepo.js +51 -45
  55. package/dist/analysts/detectors/detector-performance.js +24 -17
  56. package/dist/analysts/detectors/detector-recommendation-architecture.js +40 -36
  57. package/dist/analysts/detectors/detector-security.js +43 -42
  58. package/dist/analysts/detectors/detector-structure.js +31 -28
  59. package/dist/analysts/github-actions/analysts/analyst-github-actions.js +5 -8
  60. package/dist/analysts/github-actions/detectors/detector-workflow-accessibility.js +1 -9
  61. package/dist/analysts/github-actions/detectors/detector-workflow-structure.js +1 -4
  62. package/dist/analysts/github-actions/detectors/detector-workflow-trigger-unsafe.js +6 -5
  63. package/dist/analysts/gitlab-ci/analysts/analyst-gitlab-ci.js +2 -2
  64. package/dist/analysts/gitlab-ci/detectors/detector-gitlab-ci-structure.js +0 -3
  65. package/dist/analysts/go/analysts/analyst-best-practices.js +2 -2
  66. package/dist/analysts/go/analysts/analyst-concurrency.js +1 -1
  67. package/dist/analysts/go/analysts/analyst-security.js +1 -1
  68. package/dist/analysts/go/detectors/detector-conventions.js +1 -1
  69. package/dist/analysts/html/analysts/analyst-html.js +10 -12
  70. package/dist/analysts/html/detectors/detector-html.js +3 -3
  71. package/dist/analysts/js-ts/analysts/analyst-commands-cli.js +22 -22
  72. package/dist/analysts/js-ts/analysts/analyst-functions-long.js +5 -5
  73. package/dist/analysts/js-ts/analysts/analyst-patterns-usage.js +6 -2
  74. package/dist/analysts/js-ts/detectors/detector-anti-patterns-async.js +84 -9
  75. package/dist/analysts/js-ts/detectors/detector-constructions-syntactic.js +33 -10
  76. package/dist/analysts/js-ts/detectors/detector-context-smart.js +6 -6
  77. package/dist/analysts/js-ts/detectors/detector-leaks-memory.js +1 -1
  78. package/dist/analysts/js-ts/detectors/detector-types-unsafe.js +33 -2
  79. package/dist/analysts/php/analysts/analyst-php.js +3 -3
  80. package/dist/analysts/php/detectors/detector-php-best-practices.js +5 -5
  81. package/dist/analysts/php/detectors/detector-php-security.js +5 -5
  82. package/dist/analysts/php/scorers/scorer-php.js +2 -2
  83. package/dist/analysts/plugins/detector-documentation.js +6 -6
  84. package/dist/analysts/plugins/detector-markdown.js +7 -7
  85. package/dist/analysts/python/analysts/analyst-python.js +2 -5
  86. package/dist/analysts/react/analysts/analyst-react-hooks.js +66 -9
  87. package/dist/analysts/react/analysts/analyst-react.js +46 -2
  88. package/dist/analysts/react/corrections/correction-react.js +41 -1
  89. package/dist/analysts/react/detectors/detector-react-best-practices.js +5 -5
  90. package/dist/analysts/react/scorers/scorer-react.js +36 -1
  91. package/dist/analysts/registry/autodiscovery.js +2 -2
  92. package/dist/analysts/rust/analysts/analyst-best-practices.js +42 -3
  93. package/dist/analysts/rust/analysts/analyst-memory.js +2 -2
  94. package/dist/analysts/rust/analysts/analyst-rust.js +52 -5
  95. package/dist/analysts/rust/analysts/analyst-security.js +29 -1
  96. package/dist/analysts/scorers/scorer.js +5 -5
  97. package/dist/analysts/shell/analysts/analyst-shell.js +81 -5
  98. package/dist/analysts/shell/corrections/correction-shell.js +34 -1
  99. package/dist/analysts/shell/detectors/detector-shell-security.js +88 -0
  100. package/dist/analysts/shell/scorers/scorer-shell.js +58 -1
  101. package/dist/analysts/strategists/operator-structure.js +5 -7
  102. package/dist/analysts/strategists/suggestions-contextuais.js +5 -2
  103. package/dist/analysts/svg/analysts/analyst-svg.js +1 -0
  104. package/dist/analysts/tailwind/analysts/analyst-tailwind.js +79 -55
  105. package/dist/analysts/tailwind/corrections/correction-tailwind.js +108 -9
  106. package/dist/analysts/tailwind/detectors/detector-tailwind-best-practices.js +122 -7
  107. package/dist/analysts/tailwind/scorers/scorer-tailwind.js +62 -11
  108. package/dist/analysts/xml/analysts/analyst-xml.js +2 -2
  109. package/dist/app/index.js +1 -0
  110. package/dist/bin/cli-bootstrap.js +6 -4
  111. package/dist/bin/index.js +6 -4
  112. package/dist/caretakers/scoring.js +1 -0
  113. package/dist/cli/commands/command-analysts.js +4 -3
  114. package/dist/cli/commands/command-azure.js +1 -1
  115. package/dist/cli/commands/command-circleci.js +1 -1
  116. package/dist/cli/commands/command-compliance.js +1 -1
  117. package/dist/cli/commands/command-diagnose.js +10 -13
  118. package/dist/cli/commands/command-fix-types.js +9 -32
  119. package/dist/cli/commands/command-formatters.js +2 -2
  120. package/dist/cli/commands/command-github-actions.js +1 -1
  121. package/dist/cli/commands/command-gitlab-ci.js +1 -1
  122. package/dist/cli/commands/command-guardian.js +1 -1
  123. package/dist/cli/commands/command-imports.js +6 -6
  124. package/dist/cli/commands/command-jenkins.js +1 -1
  125. package/dist/cli/commands/command-licenses.js +1 -1
  126. package/dist/cli/commands/command-marketplace.js +7 -7
  127. package/dist/cli/commands/command-names.js +3 -8
  128. package/dist/cli/commands/command-perf.js +7 -9
  129. package/dist/cli/commands/command-plugins.js +4 -4
  130. package/dist/cli/diagnostic/auto-fix.js +2 -2
  131. package/dist/cli/diagnostic/exporters/json-exporter.js +1 -1
  132. package/dist/cli/diagnostic/exporters/sharded-exporter.js +0 -3
  133. package/dist/cli/diagnostic/exporters/svg-optimization-exporter.js +1 -1
  134. package/dist/cli/diagnostic/filters.js +9 -9
  135. package/dist/cli/diagnostic/handlers/archetype-handler.js +3 -6
  136. package/dist/cli/diagnostic/handlers/auto-fix-handler.js +4 -4
  137. package/dist/cli/diagnostic/handlers/guardian-handler.js +1 -1
  138. package/dist/cli/diagnostic/processing-diagnostic.js +31 -35
  139. package/dist/cli/diagnostic/processing-helpers.js +2 -2
  140. package/dist/cli/handlers/fix-types-exporter.js +1 -4
  141. package/dist/cli/helpers/display-frame.js +5 -5
  142. package/dist/core/config/auto/auto-fix-config.js +0 -6
  143. package/dist/core/config/chalk-safe.js +6 -6
  144. package/dist/core/config/config.js +11 -13
  145. package/dist/core/config/conventions.js +1 -1
  146. package/dist/core/config/security.js +2 -2
  147. package/dist/core/execution/analysis-cache.js +5 -8
  148. package/dist/core/execution/ast-cache.js +1 -3
  149. package/dist/core/execution/executor.js +8 -21
  150. package/dist/core/execution/inquisitor.js +24 -21
  151. package/dist/core/execution/results.js +1 -0
  152. package/dist/core/execution/scanner.js +4 -4
  153. package/dist/core/messages/en/analysts/analyst-github-actions-messages.js +1 -1
  154. package/dist/core/messages/en/analysts/analyst-patterns-usage-messages.js +1 -0
  155. package/dist/core/messages/en/analysts/analyst-shell-messages.js +15 -0
  156. package/dist/core/messages/en/analysts/detector-constructions-syntactic-messages.js +6 -0
  157. package/dist/core/messages/en/analysts/detector-js-ts-messages.js +27 -0
  158. package/dist/core/messages/en/analysts/detector-react-messages.js +8 -0
  159. package/dist/core/messages/en/analysts/detector-shell-messages.js +17 -0
  160. package/dist/core/messages/en/analysts/detector-tailwind-messages.js +3 -3
  161. package/dist/core/messages/en/analysts/report-circleci-messages.js +7 -7
  162. package/dist/core/messages/en/caretakers/caretaker-messages.js +1 -7
  163. package/dist/core/messages/en/core/plugin-messages.js +31 -3
  164. package/dist/core/messages/en/log/log.js +4 -4
  165. package/dist/core/messages/en/ui/filter-config.js +1 -5
  166. package/dist/core/messages/i18n-system.js +3 -3
  167. package/dist/core/messages/index.js +3 -3
  168. package/dist/core/messages/ja/analysts/analyst-github-actions-messages.js +1 -1
  169. package/dist/core/messages/ja/analysts/analyst-patterns-usage-messages.js +1 -0
  170. package/dist/core/messages/ja/analysts/analyst-shell-messages.js +15 -0
  171. package/dist/core/messages/ja/analysts/detector-constructions-syntactic-messages.js +6 -0
  172. package/dist/core/messages/ja/analysts/detector-js-ts-messages.js +27 -0
  173. package/dist/core/messages/ja/analysts/detector-react-messages.js +8 -0
  174. package/dist/core/messages/ja/analysts/detector-shell-messages.js +17 -0
  175. package/dist/core/messages/ja/analysts/detector-tailwind-messages.js +3 -3
  176. package/dist/core/messages/ja/analysts/report-circleci-messages.js +7 -7
  177. package/dist/core/messages/ja/caretakers/caretaker-messages.js +1 -7
  178. package/dist/core/messages/ja/core/plugin-messages.js +31 -3
  179. package/dist/core/messages/ja/log/log.js +4 -4
  180. package/dist/core/messages/ja/ui/filter-config.js +1 -1
  181. package/dist/core/messages/pt/analysts/analyst-github-actions-messages.js +1 -1
  182. package/dist/core/messages/pt/analysts/analyst-patterns-usage-messages.js +1 -0
  183. package/dist/core/messages/pt/analysts/analyst-shell-messages.js +15 -0
  184. package/dist/core/messages/pt/analysts/detector-constructions-syntactic-messages.js +6 -0
  185. package/dist/core/messages/pt/analysts/detector-js-ts-messages.js +27 -0
  186. package/dist/core/messages/pt/analysts/detector-react-messages.js +8 -0
  187. package/dist/core/messages/pt/analysts/detector-shell-messages.js +17 -0
  188. package/dist/core/messages/pt/analysts/detector-tailwind-messages.js +3 -3
  189. package/dist/core/messages/pt/analysts/report-circleci-messages.js +7 -7
  190. package/dist/core/messages/pt/caretakers/caretaker-messages.js +1 -7
  191. package/dist/core/messages/pt/core/plugin-messages.js +31 -3
  192. package/dist/core/messages/pt/log/log-engine.js +6 -6
  193. package/dist/core/messages/pt/log/log.js +3 -3
  194. package/dist/core/messages/pt/ui/filter-config.js +1 -1
  195. package/dist/core/messages/shared/icons.js +3 -3
  196. package/dist/core/messages/zh/analysts/analyst-github-actions-messages.js +1 -1
  197. package/dist/core/messages/zh/analysts/analyst-patterns-usage-messages.js +1 -0
  198. package/dist/core/messages/zh/analysts/analyst-shell-messages.js +15 -0
  199. package/dist/core/messages/zh/analysts/detector-constructions-syntactic-messages.js +6 -0
  200. package/dist/core/messages/zh/analysts/detector-js-ts-messages.js +27 -0
  201. package/dist/core/messages/zh/analysts/detector-react-messages.js +8 -0
  202. package/dist/core/messages/zh/analysts/detector-shell-messages.js +17 -0
  203. package/dist/core/messages/zh/analysts/detector-tailwind-messages.js +3 -3
  204. package/dist/core/messages/zh/analysts/report-circleci-messages.js +7 -7
  205. package/dist/core/messages/zh/caretakers/caretaker-messages.js +1 -7
  206. package/dist/core/messages/zh/core/plugin-messages.js +31 -3
  207. package/dist/core/messages/zh/log/log.js +3 -3
  208. package/dist/core/messages/zh/ui/filter-config.js +1 -5
  209. package/dist/core/parsing/filters.js +3 -3
  210. package/dist/core/parsing/langs/json.js +1 -1
  211. package/dist/core/parsing/langs/python.js +1 -1
  212. package/dist/core/parsing/langs/typescript.js +1 -1
  213. package/dist/core/parsing/parser.js +3 -4
  214. package/dist/core/parsing/utils.js +1 -0
  215. package/dist/core/registry/file-registry.js +3 -2
  216. package/dist/core/registry/paths.js +3 -3
  217. package/dist/core/reporting/default-reporter.js +2 -2
  218. package/dist/core/schema/version.js +6 -6
  219. package/dist/core/workers/worker-executor.js +3 -3
  220. package/dist/core/workers/worker-pool.js +0 -0
  221. package/dist/guardian/baseline.js +1 -1
  222. package/dist/guardian/gpg.js +3 -1
  223. package/dist/guardian/integrity.js +2 -7
  224. package/dist/guardian/records.js +7 -1
  225. package/dist/guardian/result.js +1 -1
  226. package/dist/guardian/sentinel.js +7 -1
  227. package/dist/guardian/watcher-hidden.js +6 -1
  228. package/dist/licenses/fs-utils.js +5 -1
  229. package/dist/licenses/generate-notices.js +1 -1
  230. package/dist/licenses/index.js +1 -0
  231. package/dist/licenses/normalizer.js +10 -9
  232. package/dist/node.loader.js +224 -4
  233. package/dist/reports/analise-async-patterns.js +1 -1
  234. package/dist/reports/filter-smart.js +2 -5
  235. package/dist/reports/generator-report.js +7 -7
  236. package/dist/reports/report-archetypes.js +10 -10
  237. package/dist/reports/report-caretaker-health.js +2 -1
  238. package/dist/reports/report-patterns-usage.js +1 -1
  239. package/dist/sdk/analyzer.js +1 -1
  240. package/dist/shared/data-processing/fragment-report.js +11 -11
  241. package/dist/shared/data-processing/json.js +1 -1
  242. package/dist/shared/data-processing/occurrences.js +3 -2
  243. package/dist/shared/formatters/code-min-formatter.js +1 -1
  244. package/dist/shared/formatters/engines/prettier.js +1 -1
  245. package/dist/shared/formatters/engines/stylelint.js +43 -40
  246. package/dist/shared/formatters/formatter.js +8 -8
  247. package/dist/shared/formatters/formatters/code.js +3 -3
  248. package/dist/shared/formatters/formatters/css.js +2 -10
  249. package/dist/shared/formatters/formatters/dotfiles.js +1 -1
  250. package/dist/shared/formatters/formatters/html.js +1 -5
  251. package/dist/shared/formatters/formatters/json.js +0 -3
  252. package/dist/shared/formatters/formatters/markdown.js +19 -24
  253. package/dist/shared/formatters/formatters/properties.js +2 -2
  254. package/dist/shared/formatters/formatters/shell.js +1 -4
  255. package/dist/shared/formatters/formatters/sql.js +0 -3
  256. package/dist/shared/formatters/formatters/xml.js +5 -7
  257. package/dist/shared/formatters/formatters/yaml.js +2 -7
  258. package/dist/shared/formatters/index.js +1 -1
  259. package/dist/shared/helpers/framework-detector.js +6 -2
  260. package/dist/shared/helpers/json.js +1 -1
  261. package/dist/shared/helpers/magic-constants-whitelist.js +1 -1
  262. package/dist/shared/helpers/masking.js +17 -17
  263. package/dist/shared/helpers/reader-report.js +0 -12
  264. package/dist/shared/helpers/structural-hash.js +3 -2
  265. package/dist/shared/helpers/structure.js +1 -1
  266. package/dist/shared/marketplace/registry.js +1 -1
  267. package/dist/shared/marketplace/remote.js +4 -4
  268. package/dist/shared/persistence/persistence.js +13 -6
  269. package/dist/shared/plugins/core-plugin.js +5 -1
  270. package/dist/shared/plugins/registry.js +3 -2
  271. package/dist/types/analysts/tailwind-correction.js +2 -0
  272. package/dist/types/caretakers/imports-barrel.js +1 -0
  273. package/dist/types/caretakers/index.js +4 -0
  274. package/dist/types/caretakers/map-reversion.js +1 -0
  275. package/dist/types/caretakers/pruning.js +1 -0
  276. package/dist/types/caretakers/scoring.js +1 -0
  277. package/dist/types/common/analysts.js +1 -1
  278. package/dist/types/core/config/filters.js +1 -0
  279. package/dist/types/core/config/index.js +2 -0
  280. package/dist/types/core/execution/ambiente.js +1 -0
  281. package/dist/types/core/execution/linguagens.js +1 -0
  282. package/dist/types/core/execution/parse-errors.js +1 -0
  283. package/dist/types/core/execution/results.js +1 -0
  284. package/dist/types/core/execution/structure-json.js +1 -0
  285. package/dist/types/core/index.js +1 -0
  286. package/dist/types/core/messages/index.js +1 -0
  287. package/dist/types/core/messages/log.js +1 -0
  288. package/dist/types/core/parsing/babel-narrow.js +1 -0
  289. package/dist/types/core/parsing/index.js +1 -0
  290. package/dist/types/core/runtime.js +1 -0
  291. package/dist/types/guardian/index.js +2 -0
  292. package/dist/types/guardian/integrity.js +7 -0
  293. package/dist/types/guardian/snapshot.js +1 -0
  294. package/dist/types/index.js +4 -4
  295. package/dist/types/licenses/index.js +2 -0
  296. package/dist/types/processing/display.js +16 -13
  297. package/dist/types/processing/filters.js +8 -11
  298. package/dist/types/reports/compliance.js +1 -0
  299. package/dist/types/reports/streaming.js +1 -0
  300. package/dist/types/scripts/index.js +1 -0
  301. package/dist/types/scripts/migrar-aliases.js +1 -0
  302. package/dist/types/sdk/index.js +1 -0
  303. package/dist/types/shared/index.js +2 -0
  304. package/dist/types/vulnerabilities/index.js +2 -0
  305. package/dist/vulnerabilities/scanner.js +3 -2
  306. package/package.json +14 -20
@@ -2,30 +2,33 @@
2
2
  import { messages } from '../../../core/messages/index.js';
3
3
  import { splitLines } from '../../../shared/helpers/index.js';
4
4
  import { criarOcorrencia } from '../../../types/index.js';
5
+ function processarLinhaAzure(linha, numeroLinha, correcoes) {
6
+ if (/vmImage:\s*['"]?.*latest['"]?/i.test(linha)) {
7
+ correcoes.push({
8
+ tipo: 'azure-fix-latest-image',
9
+ original: linha,
10
+ sugerido: linha.replace(/latest/i, 'ubuntu-22.04'),
11
+ linha: numeroLinha,
12
+ confianca: 90
13
+ });
14
+ }
15
+ if (/(password|token|secret|key):\s*['"]?([^$({][a-zA-Z0-9_\-]{8,})['"]?/gi.test(linha)) {
16
+ const replaced = linha.replace(/['"][^'"]+['"]/, '$(SECRET_NAME)');
17
+ correcoes.push({
18
+ tipo: 'azure-use-variable',
19
+ original: linha,
20
+ sugerido: replaced,
21
+ linha: numeroLinha,
22
+ confianca: 90
23
+ });
24
+ }
25
+ }
5
26
  export function detectarCorrecoesAzure(src, _relPath) {
6
27
  const correcoes = [];
7
28
  const linhas = splitLines(src);
8
29
  linhas.forEach((linha, index) => {
9
30
  const numeroLinha = index + 1;
10
- if (/vmImage:\s*['"]?.*latest['"]?/i.test(linha)) {
11
- correcoes.push({
12
- tipo: 'azure-fix-latest-image',
13
- original: linha,
14
- sugerido: linha.replace(/latest/i, 'ubuntu-22.04'),
15
- linha: numeroLinha,
16
- confianca: 90
17
- });
18
- }
19
- if (/(password|token|secret|key):\s*['"]?([^$({][a-zA-Z0-9_\-]{8,})['"]?/gi.test(linha)) {
20
- const replaced = linha.replace(/['"][^'"]+['"]/, '$(SECRET_NAME)');
21
- correcoes.push({
22
- tipo: 'azure-use-variable',
23
- original: linha,
24
- sugerido: replaced,
25
- linha: numeroLinha,
26
- confianca: 90
27
- });
28
- }
31
+ processarLinhaAzure(linha, numeroLinha, correcoes);
29
32
  });
30
33
  if (!/task:\s*Cache@/i.test(src) && !/CacheBeta@/i.test(src)) {
31
34
  correcoes.push({
@@ -1,6 +1,26 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  import { messages } from '../../../core/messages/index.js';
3
3
  import { criarOcorrencia } from '../../../types/index.js';
4
+ function detectarParallelDeployment(src, relPath, ocorrencias) {
5
+ if (/environment:/i.test(src) && /deploy/i.test(src)) {
6
+ const deployJobs = src.match(/- job:\s*\w+/g);
7
+ const deployStages = src.match(/- stage:\s*\w+/g);
8
+ const totalDeployUnits = (deployJobs ? deployJobs.length : 0) + (deployStages ? deployStages.length : 0);
9
+ if (totalDeployUnits > 1) {
10
+ const hasDependsOn = /dependsOn:/i.test(src);
11
+ if (!hasDependsOn) {
12
+ ocorrencias.push(criarOcorrencia({
13
+ tipo: 'azure-pipelines-missing-parallel-deployment',
14
+ nivel: 'info',
15
+ mensagem: messages.RelatorioAzureMensagens.missingParallelDeployment,
16
+ relPath,
17
+ linha: 1,
18
+ sugestao: messages.RelatorioAzureMensagens.missingParallelDeploymentSugestao,
19
+ }));
20
+ }
21
+ }
22
+ }
23
+ }
4
24
  export const detectorAzurePerformance = {
5
25
  nome: 'detector-azure-performance',
6
26
  categoria: 'performance',
@@ -47,24 +67,7 @@ export const detectorAzurePerformance = {
47
67
  }));
48
68
  }
49
69
  // 4. Parallel deployment detection
50
- if (/environment:/i.test(src) && /deploy/i.test(src)) {
51
- const deployJobs = src.match(/- job:\s*\w+/g);
52
- const deployStages = src.match(/- stage:\s*\w+/g);
53
- const totalDeployUnits = (deployJobs ? deployJobs.length : 0) + (deployStages ? deployStages.length : 0);
54
- if (totalDeployUnits > 1) {
55
- const hasDependsOn = /dependsOn:/i.test(src);
56
- if (!hasDependsOn) {
57
- ocorrencias.push(criarOcorrencia({
58
- tipo: 'azure-pipelines-missing-parallel-deployment',
59
- nivel: 'info',
60
- mensagem: messages.RelatorioAzureMensagens.missingParallelDeployment,
61
- relPath,
62
- linha: 1,
63
- sugestao: messages.RelatorioAzureMensagens.missingParallelDeploymentSugestao,
64
- }));
65
- }
66
- }
67
- }
70
+ detectarParallelDeployment(src, relPath, ocorrencias);
68
71
  // 5. Large artifact staging
69
72
  const publishArtifacts = src.match(/task:\s*PublishBuildArtifacts@/gi);
70
73
  if (publishArtifacts && publishArtifacts.length > 3) {
@@ -16,6 +16,30 @@ const SECRET_PATTERNS = [
16
16
  /(connection[_-]?string)\s*[:=]\s*['"][^'"]+['"]/gi,
17
17
  /(azure[_-]?devops[_-]?pat)\s*[:=]\s*['"][^'"]+['"]/gi,
18
18
  ];
19
+ function detectarSecretVarsPlainText(src, relPath, ocorrencias) {
20
+ const variableLines = src.match(/^\s*variables\s*:\s*$/im);
21
+ if (!variableLines) {
22
+ return;
23
+ }
24
+ const varSection = src.slice(src.indexOf('variables:'));
25
+ const secretVars = varSection.match(/([a-zA-Z_]\w*)\s*:\s*['"][^'"]+['"]/g);
26
+ if (!secretVars) {
27
+ return;
28
+ }
29
+ for (const sv of secretVars) {
30
+ const varName = sv.split(':')[0].trim();
31
+ if (/(password|token|secret|key|connection)/i.test(varName)) {
32
+ ocorrencias.push(criarOcorrencia({
33
+ tipo: 'azure-pipelines-secret-variable-plaintext',
34
+ nivel: 'erro',
35
+ mensagem: `Variável '${varName}' definida em texto plano na seção variables`,
36
+ relPath,
37
+ linha: 1,
38
+ sugestao: 'Use um grupo de variáveis com proteção de segredo ou referencie como $(varName)',
39
+ }));
40
+ }
41
+ }
42
+ }
19
43
  export const detectorAzureSecurity = {
20
44
  nome: 'detector-azure-security',
21
45
  categoria: 'seguranca',
@@ -148,26 +172,7 @@ export const detectorAzureSecurity = {
148
172
  }
149
173
  }
150
174
  // 10. Secret variable detection in plain text
151
- const variableLines = src.match(/^\s*variables\s*:\s*$/im);
152
- if (variableLines) {
153
- const varSection = src.slice(src.indexOf('variables:'));
154
- const secretVars = varSection.match(/([a-zA-Z_]\w*)\s*:\s*['"][^'"]+['"]/g);
155
- if (secretVars) {
156
- for (const sv of secretVars) {
157
- const varName = sv.split(':')[0].trim();
158
- if (/(password|token|secret|key|connection)/i.test(varName)) {
159
- ocorrencias.push(criarOcorrencia({
160
- tipo: 'azure-pipelines-secret-variable-plaintext',
161
- nivel: 'erro',
162
- mensagem: `Variável '${varName}' definida em texto plano na seção variables`,
163
- relPath,
164
- linha: 1,
165
- sugestao: 'Use um grupo de variáveis com proteção de segredo ou referencie como $(varName)',
166
- }));
167
- }
168
- }
169
- }
170
- }
175
+ detectarSecretVarsPlainText(src, relPath, ocorrencias);
171
176
  return ocorrencias;
172
177
  }
173
178
  };
@@ -2,6 +2,44 @@
2
2
  import { parseDocument } from 'yaml';
3
3
  import { messages } from '../../../core/messages/index.js';
4
4
  import { criarOcorrencia } from '../../../types/index.js';
5
+ function processarJobsAzure(jobs, relPath, ocorrencias) {
6
+ const jobNames = jobs.map(j => j.job ?? '').filter(Boolean);
7
+ for (const job of jobs) {
8
+ if (job.dependsOn && !jobNames.includes(job.dependsOn)) {
9
+ ocorrencias.push(criarOcorrencia({
10
+ tipo: 'azure-job-orfao',
11
+ nivel: 'erro',
12
+ mensagem: `Job '${job.job ?? job.displayName}' depende de '${job.dependsOn}' que não existe`,
13
+ relPath, linha: 1,
14
+ sugestao: 'Verifique se o nome do job no dependsOn está correto',
15
+ }));
16
+ }
17
+ }
18
+ if (jobs.length > 3 && jobs.every(j => !j.dependsOn || jobNames.indexOf(j.dependsOn) === jobNames.indexOf(j.job ?? '') - 1)) {
19
+ const hasParallel = jobs.some(j => !j.dependsOn && j !== jobs[0]);
20
+ if (!hasParallel) {
21
+ ocorrencias.push(criarOcorrencia({
22
+ tipo: 'azure-sem-paralelismo',
23
+ nivel: 'info',
24
+ mensagem: 'Pipeline com jobs estritamente sequenciais',
25
+ relPath, linha: 1,
26
+ sugestao: 'Considere paralelizar jobs independentes',
27
+ }));
28
+ }
29
+ }
30
+ for (const job of jobs) {
31
+ if (job.job && !job.displayName) {
32
+ ocorrencias.push(criarOcorrencia({
33
+ tipo: 'azure-missing-job-displayname',
34
+ nivel: 'info',
35
+ mensagem: messages.RelatorioAzureMensagens.missingJobDisplayName,
36
+ relPath,
37
+ linha: 1,
38
+ sugestao: messages.RelatorioAzureMensagens.missingJobDisplayNameSugestao,
39
+ }));
40
+ }
41
+ }
42
+ }
5
43
  export const detectorAzureEstrutura = {
6
44
  nome: 'detector-azure-estrutura',
7
45
  categoria: 'workflows',
@@ -15,48 +53,9 @@ export const detectorAzureEstrutura = {
15
53
  try {
16
54
  const doc = parseDocument(src);
17
55
  const pipeline = doc.toJS();
18
- if (!pipeline) {
19
- return ocorrencias;
20
- }
21
56
  const jobs = pipeline.jobs;
22
57
  if (jobs && Array.isArray(jobs)) {
23
- const jobNames = jobs.map(j => j.job ?? '').filter(Boolean);
24
- for (const job of jobs) {
25
- if (job.dependsOn && !jobNames.includes(job.dependsOn)) {
26
- ocorrencias.push(criarOcorrencia({
27
- tipo: 'azure-job-orfao',
28
- nivel: 'erro',
29
- mensagem: `Job '${job.job ?? job.displayName}' depende de '${job.dependsOn}' que não existe`,
30
- relPath, linha: 1,
31
- sugestao: 'Verifique se o nome do job no dependsOn está correto',
32
- }));
33
- }
34
- }
35
- if (jobs.length > 3 && jobs.every(j => !j.dependsOn || jobNames.indexOf(j.dependsOn) === jobNames.indexOf(j.job ?? '') - 1)) {
36
- const hasParallel = jobs.some(j => !j.dependsOn && j !== jobs[0]);
37
- if (!hasParallel) {
38
- ocorrencias.push(criarOcorrencia({
39
- tipo: 'azure-sem-paralelismo',
40
- nivel: 'info',
41
- mensagem: 'Pipeline com jobs estritamente sequenciais',
42
- relPath, linha: 1,
43
- sugestao: 'Considere paralelizar jobs independentes',
44
- }));
45
- }
46
- }
47
- // Display name conventions
48
- for (const job of jobs) {
49
- if (job.job && !job.displayName) {
50
- ocorrencias.push(criarOcorrencia({
51
- tipo: 'azure-missing-job-displayname',
52
- nivel: 'info',
53
- mensagem: messages.RelatorioAzureMensagens.missingJobDisplayName,
54
- relPath,
55
- linha: 1,
56
- sugestao: messages.RelatorioAzureMensagens.missingJobDisplayNameSugestao,
57
- }));
58
- }
59
- }
58
+ processarJobsAzure(jobs, relPath, ocorrencias);
60
59
  }
61
60
  const container = pipeline;
62
61
  const containers = container.resources?.containers;
@@ -1,6 +1,15 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  import { messages } from '../../../core/messages/index.js';
3
3
  import { splitLines } from '../../../shared/helpers/index.js';
4
+ function detectarVulnerableTask(srcCompleto) {
5
+ return Object.keys({
6
+ 'Npm@1': 'Npm@1',
7
+ 'NuGetCommand@2': 'NuGetCommand@2',
8
+ 'DotNetCoreCLI@1': 'DotNetCoreCLI@1',
9
+ 'VSBuild@1': 'VSBuild@1',
10
+ 'VsTest@1': 'VsTest@1',
11
+ }).some(task => new RegExp(`task:\\s*${task.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`, 'i').test(srcCompleto));
12
+ }
4
13
  export function pontuarAzure(src, _relPath) {
5
14
  let score = 0;
6
15
  const maxScore = 100;
@@ -16,13 +25,7 @@ export function pontuarAzure(src, _relPath) {
16
25
  else {
17
26
  issues.push(messages.RelatorioAzureMensagens.vulnerableTask);
18
27
  }
19
- const isVulnerableTask = Object.keys({
20
- 'Npm@1': 'Npm@1',
21
- 'NuGetCommand@2': 'NuGetCommand@2',
22
- 'DotNetCoreCLI@1': 'DotNetCoreCLI@1',
23
- 'VSBuild@1': 'VSBuild@1',
24
- 'VsTest@1': 'VsTest@1',
25
- }).some(task => new RegExp(`task:\\s*${task.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`, 'i').test(srcCompleto));
28
+ const isVulnerableTask = detectarVulnerableTask(srcCompleto);
26
29
  if (!isVulnerableTask) {
27
30
  score += 5;
28
31
  strengths.push('Tasks com versões seguras');
@@ -3,6 +3,23 @@ import { parseDocument } from 'yaml';
3
3
  import { detectorCircleCIPerformance } from '../detectors/detector-circleci-performance.js';
4
4
  import { detectorCircleCISecurity, obterDetectoresCircleCI } from '../detectors/detector-circleci-security.js';
5
5
  import { detectorCircleCIEstrutura } from '../detectors/detector-circleci-structure.js';
6
+ function processarJobsCircleCI(jobs, relPath, ocorrencias) {
7
+ if (!jobs) {
8
+ return;
9
+ }
10
+ for (const [jobName, job] of Object.entries(jobs)) {
11
+ if (job.steps && job.steps.length > 20) {
12
+ ocorrencias.push({
13
+ tipo: 'CIRCLECI_JOB_TOO_MANY_STEPS',
14
+ mensagem: `Job '${jobName}' possui muitos steps (${job.steps.length})`,
15
+ relPath,
16
+ linha: 1,
17
+ nivel: 'info',
18
+ sugestao: 'Agrupe steps repetitivos em "commands" para maior legibilidade'
19
+ });
20
+ }
21
+ }
22
+ }
6
23
  export const analistaCircleCI = {
7
24
  nome: 'circleci',
8
25
  categoria: 'workflows',
@@ -14,7 +31,11 @@ export const analistaCircleCI = {
14
31
  const securityResults = await Promise.resolve(detectorCircleCISecurity.aplicar(conteudo, relPath, null, _fc, contexto));
15
32
  const performanceResults = await Promise.resolve(detectorCircleCIPerformance.aplicar(conteudo, relPath, null, _fc, contexto));
16
33
  const structureResults = await Promise.resolve(detectorCircleCIEstrutura.aplicar(conteudo, relPath, null, _fc, contexto));
17
- ocorrencias.push(...securityResults, ...performanceResults, ...structureResults);
34
+ for (const r of [securityResults, performanceResults, structureResults]) {
35
+ if (r) {
36
+ ocorrencias.push(...(Array.isArray(r) ? r : [r]));
37
+ }
38
+ }
18
39
  // Run registered plugins
19
40
  const caminhos = contexto?.arquivos.map(arquivo => arquivo.relPath) ?? [];
20
41
  const plugResults = await Promise.all(obterDetectoresCircleCI().map(d => Promise.resolve(d.testar({ content: conteudo }, { conteudo, caminhos })).catch(() => [])));
@@ -32,33 +53,18 @@ export const analistaCircleCI = {
32
53
  }
33
54
  const doc = parseDocument(conteudo);
34
55
  const config = doc.toJS();
35
- if (config) {
36
- const configData = config;
37
- if (configData.version && parseFloat(String(configData.version)) < 2.1) {
38
- ocorrencias.push({
39
- tipo: 'CIRCLECI_OBSOLETE_VERSION',
40
- mensagem: `Versão do CircleCI (${configData.version}) está obsoleta`,
41
- relPath,
42
- linha: 1,
43
- nivel: 'aviso',
44
- sugestao: 'Atualize para a versão 2.1 para usar Orbs, Commands e outras melhorias modernas'
45
- });
46
- }
47
- if (configData.jobs) {
48
- for (const [jobName, job] of Object.entries(configData.jobs)) {
49
- if (job.steps && job.steps.length > 20) {
50
- ocorrencias.push({
51
- tipo: 'CIRCLECI_JOB_TOO_MANY_STEPS',
52
- mensagem: `Job '${jobName}' possui muitos steps (${job.steps.length})`,
53
- relPath,
54
- linha: 1,
55
- nivel: 'info',
56
- sugestao: 'Agrupe steps repetitivos em "commands" para maior legibilidade'
57
- });
58
- }
59
- }
60
- }
56
+ const configData = config;
57
+ if (configData.version && parseFloat(String(configData.version)) < 2.1) {
58
+ ocorrencias.push({
59
+ tipo: 'CIRCLECI_OBSOLETE_VERSION',
60
+ mensagem: `Versão do CircleCI (${configData.version}) está obsoleta`,
61
+ relPath,
62
+ linha: 1,
63
+ nivel: 'aviso',
64
+ sugestao: 'Atualize para a versão 2.1 para usar Orbs, Commands e outras melhorias modernas'
65
+ });
61
66
  }
67
+ processarJobsCircleCI(configData.jobs, relPath, ocorrencias);
62
68
  }
63
69
  catch {
64
70
  // Falha silenciosa no parse
@@ -2,30 +2,32 @@
2
2
  import { messages } from '../../../core/messages/index.js';
3
3
  import { splitLines } from '../../../shared/helpers/index.js';
4
4
  import { criarOcorrencia } from '../../../types/index.js';
5
+ function processarLinhaCircleCI(linha, numeroLinha, correcoes) {
6
+ if (/\bimage:\s*[^\s@]+:latest\b/.test(linha)) {
7
+ correcoes.push({
8
+ tipo: 'circleci-fix-latest-image',
9
+ original: linha,
10
+ sugerido: linha.replace(/:latest\b/, ':3.18'),
11
+ linha: numeroLinha,
12
+ confianca: 90
13
+ });
14
+ }
15
+ if (/\b(TOKEN|SECRET|PASSWORD|KEY|ACCESS_KEY)\b.*:\s*['"][^'"]+['"]/i.test(linha) && !/\$/.test(linha)) {
16
+ const replaced = linha.replace(/['"][^'"]+['"]/, '"$VAR_NAME"');
17
+ correcoes.push({
18
+ tipo: 'circleci-use-context',
19
+ original: linha,
20
+ sugerido: replaced,
21
+ linha: numeroLinha,
22
+ confianca: 90
23
+ });
24
+ }
25
+ }
5
26
  export function detectarCorrecoesCircleCI(src, _relPath) {
6
27
  const correcoes = [];
7
28
  const linhas = splitLines(src);
8
29
  linhas.forEach((linha, index) => {
9
- const numeroLinha = index + 1;
10
- if (/\bimage:\s*[^\s@]+:latest\b/.test(linha)) {
11
- correcoes.push({
12
- tipo: 'circleci-fix-latest-image',
13
- original: linha,
14
- sugerido: linha.replace(/:latest\b/, ':3.18'),
15
- linha: numeroLinha,
16
- confianca: 90
17
- });
18
- }
19
- if (/\b(TOKEN|SECRET|PASSWORD|KEY|ACCESS_KEY)\b.*:\s*['"][^'"]+['"]/i.test(linha) && !/\$/.test(linha)) {
20
- const replaced = linha.replace(/['"][^'"]+['"]/, '"$VAR_NAME"');
21
- correcoes.push({
22
- tipo: 'circleci-use-context',
23
- original: linha,
24
- sugerido: replaced,
25
- linha: numeroLinha,
26
- confianca: 90
27
- });
28
- }
30
+ processarLinhaCircleCI(linha, index + 1, correcoes);
29
31
  });
30
32
  if (!/save_cache:/.test(src) && !/restore_cache:/.test(src)) {
31
33
  const firstJobLine = linhas.findIndex(l => /steps:/.test(l));
@@ -2,6 +2,28 @@
2
2
  import { messages } from '../../../core/messages/index.js';
3
3
  import { criarOcorrencia } from '../../../types/index.js';
4
4
  const MSG = messages.RelatorioCircleciMensagens;
5
+ function analisarJobsSequenciais(src, relPath, ocorrencias) {
6
+ const jobSections = src.split(/^jobs:/m);
7
+ if (jobSections.length <= 1) {
8
+ return 0;
9
+ }
10
+ const jobsBlock = jobSections[1].split(/^workflows:|^orbs:|^commands:|^executors:/m)[0];
11
+ const jobNames = jobsBlock.match(/^\s{2}\w[^:]+:/gm);
12
+ const jobCountEstimate = jobNames ? jobNames.length : 0;
13
+ if (jobCountEstimate > 2) {
14
+ const needsCount = (jobsBlock.match(/needs:/g) ?? []).length;
15
+ if (needsCount >= jobCountEstimate - 1) {
16
+ ocorrencias.push(criarOcorrencia({
17
+ tipo: 'circleci-sequential-test-jobs',
18
+ nivel: 'info',
19
+ mensagem: typeof MSG.sequentialTestJobs === 'function' ? (MSG.sequentialTestJobs)() : MSG.sequentialTestJobs,
20
+ relPath, linha: 1,
21
+ sugestao: typeof MSG.sequentialTestJobsSugestao === 'function' ? (MSG.sequentialTestJobsSugestao)() : MSG.sequentialTestJobsSugestao
22
+ }));
23
+ }
24
+ }
25
+ return jobCountEstimate;
26
+ }
5
27
  export const detectorCircleCIPerformance = {
6
28
  nome: 'detector-circleci-performance',
7
29
  categoria: 'performance',
@@ -73,25 +95,7 @@ export const detectorCircleCIPerformance = {
73
95
  }));
74
96
  }
75
97
  // 6. Sequential test jobs analysis
76
- let jobCountEstimate = 0;
77
- const jobSections = src.split(/^jobs:/m);
78
- if (jobSections.length > 1) {
79
- const jobsBlock = jobSections[1].split(/^workflows:|^orbs:|^commands:|^executors:/m)[0];
80
- const jobNames = jobsBlock.match(/^\s{2}\w[^:]+:/gm);
81
- jobCountEstimate = jobNames ? jobNames.length : 0;
82
- if (jobCountEstimate > 2) {
83
- const needsCount = (jobsBlock.match(/needs:/g) ?? []).length;
84
- if (needsCount >= jobCountEstimate - 1) {
85
- ocorrencias.push(criarOcorrencia({
86
- tipo: 'circleci-sequential-test-jobs',
87
- nivel: 'info',
88
- mensagem: typeof MSG.sequentialTestJobs === 'function' ? (MSG.sequentialTestJobs)() : MSG.sequentialTestJobs,
89
- relPath, linha: 1,
90
- sugestao: typeof MSG.sequentialTestJobsSugestao === 'function' ? (MSG.sequentialTestJobsSugestao)() : MSG.sequentialTestJobsSugestao
91
- }));
92
- }
93
- }
94
- }
98
+ const jobCountEstimate = analisarJobsSequenciais(src, relPath, ocorrencias);
95
99
  // 7. Single executor type for all jobs
96
100
  const executorTypes = new Set();
97
101
  const machineMatch = src.match(/machine:/g);
@@ -13,6 +13,83 @@ function _isProblemaWorkflowArray(val) {
13
13
  return Array.isArray(val);
14
14
  }
15
15
  const MSG = messages.RelatorioCircleciMensagens;
16
+ function processarLinhaSegurancaCircleCI(linha, index, linhas, relPath, ocorrencias) {
17
+ const numeroLinha = index + 1;
18
+ if (/\b(TOKEN|SECRET|PASSWORD|KEY|ACCESS_KEY)\b.*:\s*['"][^'"]+['"]/i.test(linha) && !/\$/.test(linha)) {
19
+ ocorrencias.push(criarOcorrencia({
20
+ tipo: 'circleci-hardcoded-secret',
21
+ nivel: 'erro',
22
+ mensagem: typeof MSG.hardcodedKeyInConfig === 'function' ? (MSG.hardcodedKeyInConfig)() : MSG.hardcodedKeyInConfig,
23
+ relPath,
24
+ linha: numeroLinha,
25
+ sugestao: typeof MSG.hardcodedKeyInConfigSugestao === 'function' ? (MSG.hardcodedKeyInConfigSugestao)() : MSG.hardcodedKeyInConfigSugestao
26
+ }));
27
+ }
28
+ if (/\bimage:\s*[^\s@]+:latest\b/.test(linha) || (/\bimage:\s*[^\s@:]+$/.test(linha.trim()) && linha.includes('image:'))) {
29
+ ocorrencias.push(criarOcorrencia({
30
+ tipo: 'circleci-image-latest',
31
+ nivel: 'aviso',
32
+ mensagem: 'Uso de imagem :latest ou sem tag específica',
33
+ relPath,
34
+ linha: numeroLinha,
35
+ sugestao: 'Use um digest (SHA) ou uma versão fixa para garantir segurança e reprodutibilidade'
36
+ }));
37
+ }
38
+ if (/resource_class\s*:\s*(medium|small)\s*$/.test(linha)) {
39
+ ocorrencias.push(criarOcorrencia({
40
+ tipo: 'circleci-default-resource-class',
41
+ nivel: 'info',
42
+ mensagem: typeof MSG.missingResourceClass === 'function' ? (MSG.missingResourceClass)() : MSG.missingResourceClass,
43
+ relPath,
44
+ linha: numeroLinha,
45
+ sugestao: typeof MSG.missingResourceClassSugestao === 'function' ? (MSG.missingResourceClassSugestao)() : MSG.missingResourceClassSugestao
46
+ }));
47
+ }
48
+ if (/context\s*:/.test(linha)) {
49
+ const nextLines = linhas.slice(index + 1, index + 5);
50
+ const hasBranchFilter = nextLines.some(l => /branches\s*:/.test(l));
51
+ if (!hasBranchFilter) {
52
+ ocorrencias.push(criarOcorrencia({
53
+ tipo: 'circleci-context-no-branch-restriction',
54
+ nivel: 'aviso',
55
+ mensagem: typeof MSG.unsafeContextUsage === 'function' ? (MSG.unsafeContextUsage)() : MSG.unsafeContextUsage,
56
+ relPath,
57
+ linha: numeroLinha,
58
+ sugestao: typeof MSG.unsafeContextUsageSugestao === 'function' ? (MSG.unsafeContextUsageSugestao)() : MSG.unsafeContextUsageSugestao
59
+ }));
60
+ }
61
+ }
62
+ if (/fingerprint\s*:\s*['"][^'"]+['"]/.test(linha) || /\bkey\s*:\s*['"][A-Za-z0-9+/]{20,}['"]/.test(linha)) {
63
+ ocorrencias.push(criarOcorrencia({
64
+ tipo: 'circleci-hardcoded-fingerprint',
65
+ nivel: 'erro',
66
+ mensagem: typeof MSG.hardcodedKeyInConfig === 'function' ? (MSG.hardcodedKeyInConfig)() : MSG.hardcodedKeyInConfig,
67
+ relPath,
68
+ linha: numeroLinha,
69
+ sugestao: typeof MSG.hardcodedKeyInConfigSugestao === 'function' ? (MSG.hardcodedKeyInConfigSugestao)() : MSG.hardcodedKeyInConfigSugestao
70
+ }));
71
+ }
72
+ if (/checkout\s*:/.test(linha) && /path\s*:/.test(linha)) {
73
+ ocorrencias.push(criarOcorrencia({
74
+ tipo: 'circleci-custom-checkout-path',
75
+ nivel: 'info',
76
+ mensagem: typeof MSG.dangerousCheckoutPath === 'function' ? (MSG.dangerousCheckoutPath)() : MSG.dangerousCheckoutPath,
77
+ relPath,
78
+ linha: numeroLinha,
79
+ sugestao: typeof MSG.dangerousCheckoutPathSugestao === 'function' ? (MSG.dangerousCheckoutPathSugestao)() : MSG.dangerousCheckoutPathSugestao
80
+ }));
81
+ }
82
+ if (/\bsudo\s+/.test(linha)) {
83
+ ocorrencias.push(criarOcorrencia({
84
+ tipo: 'circleci-sudo-usage',
85
+ nivel: 'aviso',
86
+ mensagem: typeof MSG.sudoWithoutPasswordless === 'function' ? (MSG.sudoWithoutPasswordless)() : MSG.sudoWithoutPasswordless,
87
+ relPath,
88
+ linha: numeroLinha,
89
+ sugestao: typeof MSG.sudoWithoutPasswordlessSugestao === 'function' ? (MSG.sudoWithoutPasswordlessSugestao)() : MSG.sudoWithoutPasswordlessSugestao
90
+ }));
91
+ }
92
+ }
16
93
  export const detectorCircleCISecurity = {
17
94
  nome: 'detector-circleci-security',
18
95
  categoria: 'security',
@@ -25,88 +102,7 @@ export const detectorCircleCISecurity = {
25
102
  const ocorrencias = [];
26
103
  const linhas = splitLines(src);
27
104
  linhas.forEach((linha, index) => {
28
- const numeroLinha = index + 1;
29
- // 1. Secrets hardcoded em environment
30
- if (/\b(TOKEN|SECRET|PASSWORD|KEY|ACCESS_KEY)\b.*:\s*['"][^'"]+['"]/i.test(linha) && !/\$/.test(linha)) {
31
- ocorrencias.push(criarOcorrencia({
32
- tipo: 'circleci-hardcoded-secret',
33
- nivel: 'erro',
34
- mensagem: typeof MSG.hardcodedKeyInConfig === 'function' ? (MSG.hardcodedKeyInConfig)() : MSG.hardcodedKeyInConfig,
35
- relPath,
36
- linha: numeroLinha,
37
- sugestao: typeof MSG.hardcodedKeyInConfigSugestao === 'function' ? (MSG.hardcodedKeyInConfigSugestao)() : MSG.hardcodedKeyInConfigSugestao
38
- }));
39
- }
40
- // 2. Uso de imagem :latest
41
- if (/\bimage:\s*[^\s@]+:latest\b/.test(linha) || (/\bimage:\s*[^\s@:]+$/.test(linha.trim()) && linha.includes('image:'))) {
42
- ocorrencias.push(criarOcorrencia({
43
- tipo: 'circleci-image-latest',
44
- nivel: 'aviso',
45
- mensagem: 'Uso de imagem :latest ou sem tag específica',
46
- relPath,
47
- linha: numeroLinha,
48
- sugestao: 'Use um digest (SHA) ou uma versão fixa para garantir segurança e reprodutibilidade'
49
- }));
50
- }
51
- // 3. Resource class analysis
52
- if (/resource_class\s*:\s*(medium|small)\s*$/.test(linha)) {
53
- ocorrencias.push(criarOcorrencia({
54
- tipo: 'circleci-default-resource-class',
55
- nivel: 'info',
56
- mensagem: typeof MSG.missingResourceClass === 'function' ? (MSG.missingResourceClass)() : MSG.missingResourceClass,
57
- relPath,
58
- linha: numeroLinha,
59
- sugestao: typeof MSG.missingResourceClassSugestao === 'function' ? (MSG.missingResourceClassSugestao)() : MSG.missingResourceClassSugestao
60
- }));
61
- }
62
- // 4. Context usage without branch restriction
63
- if (/context\s*:/.test(linha)) {
64
- const nextLines = linhas.slice(index + 1, index + 5);
65
- const hasBranchFilter = nextLines.some(l => /branches\s*:/.test(l));
66
- if (!hasBranchFilter) {
67
- ocorrencias.push(criarOcorrencia({
68
- tipo: 'circleci-context-no-branch-restriction',
69
- nivel: 'aviso',
70
- mensagem: typeof MSG.unsafeContextUsage === 'function' ? (MSG.unsafeContextUsage)() : MSG.unsafeContextUsage,
71
- relPath,
72
- linha: numeroLinha,
73
- sugestao: typeof MSG.unsafeContextUsageSugestao === 'function' ? (MSG.unsafeContextUsageSugestao)() : MSG.unsafeContextUsageSugestao
74
- }));
75
- }
76
- }
77
- // 5. Hardcoded key/fingerprint
78
- if (/fingerprint\s*:\s*['"][^'"]+['"]/.test(linha) || /\bkey\s*:\s*['"][A-Za-z0-9+/]{20,}['"]/.test(linha)) {
79
- ocorrencias.push(criarOcorrencia({
80
- tipo: 'circleci-hardcoded-fingerprint',
81
- nivel: 'erro',
82
- mensagem: typeof MSG.hardcodedKeyInConfig === 'function' ? (MSG.hardcodedKeyInConfig)() : MSG.hardcodedKeyInConfig,
83
- relPath,
84
- linha: numeroLinha,
85
- sugestao: typeof MSG.hardcodedKeyInConfigSugestao === 'function' ? (MSG.hardcodedKeyInConfigSugestao)() : MSG.hardcodedKeyInConfigSugestao
86
- }));
87
- }
88
- // 6. Dangerous checkout path
89
- if (/checkout\s*:/.test(linha) && /path\s*:/.test(linha)) {
90
- ocorrencias.push(criarOcorrencia({
91
- tipo: 'circleci-custom-checkout-path',
92
- nivel: 'info',
93
- mensagem: typeof MSG.dangerousCheckoutPath === 'function' ? (MSG.dangerousCheckoutPath)() : MSG.dangerousCheckoutPath,
94
- relPath,
95
- linha: numeroLinha,
96
- sugestao: typeof MSG.dangerousCheckoutPathSugestao === 'function' ? (MSG.dangerousCheckoutPathSugestao)() : MSG.dangerousCheckoutPathSugestao
97
- }));
98
- }
99
- // 7. Sudo usage
100
- if (/\bsudo\s+/.test(linha)) {
101
- ocorrencias.push(criarOcorrencia({
102
- tipo: 'circleci-sudo-usage',
103
- nivel: 'aviso',
104
- mensagem: typeof MSG.sudoWithoutPasswordless === 'function' ? (MSG.sudoWithoutPasswordless)() : MSG.sudoWithoutPasswordless,
105
- relPath,
106
- linha: numeroLinha,
107
- sugestao: typeof MSG.sudoWithoutPasswordlessSugestao === 'function' ? (MSG.sudoWithoutPasswordlessSugestao)() : MSG.sudoWithoutPasswordlessSugestao
108
- }));
109
- }
105
+ processarLinhaSegurancaCircleCI(linha, index, linhas, relPath, ocorrencias);
110
106
  });
111
107
  return ocorrencias;
112
108
  }