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