@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.
- package/README.md +0 -34
- package/dist/analysts/architects/analyst-structure.js +20 -20
- package/dist/analysts/architects/archetypes-custom.js +16 -19
- package/dist/analysts/architects/detector-archetypes.js +43 -36
- package/dist/analysts/architects/orchestrator-archetypes.js +3 -3
- package/dist/analysts/architects/signals-project-advanced.js +26 -28
- package/dist/analysts/architects/signals-project.js +2 -2
- package/dist/analysts/architects/strategist-structure.js +47 -66
- package/dist/analysts/azure-pipelines/analysts/analyst-azure.js +41 -35
- package/dist/analysts/azure-pipelines/corrections/correction-azure.js +22 -19
- package/dist/analysts/azure-pipelines/detectors/detector-azure-performance.js +21 -18
- package/dist/analysts/azure-pipelines/detectors/detector-azure-security.js +25 -20
- package/dist/analysts/azure-pipelines/detectors/detector-azure-structure.js +39 -40
- package/dist/analysts/azure-pipelines/scorers/scorer-azure.js +10 -7
- package/dist/analysts/circleci/analysts/analyst-circleci.js +33 -27
- package/dist/analysts/circleci/corrections/correction-circleci.js +22 -20
- package/dist/analysts/circleci/detectors/detector-circleci-performance.js +23 -19
- package/dist/analysts/circleci/detectors/detector-circleci-security.js +78 -82
- package/dist/analysts/circleci/detectors/detector-circleci-structure.js +22 -22
- package/dist/analysts/converters/convert-registry.js +1 -1
- package/dist/analysts/converters/engines/converter-gha-to-gitlab.js +17 -15
- package/dist/analysts/converters/index.js +1 -1
- package/dist/analysts/corrections/analyst-quick-fixes.js +25 -28
- package/dist/analysts/corrections/analyst-scoring.js +24 -21
- package/dist/analysts/corrections/auto-fix-engine.js +1 -1
- package/dist/analysts/corrections/corrector-structure.js +43 -42
- package/dist/analysts/corrections/fix-alias-imports.js +10 -6
- package/dist/analysts/corrections/fix-md-fences.js +23 -23
- package/dist/analysts/corrections/map-reversion.js +56 -53
- package/dist/analysts/corrections/pruning.js +19 -16
- package/dist/analysts/corrections/quick-fixes/fix-any-to-proper-type.js +57 -61
- package/dist/analysts/corrections/quick-fixes/fix-unknown-to-specific-type.js +30 -30
- package/dist/analysts/corrections/reorganize-barrels.js +13 -13
- package/dist/analysts/corrections/rewrite-testes-aliases.js +23 -22
- package/dist/analysts/corrections/scoring-shared.js +19 -18
- package/dist/analysts/corrections/type-safety/context-analyzer.js +15 -10
- package/dist/analysts/corrections/type-safety/type-analyzer.js +3 -3
- package/dist/analysts/corrections/type-safety/type-validator.js +4 -3
- package/dist/analysts/corrections/type-safety/usage-analyzer.js +39 -42
- package/dist/analysts/css/analysts/analyst-css.js +42 -42
- package/dist/analysts/css/corrections/correction-css.js +19 -11
- package/dist/analysts/css/detectors/detector-css-best-practices.js +129 -118
- package/dist/analysts/css/scorers/scorer-css.js +23 -18
- package/dist/analysts/css-in-js/analysts/analyst-css-in-js.js +24 -17
- package/dist/analysts/css-in-js/corrections/correction-css-in-js.js +31 -16
- package/dist/analysts/css-in-js/detectors/detector-css-in-js-best-practices.js +28 -30
- package/dist/analysts/css-in-js/scorers/scorer-css-in-js.js +79 -54
- package/dist/analysts/css-in-js/shared.js +7 -0
- package/dist/analysts/detectors/detector-architecture.js +91 -85
- package/dist/analysts/detectors/detector-bugs-ml.js +29 -27
- package/dist/analysts/detectors/detector-code-fragile.js +41 -36
- package/dist/analysts/detectors/detector-dependencies.js +5 -5
- package/dist/analysts/detectors/detector-duplications.js +26 -26
- package/dist/analysts/detectors/detector-monorepo.js +51 -45
- package/dist/analysts/detectors/detector-performance.js +24 -17
- package/dist/analysts/detectors/detector-recommendation-architecture.js +40 -36
- package/dist/analysts/detectors/detector-security.js +43 -42
- package/dist/analysts/detectors/detector-structure.js +31 -28
- package/dist/analysts/github-actions/analysts/analyst-github-actions.js +5 -8
- package/dist/analysts/github-actions/detectors/detector-workflow-accessibility.js +1 -9
- package/dist/analysts/github-actions/detectors/detector-workflow-structure.js +1 -4
- package/dist/analysts/github-actions/detectors/detector-workflow-trigger-unsafe.js +6 -5
- package/dist/analysts/gitlab-ci/analysts/analyst-gitlab-ci.js +2 -2
- package/dist/analysts/gitlab-ci/detectors/detector-gitlab-ci-structure.js +0 -3
- package/dist/analysts/go/analysts/analyst-best-practices.js +2 -2
- package/dist/analysts/go/analysts/analyst-concurrency.js +1 -1
- package/dist/analysts/go/analysts/analyst-security.js +1 -1
- package/dist/analysts/go/detectors/detector-conventions.js +1 -1
- package/dist/analysts/html/analysts/analyst-html.js +10 -12
- package/dist/analysts/html/detectors/detector-html.js +3 -3
- package/dist/analysts/js-ts/analysts/analyst-commands-cli.js +22 -22
- package/dist/analysts/js-ts/analysts/analyst-functions-long.js +5 -5
- package/dist/analysts/js-ts/analysts/analyst-patterns-usage.js +6 -2
- package/dist/analysts/js-ts/detectors/detector-anti-patterns-async.js +84 -9
- package/dist/analysts/js-ts/detectors/detector-constructions-syntactic.js +33 -10
- package/dist/analysts/js-ts/detectors/detector-context-smart.js +6 -6
- package/dist/analysts/js-ts/detectors/detector-leaks-memory.js +1 -1
- package/dist/analysts/js-ts/detectors/detector-types-unsafe.js +33 -2
- package/dist/analysts/php/analysts/analyst-php.js +3 -3
- package/dist/analysts/php/detectors/detector-php-best-practices.js +5 -5
- package/dist/analysts/php/detectors/detector-php-security.js +5 -5
- package/dist/analysts/php/scorers/scorer-php.js +2 -2
- package/dist/analysts/plugins/detector-documentation.js +6 -6
- package/dist/analysts/plugins/detector-markdown.js +7 -7
- package/dist/analysts/python/analysts/analyst-python.js +2 -5
- package/dist/analysts/react/analysts/analyst-react-hooks.js +66 -9
- package/dist/analysts/react/analysts/analyst-react.js +46 -2
- package/dist/analysts/react/corrections/correction-react.js +41 -1
- package/dist/analysts/react/detectors/detector-react-best-practices.js +5 -5
- package/dist/analysts/react/scorers/scorer-react.js +36 -1
- package/dist/analysts/registry/autodiscovery.js +2 -2
- package/dist/analysts/rust/analysts/analyst-best-practices.js +42 -3
- package/dist/analysts/rust/analysts/analyst-memory.js +2 -2
- package/dist/analysts/rust/analysts/analyst-rust.js +52 -5
- package/dist/analysts/rust/analysts/analyst-security.js +29 -1
- package/dist/analysts/scorers/scorer.js +5 -5
- package/dist/analysts/shell/analysts/analyst-shell.js +81 -5
- package/dist/analysts/shell/corrections/correction-shell.js +34 -1
- package/dist/analysts/shell/detectors/detector-shell-security.js +88 -0
- package/dist/analysts/shell/scorers/scorer-shell.js +58 -1
- package/dist/analysts/strategists/operator-structure.js +5 -7
- package/dist/analysts/strategists/suggestions-contextuais.js +5 -2
- package/dist/analysts/svg/analysts/analyst-svg.js +1 -0
- package/dist/analysts/tailwind/analysts/analyst-tailwind.js +79 -55
- package/dist/analysts/tailwind/corrections/correction-tailwind.js +108 -9
- package/dist/analysts/tailwind/detectors/detector-tailwind-best-practices.js +122 -7
- package/dist/analysts/tailwind/scorers/scorer-tailwind.js +62 -11
- package/dist/analysts/xml/analysts/analyst-xml.js +2 -2
- package/dist/app/index.js +1 -0
- package/dist/bin/cli-bootstrap.js +6 -4
- package/dist/bin/index.js +6 -4
- package/dist/caretakers/scoring.js +1 -0
- package/dist/cli/commands/command-analysts.js +4 -3
- package/dist/cli/commands/command-azure.js +1 -1
- package/dist/cli/commands/command-circleci.js +1 -1
- package/dist/cli/commands/command-compliance.js +1 -1
- package/dist/cli/commands/command-diagnose.js +10 -13
- package/dist/cli/commands/command-fix-types.js +9 -32
- package/dist/cli/commands/command-formatters.js +2 -2
- package/dist/cli/commands/command-github-actions.js +1 -1
- package/dist/cli/commands/command-gitlab-ci.js +1 -1
- package/dist/cli/commands/command-guardian.js +1 -1
- package/dist/cli/commands/command-imports.js +6 -6
- package/dist/cli/commands/command-jenkins.js +1 -1
- package/dist/cli/commands/command-licenses.js +1 -1
- package/dist/cli/commands/command-marketplace.js +7 -7
- package/dist/cli/commands/command-names.js +3 -8
- package/dist/cli/commands/command-perf.js +7 -9
- package/dist/cli/commands/command-plugins.js +4 -4
- package/dist/cli/diagnostic/auto-fix.js +2 -2
- package/dist/cli/diagnostic/exporters/json-exporter.js +1 -1
- package/dist/cli/diagnostic/exporters/sharded-exporter.js +0 -3
- package/dist/cli/diagnostic/exporters/svg-optimization-exporter.js +1 -1
- package/dist/cli/diagnostic/filters.js +9 -9
- package/dist/cli/diagnostic/handlers/archetype-handler.js +3 -6
- package/dist/cli/diagnostic/handlers/auto-fix-handler.js +4 -4
- package/dist/cli/diagnostic/handlers/guardian-handler.js +1 -1
- package/dist/cli/diagnostic/processing-diagnostic.js +31 -35
- package/dist/cli/diagnostic/processing-helpers.js +2 -2
- package/dist/cli/handlers/fix-types-exporter.js +1 -4
- package/dist/cli/helpers/display-frame.js +5 -5
- package/dist/core/config/auto/auto-fix-config.js +0 -6
- package/dist/core/config/chalk-safe.js +6 -6
- package/dist/core/config/config.js +11 -13
- package/dist/core/config/conventions.js +1 -1
- package/dist/core/config/security.js +2 -2
- package/dist/core/execution/analysis-cache.js +5 -8
- package/dist/core/execution/ast-cache.js +1 -3
- package/dist/core/execution/executor.js +8 -21
- package/dist/core/execution/inquisitor.js +24 -21
- package/dist/core/execution/results.js +1 -0
- package/dist/core/execution/scanner.js +4 -4
- package/dist/core/messages/en/analysts/analyst-github-actions-messages.js +1 -1
- package/dist/core/messages/en/analysts/analyst-patterns-usage-messages.js +1 -0
- package/dist/core/messages/en/analysts/analyst-shell-messages.js +15 -0
- package/dist/core/messages/en/analysts/detector-constructions-syntactic-messages.js +6 -0
- package/dist/core/messages/en/analysts/detector-js-ts-messages.js +27 -0
- package/dist/core/messages/en/analysts/detector-react-messages.js +8 -0
- package/dist/core/messages/en/analysts/detector-shell-messages.js +17 -0
- package/dist/core/messages/en/analysts/detector-tailwind-messages.js +3 -3
- package/dist/core/messages/en/analysts/report-circleci-messages.js +7 -7
- package/dist/core/messages/en/caretakers/caretaker-messages.js +1 -7
- package/dist/core/messages/en/core/plugin-messages.js +31 -3
- package/dist/core/messages/en/log/log.js +4 -4
- package/dist/core/messages/en/ui/filter-config.js +1 -5
- package/dist/core/messages/i18n-system.js +3 -3
- package/dist/core/messages/index.js +3 -3
- package/dist/core/messages/ja/analysts/analyst-github-actions-messages.js +1 -1
- package/dist/core/messages/ja/analysts/analyst-patterns-usage-messages.js +1 -0
- package/dist/core/messages/ja/analysts/analyst-shell-messages.js +15 -0
- package/dist/core/messages/ja/analysts/detector-constructions-syntactic-messages.js +6 -0
- package/dist/core/messages/ja/analysts/detector-js-ts-messages.js +27 -0
- package/dist/core/messages/ja/analysts/detector-react-messages.js +8 -0
- package/dist/core/messages/ja/analysts/detector-shell-messages.js +17 -0
- package/dist/core/messages/ja/analysts/detector-tailwind-messages.js +3 -3
- package/dist/core/messages/ja/analysts/report-circleci-messages.js +7 -7
- package/dist/core/messages/ja/caretakers/caretaker-messages.js +1 -7
- package/dist/core/messages/ja/core/plugin-messages.js +31 -3
- package/dist/core/messages/ja/log/log.js +4 -4
- package/dist/core/messages/ja/ui/filter-config.js +1 -1
- package/dist/core/messages/pt/analysts/analyst-github-actions-messages.js +1 -1
- package/dist/core/messages/pt/analysts/analyst-patterns-usage-messages.js +1 -0
- package/dist/core/messages/pt/analysts/analyst-shell-messages.js +15 -0
- package/dist/core/messages/pt/analysts/detector-constructions-syntactic-messages.js +6 -0
- package/dist/core/messages/pt/analysts/detector-js-ts-messages.js +27 -0
- package/dist/core/messages/pt/analysts/detector-react-messages.js +8 -0
- package/dist/core/messages/pt/analysts/detector-shell-messages.js +17 -0
- package/dist/core/messages/pt/analysts/detector-tailwind-messages.js +3 -3
- package/dist/core/messages/pt/analysts/report-circleci-messages.js +7 -7
- package/dist/core/messages/pt/caretakers/caretaker-messages.js +1 -7
- package/dist/core/messages/pt/core/plugin-messages.js +31 -3
- package/dist/core/messages/pt/log/log-engine.js +6 -6
- package/dist/core/messages/pt/log/log.js +3 -3
- package/dist/core/messages/pt/ui/filter-config.js +1 -1
- package/dist/core/messages/shared/icons.js +3 -3
- package/dist/core/messages/zh/analysts/analyst-github-actions-messages.js +1 -1
- package/dist/core/messages/zh/analysts/analyst-patterns-usage-messages.js +1 -0
- package/dist/core/messages/zh/analysts/analyst-shell-messages.js +15 -0
- package/dist/core/messages/zh/analysts/detector-constructions-syntactic-messages.js +6 -0
- package/dist/core/messages/zh/analysts/detector-js-ts-messages.js +27 -0
- package/dist/core/messages/zh/analysts/detector-react-messages.js +8 -0
- package/dist/core/messages/zh/analysts/detector-shell-messages.js +17 -0
- package/dist/core/messages/zh/analysts/detector-tailwind-messages.js +3 -3
- package/dist/core/messages/zh/analysts/report-circleci-messages.js +7 -7
- package/dist/core/messages/zh/caretakers/caretaker-messages.js +1 -7
- package/dist/core/messages/zh/core/plugin-messages.js +31 -3
- package/dist/core/messages/zh/log/log.js +3 -3
- package/dist/core/messages/zh/ui/filter-config.js +1 -5
- package/dist/core/parsing/filters.js +3 -3
- package/dist/core/parsing/langs/json.js +1 -1
- package/dist/core/parsing/langs/python.js +1 -1
- package/dist/core/parsing/langs/typescript.js +1 -1
- package/dist/core/parsing/parser.js +3 -4
- package/dist/core/parsing/utils.js +1 -0
- package/dist/core/registry/file-registry.js +3 -2
- package/dist/core/registry/paths.js +3 -3
- package/dist/core/reporting/default-reporter.js +2 -2
- package/dist/core/schema/version.js +6 -6
- package/dist/core/workers/worker-executor.js +3 -3
- package/dist/core/workers/worker-pool.js +0 -0
- package/dist/guardian/baseline.js +1 -1
- package/dist/guardian/gpg.js +3 -1
- package/dist/guardian/integrity.js +2 -7
- package/dist/guardian/records.js +7 -1
- package/dist/guardian/result.js +1 -1
- package/dist/guardian/sentinel.js +7 -1
- package/dist/guardian/watcher-hidden.js +6 -1
- package/dist/licenses/fs-utils.js +5 -1
- package/dist/licenses/generate-notices.js +1 -1
- package/dist/licenses/index.js +1 -0
- package/dist/licenses/normalizer.js +10 -9
- package/dist/node.loader.js +224 -4
- package/dist/reports/analise-async-patterns.js +1 -1
- package/dist/reports/filter-smart.js +2 -5
- package/dist/reports/generator-report.js +7 -7
- package/dist/reports/report-archetypes.js +10 -10
- package/dist/reports/report-caretaker-health.js +2 -1
- package/dist/reports/report-patterns-usage.js +1 -1
- package/dist/sdk/analyzer.js +1 -1
- package/dist/shared/data-processing/fragment-report.js +11 -11
- package/dist/shared/data-processing/json.js +1 -1
- package/dist/shared/data-processing/occurrences.js +3 -2
- package/dist/shared/formatters/code-min-formatter.js +1 -1
- package/dist/shared/formatters/engines/prettier.js +1 -1
- package/dist/shared/formatters/engines/stylelint.js +43 -40
- package/dist/shared/formatters/formatter.js +8 -8
- package/dist/shared/formatters/formatters/code.js +3 -3
- package/dist/shared/formatters/formatters/css.js +2 -10
- package/dist/shared/formatters/formatters/dotfiles.js +1 -1
- package/dist/shared/formatters/formatters/html.js +1 -5
- package/dist/shared/formatters/formatters/json.js +0 -3
- package/dist/shared/formatters/formatters/markdown.js +19 -24
- package/dist/shared/formatters/formatters/properties.js +2 -2
- package/dist/shared/formatters/formatters/shell.js +1 -4
- package/dist/shared/formatters/formatters/sql.js +0 -3
- package/dist/shared/formatters/formatters/xml.js +5 -7
- package/dist/shared/formatters/formatters/yaml.js +2 -7
- package/dist/shared/formatters/index.js +1 -1
- package/dist/shared/helpers/framework-detector.js +6 -2
- package/dist/shared/helpers/json.js +1 -1
- package/dist/shared/helpers/magic-constants-whitelist.js +1 -1
- package/dist/shared/helpers/masking.js +17 -17
- package/dist/shared/helpers/reader-report.js +0 -12
- package/dist/shared/helpers/structural-hash.js +3 -2
- package/dist/shared/helpers/structure.js +1 -1
- package/dist/shared/marketplace/registry.js +1 -1
- package/dist/shared/marketplace/remote.js +4 -4
- package/dist/shared/persistence/persistence.js +13 -6
- package/dist/shared/plugins/core-plugin.js +5 -1
- package/dist/shared/plugins/registry.js +3 -2
- package/dist/types/analysts/tailwind-correction.js +2 -0
- package/dist/types/caretakers/imports-barrel.js +1 -0
- package/dist/types/caretakers/index.js +4 -0
- package/dist/types/caretakers/map-reversion.js +1 -0
- package/dist/types/caretakers/pruning.js +1 -0
- package/dist/types/caretakers/scoring.js +1 -0
- package/dist/types/common/analysts.js +1 -1
- package/dist/types/core/config/filters.js +1 -0
- package/dist/types/core/config/index.js +2 -0
- package/dist/types/core/execution/ambiente.js +1 -0
- package/dist/types/core/execution/linguagens.js +1 -0
- package/dist/types/core/execution/parse-errors.js +1 -0
- package/dist/types/core/execution/results.js +1 -0
- package/dist/types/core/execution/structure-json.js +1 -0
- package/dist/types/core/index.js +1 -0
- package/dist/types/core/messages/index.js +1 -0
- package/dist/types/core/messages/log.js +1 -0
- package/dist/types/core/parsing/babel-narrow.js +1 -0
- package/dist/types/core/parsing/index.js +1 -0
- package/dist/types/core/runtime.js +1 -0
- package/dist/types/guardian/index.js +2 -0
- package/dist/types/guardian/integrity.js +7 -0
- package/dist/types/guardian/snapshot.js +1 -0
- package/dist/types/index.js +4 -4
- package/dist/types/licenses/index.js +2 -0
- package/dist/types/processing/display.js +16 -13
- package/dist/types/processing/filters.js +8 -11
- package/dist/types/reports/compliance.js +1 -0
- package/dist/types/reports/streaming.js +1 -0
- package/dist/types/scripts/index.js +1 -0
- package/dist/types/scripts/migrar-aliases.js +1 -0
- package/dist/types/sdk/index.js +1 -0
- package/dist/types/shared/index.js +2 -0
- package/dist/types/vulnerabilities/index.js +2 -0
- package/dist/vulnerabilities/scanner.js +3 -2
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|