@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
|
@@ -322,6 +322,47 @@ function detectarAsyncSemTryCatch(src, problemas) {
|
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
|
+
function detectarCookieInseguro(callee, args, path, problemas) {
|
|
326
|
+
if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier' && callee.property.name === 'cookie') {
|
|
327
|
+
const object = callee.object;
|
|
328
|
+
const isRequest = object.type === 'Identifier' && (object.name === 'req' || object.name === 'request');
|
|
329
|
+
if (!isRequest) {
|
|
330
|
+
const options = args[2];
|
|
331
|
+
let hasHttpOnly = false;
|
|
332
|
+
if (options.type === 'ObjectExpression') {
|
|
333
|
+
hasHttpOnly = options.properties.some((prop) => prop.type === 'ObjectProperty' && prop.key.type === 'Identifier' && prop.key.name === 'httpOnly' && (prop.value.type === 'BooleanLiteral' ? prop.value.value : false));
|
|
334
|
+
}
|
|
335
|
+
if (!hasHttpOnly) {
|
|
336
|
+
problemas.push({
|
|
337
|
+
tipo: 'insecure-cookie',
|
|
338
|
+
descricao: 'Cookie configurado sem a flag httpOnly',
|
|
339
|
+
severidade: 'media',
|
|
340
|
+
linha: path.node.loc?.start.line ?? 0,
|
|
341
|
+
sugestao: 'Sempre use httpOnly: true para evitar acesso via JavaScript (XSS)'
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function detectarSQLInjectionAST(callee, args, path, problemas) {
|
|
348
|
+
if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier' && ['query', 'execute', 'raw', 'queryRaw', 'executeRaw'].includes(callee.property.name)) {
|
|
349
|
+
const firstArg = args[0];
|
|
350
|
+
const isUnsafe = firstArg.type === 'TemplateLiteral' && firstArg.expressions.length > 0 || firstArg.type === 'BinaryExpression' && firstArg.operator === '+';
|
|
351
|
+
if (isUnsafe) {
|
|
352
|
+
const code = firstArg.type === 'TemplateLiteral' ? firstArg.quasis.map((delimitadorString) => delimitadorString.value.raw).join('') : '';
|
|
353
|
+
const temParametros = /['"`].*(\?|\$\d|:\w+).*['"`]/.test(code);
|
|
354
|
+
if (!temParametros) {
|
|
355
|
+
problemas.push({
|
|
356
|
+
tipo: 'sql-injection',
|
|
357
|
+
descricao: 'Possível SQL Injection detectada via AST (concatenação no primeiro argumento)',
|
|
358
|
+
severidade: 'critica',
|
|
359
|
+
linha: path.node.loc?.start.line ?? 0,
|
|
360
|
+
sugestao: 'Use queries parametrizadas ao invés de concatenar strings ou template literals com variáveis'
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
325
366
|
function detectarProblemasAST(ast, problemas) {
|
|
326
367
|
try {
|
|
327
368
|
traverse(ast.node, {
|
|
@@ -351,48 +392,8 @@ function detectarProblemasAST(ast, problemas) {
|
|
|
351
392
|
sugestao: 'Use função ao invés de string'
|
|
352
393
|
});
|
|
353
394
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
const object = callee.object;
|
|
357
|
-
const isRequest = object.type === 'Identifier' && (object.name === 'req' || object.name === 'request');
|
|
358
|
-
if (!isRequest) {
|
|
359
|
-
// Heurística: assume-se que se não é req/request, é provável ser a resposta (res)
|
|
360
|
-
const options = args[2];
|
|
361
|
-
let hasHttpOnly = false;
|
|
362
|
-
if (options.type === 'ObjectExpression') {
|
|
363
|
-
hasHttpOnly = options.properties.some(prop => prop.type === 'ObjectProperty' && prop.key.type === 'Identifier' && prop.key.name === 'httpOnly' && (prop.value.type === 'BooleanLiteral' ? prop.value.value : false));
|
|
364
|
-
}
|
|
365
|
-
if (!hasHttpOnly) {
|
|
366
|
-
problemas.push({
|
|
367
|
-
tipo: 'insecure-cookie',
|
|
368
|
-
descricao: 'Cookie configurado sem a flag httpOnly',
|
|
369
|
-
severidade: 'media',
|
|
370
|
-
linha: path.node.loc?.start.line ?? 0,
|
|
371
|
-
sugestao: 'Sempre use httpOnly: true para evitar acesso via JavaScript (XSS)'
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
// 3. SQL Injection (básico via AST)
|
|
377
|
-
if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier' && ['query', 'execute', 'raw', 'queryRaw', 'executeRaw'].includes(callee.property.name)) {
|
|
378
|
-
const firstArg = args[0];
|
|
379
|
-
// Se o primeiro argumento é uma TemplateLiteral com expressões ou uma BinaryExpression (+)
|
|
380
|
-
const isUnsafe = firstArg && (firstArg.type === 'TemplateLiteral' && firstArg.expressions.length > 0 || firstArg.type === 'BinaryExpression' && firstArg.operator === '+');
|
|
381
|
-
if (isUnsafe) {
|
|
382
|
-
// Verificar se parece usar parâmetros de qualquer forma
|
|
383
|
-
const code = firstArg.type === 'TemplateLiteral' ? firstArg.quasis.map(delimitadorString => delimitadorString.value.raw).join('') : '';
|
|
384
|
-
const temParametros = /['"`].*(\?|\$\d|:\w+).*['"`]/.test(code);
|
|
385
|
-
if (!temParametros) {
|
|
386
|
-
problemas.push({
|
|
387
|
-
tipo: 'sql-injection',
|
|
388
|
-
descricao: 'Possível SQL Injection detectada via AST (concatenação no primeiro argumento)',
|
|
389
|
-
severidade: 'critica',
|
|
390
|
-
linha: path.node.loc?.start.line ?? 0,
|
|
391
|
-
sugestao: 'Use queries parametrizadas ao invés de concatenar strings ou template literals com variáveis'
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
395
|
+
detectarCookieInseguro(callee, args, path, problemas);
|
|
396
|
+
detectarSQLInjectionAST(callee, args, path, problemas);
|
|
396
397
|
}
|
|
397
398
|
});
|
|
398
399
|
}
|
|
@@ -4,6 +4,32 @@ import { config, getAllExpectedDirNames, getDirVariantPattern, getNameConvention
|
|
|
4
4
|
import { messages } from '../../core/messages/index.js';
|
|
5
5
|
import { grafoDependencias } from './detector-dependencies.js';
|
|
6
6
|
export const sinaisDetectados = {};
|
|
7
|
+
function verificarConvencoesNomesDiretorios(ocorrencias, caminhosNorm) {
|
|
8
|
+
const expectedDirs = getAllExpectedDirNames();
|
|
9
|
+
if (expectedDirs.length === 0) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const topLevelDirs = new Set(caminhosNorm.filter(caminho => caminho.includes('/') && !caminho.startsWith('node_modules/') && !caminho.startsWith('dist/')).map(caminho => caminho.split('/')[0]));
|
|
13
|
+
const configuredNames = new Set(Object.values(getNameConventions()).filter((valor) => typeof valor === 'string' && valor.trim().length > 0).map(valor => valor.trim().toLowerCase()));
|
|
14
|
+
for (const { key, expected } of expectedDirs) {
|
|
15
|
+
const expectedLower = expected.toLowerCase();
|
|
16
|
+
const match = [...topLevelDirs].find(diretorio => diretorio.toLowerCase() === expectedLower);
|
|
17
|
+
if (!match) {
|
|
18
|
+
const variantPattern = getDirVariantPattern(key);
|
|
19
|
+
const foundVariant = [...topLevelDirs].find(diretorio => variantPattern.test(diretorio));
|
|
20
|
+
if (foundVariant && !configuredNames.has(foundVariant.toLowerCase())) {
|
|
21
|
+
ocorrencias.push({
|
|
22
|
+
tipo: 'name-convention',
|
|
23
|
+
nivel: 'aviso',
|
|
24
|
+
mensagem: messages.DetectorEstruturaMensagens.nomeDiretorioNaoConforme(foundVariant, expected),
|
|
25
|
+
origem: 'detector-estrutura',
|
|
26
|
+
relPath: `[raiz do projeto]`,
|
|
27
|
+
linha: 0
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
7
33
|
/**
|
|
8
34
|
* Analisa a estrutura do projeto e detecta padrões como monorepo, fullstack, mistura de src/packages, etc.
|
|
9
35
|
* Retorna ocorrências para cada sinal relevante encontrado.
|
|
@@ -24,8 +50,8 @@ export const detectorEstrutura = {
|
|
|
24
50
|
// Conjuntos para buscas O(1) e deduplicação implícita
|
|
25
51
|
const setCaminhos = new Set(caminhos);
|
|
26
52
|
// Detecta contexto geral do projeto para análise inteligente
|
|
27
|
-
const arquivoPrincipal = caminhos
|
|
28
|
-
const conteudoPrincipal = contexto.arquivos
|
|
53
|
+
const arquivoPrincipal = caminhos?.find(caminho => /package\.json$/.test(caminho)) ?? caminhos[0];
|
|
54
|
+
const conteudoPrincipal = contexto.arquivos?.find(arquivo => arquivo.relPath === arquivoPrincipal)?.content ?? '';
|
|
29
55
|
const contextoProjeto = detectarContextoProjeto({
|
|
30
56
|
arquivo: arquivoPrincipal,
|
|
31
57
|
conteudo: conteudoPrincipal,
|
|
@@ -160,8 +186,8 @@ export const detectorEstrutura = {
|
|
|
160
186
|
// Múltiplos entrypoints
|
|
161
187
|
// Lista de entrypoints potencialmente grande em repositórios com dependências; filtra ignorados
|
|
162
188
|
const entrypointsAll = caminhosNorm.filter(caminho => /(^|[\\/])(cli|index|main)\.(ts|js)$/.test(caminho));
|
|
163
|
-
const ignoredDyn = config.INCLUDE_EXCLUDE_RULES.globalExcludeGlob
|
|
164
|
-
const entrypoints = entrypointsAll.filter(caminho => !micromatch.isMatch(caminho, ignoredDyn
|
|
189
|
+
const ignoredDyn = config.INCLUDE_EXCLUDE_RULES.globalExcludeGlob;
|
|
190
|
+
const entrypoints = entrypointsAll.filter(caminho => !micromatch.isMatch(caminho, ignoredDyn)).sort();
|
|
165
191
|
if (entrypoints.length > 1) {
|
|
166
192
|
const MAX_LIST = 20;
|
|
167
193
|
if (entrypoints.length > MAX_LIST) {
|
|
@@ -220,30 +246,7 @@ export const detectorEstrutura = {
|
|
|
220
246
|
linha: 0
|
|
221
247
|
});
|
|
222
248
|
}
|
|
223
|
-
|
|
224
|
-
const expectedDirs = getAllExpectedDirNames();
|
|
225
|
-
if (expectedDirs.length > 0) {
|
|
226
|
-
const topLevelDirs = new Set(caminhosNorm.filter(caminho => caminho.includes('/') && !caminho.startsWith('node_modules/') && !caminho.startsWith('dist/')).map(caminho => caminho.split('/')[0]));
|
|
227
|
-
const configuredNames = new Set(Object.values(getNameConventions()).filter((valor) => typeof valor === 'string' && valor.trim().length > 0).map(valor => valor.trim().toLowerCase()));
|
|
228
|
-
for (const { key, expected } of expectedDirs) {
|
|
229
|
-
const expectedLower = expected.toLowerCase();
|
|
230
|
-
const match = [...topLevelDirs].find(diretorio => diretorio.toLowerCase() === expectedLower);
|
|
231
|
-
if (!match) {
|
|
232
|
-
const variantPattern = getDirVariantPattern(key);
|
|
233
|
-
const foundVariant = [...topLevelDirs].find(diretorio => variantPattern.test(diretorio));
|
|
234
|
-
if (foundVariant && !configuredNames.has(foundVariant.toLowerCase())) {
|
|
235
|
-
ocorrencias.push({
|
|
236
|
-
tipo: 'name-convention',
|
|
237
|
-
nivel: 'aviso',
|
|
238
|
-
mensagem: messages.DetectorEstruturaMensagens.nomeDiretorioNaoConforme(foundVariant, expected),
|
|
239
|
-
origem: 'detector-estrutura',
|
|
240
|
-
relPath: `[raiz do projeto]`,
|
|
241
|
-
linha: 0
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
249
|
+
verificarConvencoesNomesDiretorios(ocorrencias, caminhosNorm);
|
|
247
250
|
return Array.isArray(ocorrencias) ? ocorrencias : [];
|
|
248
251
|
}
|
|
249
252
|
};
|
|
@@ -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({
|
|
@@ -25,7 +25,7 @@ function collectBestPractices(src, relPath) {
|
|
|
25
25
|
// Excessive function length
|
|
26
26
|
const funcMatches = src.matchAll(/(?:^|\n)\s*func\s+\w+.*?\{/gs);
|
|
27
27
|
for (const funcMatch of funcMatches) {
|
|
28
|
-
const startIdx =
|
|
28
|
+
const startIdx = funcMatch.index + funcMatch[0].length;
|
|
29
29
|
let braceCount = 1;
|
|
30
30
|
let endIdx = startIdx;
|
|
31
31
|
for (let i = startIdx; i < src.length && braceCount > 0; i++) {
|
|
@@ -40,7 +40,7 @@ function collectBestPractices(src, relPath) {
|
|
|
40
40
|
const funcBody = src.slice(startIdx, endIdx);
|
|
41
41
|
const funcLines = funcBody.split('\n').length;
|
|
42
42
|
if (funcLines > 60) {
|
|
43
|
-
const line = lineOf(funcMatch.index
|
|
43
|
+
const line = lineOf(funcMatch.index);
|
|
44
44
|
ocorrencias.push(warn(messages.GoMensagens.longFunction, relPath, line));
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -36,7 +36,7 @@ function collectConcurrencyIssues(src, relPath) {
|
|
|
36
36
|
}
|
|
37
37
|
// Select without default case (potential blocking)
|
|
38
38
|
for (const match of scan.matchAll(/\bselect\s*\{/g)) {
|
|
39
|
-
const blockStart = match.index
|
|
39
|
+
const blockStart = match.index;
|
|
40
40
|
const block = src.slice(blockStart, blockStart + 500);
|
|
41
41
|
if (!/\bdefault\s*:/m.test(block)) {
|
|
42
42
|
const line = lineOf(blockStart);
|
|
@@ -32,7 +32,7 @@ function collectSecurityIssues(src, relPath) {
|
|
|
32
32
|
}
|
|
33
33
|
// Printf without format string
|
|
34
34
|
for (const match of scan.matchAll(/\b(fmt\.(?:Print|Sprint|Fprint))\s*\(\s*([^)]+)\)/g)) {
|
|
35
|
-
const args = match[2]
|
|
35
|
+
const args = match[2];
|
|
36
36
|
if (/["'`]/.test(args.trim())) {
|
|
37
37
|
continue;
|
|
38
38
|
}
|
|
@@ -56,7 +56,7 @@ function detectConventions(src, relPath) {
|
|
|
56
56
|
if (impMatch) {
|
|
57
57
|
const path = impMatch[1];
|
|
58
58
|
if (/\.$/i.test(path)) {
|
|
59
|
-
const absIdx =
|
|
59
|
+
const absIdx = match.index + block.indexOf(impLine);
|
|
60
60
|
const line = lineOf(absIdx);
|
|
61
61
|
ocorrencias.push({
|
|
62
62
|
relPath,
|
|
@@ -10,8 +10,8 @@ function warn(message, relPath, line) {
|
|
|
10
10
|
}
|
|
11
11
|
function isElement(node) { return node.type === ElementType.Tag; }
|
|
12
12
|
function getAttr(el, name) {
|
|
13
|
-
const attrs = el.attribs
|
|
14
|
-
const valor = attrs[name]
|
|
13
|
+
const attrs = el.attribs;
|
|
14
|
+
const valor = attrs[name] || attrs[name.toLowerCase()] || '';
|
|
15
15
|
return typeof valor === 'string' ? valor : '';
|
|
16
16
|
}
|
|
17
17
|
function walk(node, visit) {
|
|
@@ -32,7 +32,7 @@ function checkDoctype(node) {
|
|
|
32
32
|
return /^!doctype\s+html\b/i.test(data);
|
|
33
33
|
}
|
|
34
34
|
function validarButton(node, line, relPath, ocorrencias) {
|
|
35
|
-
const text = node.children.some(elem => elem.type === ElementType.Text && (
|
|
35
|
+
const text = node.children.some(elem => elem.type === ElementType.Text && (elem).data.trim().length > 0);
|
|
36
36
|
if (!text && !getAttr(node, 'aria-label') && !getAttr(node, 'title')) {
|
|
37
37
|
ocorrencias.push(warn(messages.HtmlMensagens.buttonWithoutText, relPath, line));
|
|
38
38
|
}
|
|
@@ -53,7 +53,7 @@ function validarLink(node, line, relPath, ocorrencias) {
|
|
|
53
53
|
if (target === '_blank' && !/(noopener|noreferrer)/i.test(getAttr(node, 'rel'))) {
|
|
54
54
|
ocorrencias.push(warn(messages.HtmlMensagens.linkTargetBlank, relPath, line));
|
|
55
55
|
}
|
|
56
|
-
const attrs = node.attribs
|
|
56
|
+
const attrs = node.attribs;
|
|
57
57
|
const hasHref = Object.prototype.hasOwnProperty.call(attrs, 'href');
|
|
58
58
|
const href = getAttr(node, 'href');
|
|
59
59
|
if (hasHref && href !== '' && href !== '#') {
|
|
@@ -71,7 +71,7 @@ function validarImg(node, line, relPath, ocorrencias, isTemplate) {
|
|
|
71
71
|
const ariaHidden = getAttr(node, 'aria-hidden').toLowerCase() === 'true';
|
|
72
72
|
const role = getAttr(node, 'role').toLowerCase();
|
|
73
73
|
const decorative = ariaHidden || ariaLabel || role === 'presentation' || role === 'none';
|
|
74
|
-
const attrs = node.attribs
|
|
74
|
+
const attrs = node.attribs;
|
|
75
75
|
const dataAttr = Object.keys(attrs).some(chave => /^(data-)?(decorative|icon|symbol)$/i.test(chave));
|
|
76
76
|
if (!alt && !decorative && !dataAttr && !/\.svg(\?|#|$)/i.test(getAttr(node, 'src'))) {
|
|
77
77
|
ocorrencias.push(warn(messages.HtmlMensagens.imgWithoutAlt, relPath, line));
|
|
@@ -87,7 +87,7 @@ function validarForm(node, line, relPath, ocorrencias) {
|
|
|
87
87
|
if (!getAttr(node, 'method')) {
|
|
88
88
|
ocorrencias.push(warn(messages.HtmlMensagens.formWithoutMethod, relPath, line));
|
|
89
89
|
}
|
|
90
|
-
const attrs = node.attribs
|
|
90
|
+
const attrs = node.attribs;
|
|
91
91
|
if (!getAttr(node, 'action') && !Object.keys(attrs).some(chave => chave.toLowerCase().startsWith('data-'))) {
|
|
92
92
|
ocorrencias.push(warn(messages.HtmlMensagens.formWithoutAction, relPath, line));
|
|
93
93
|
}
|
|
@@ -108,7 +108,7 @@ function validarInput(node, line, relPath, ocorrencias) {
|
|
|
108
108
|
}
|
|
109
109
|
function validarScript(node, line, relPath, ocorrencias, isTemplate) {
|
|
110
110
|
const srcAttr = getAttr(node, 'src');
|
|
111
|
-
const children = node.children
|
|
111
|
+
const children = node.children;
|
|
112
112
|
const text = children.filter((elem) => elem.type === ElementType.Text).map(elem => ((elem).data || '')).join('');
|
|
113
113
|
if (!srcAttr && text.trim().length > 0) {
|
|
114
114
|
ocorrencias.push(warn(messages.HtmlMensagens.inlineScript, relPath, line));
|
|
@@ -121,7 +121,7 @@ function validarScript(node, line, relPath, ocorrencias, isTemplate) {
|
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
function validarStyle(node, line, relPath, ocorrencias) {
|
|
124
|
-
const children = node.children
|
|
124
|
+
const children = node.children;
|
|
125
125
|
const text = children.filter((elem) => elem.type === ElementType.Text).map(elem => ((elem).data || '')).join('');
|
|
126
126
|
if (text.trim().length > 0) {
|
|
127
127
|
ocorrencias.push(warn(messages.HtmlMensagens.inlineStyle, relPath, line));
|
|
@@ -140,10 +140,8 @@ const validadores = {
|
|
|
140
140
|
};
|
|
141
141
|
function validateNode(node, tag, line, relPath, ocorrencias, isTemplate) {
|
|
142
142
|
const validador = validadores[tag];
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
const attrs = node.attribs || {};
|
|
143
|
+
validador(node, line, relPath, ocorrencias, isTemplate);
|
|
144
|
+
const attrs = node.attribs;
|
|
147
145
|
if (Object.keys(attrs).some(attr => INLINE_EVENTS.has(attr.toLowerCase()))) {
|
|
148
146
|
ocorrencias.push(warn(messages.HtmlMensagens.inlineHandler, relPath, line));
|
|
149
147
|
}
|
|
@@ -171,7 +171,7 @@ function detectSeo(src, relPath) {
|
|
|
171
171
|
const targetId = m[1].slice(1);
|
|
172
172
|
if (!ids.has(targetId)) {
|
|
173
173
|
const _lineLookup = splitLines(src);
|
|
174
|
-
const idx = m.index
|
|
174
|
+
const idx = m.index;
|
|
175
175
|
const lineNum = src.slice(0, idx).split('\n').length;
|
|
176
176
|
results.push({ type: 'html-broken-anchor', message: M.brokenAnchor, line: lineNum });
|
|
177
177
|
}
|
|
@@ -182,7 +182,7 @@ function detectSeo(src, relPath) {
|
|
|
182
182
|
for (const m of headingMatches) {
|
|
183
183
|
const level = parseInt(m[1], 10);
|
|
184
184
|
if (level > lastLevel + 1 && lastLevel > 0) {
|
|
185
|
-
const lineNum = src.slice(0, m.index
|
|
185
|
+
const lineNum = src.slice(0, m.index).split('\n').length;
|
|
186
186
|
results.push({ type: 'html-heading-hierarchy-skip', message: M.headingHierarchySkip, line: lineNum });
|
|
187
187
|
}
|
|
188
188
|
lastLevel = level;
|
|
@@ -310,7 +310,7 @@ function detectSemanticHtml(src, relPath) {
|
|
|
310
310
|
const prev = ids.get(idValue) ?? 0;
|
|
311
311
|
ids.set(idValue, prev + 1);
|
|
312
312
|
if (prev > 0) {
|
|
313
|
-
const lineNum = src.slice(0, m.index
|
|
313
|
+
const lineNum = src.slice(0, m.index).split('\n').length;
|
|
314
314
|
results.push({ type: 'html-duplicate-id-semantic', message: M.duplicateId, line: lineNum });
|
|
315
315
|
}
|
|
316
316
|
}
|