@sun-asterisk/sunlint 1.3.43 → 1.3.44
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/dart_analyzer/README.md +226 -0
- package/dart_analyzer/analysis_options.yaml +66 -0
- package/dart_analyzer/bin/sunlint-dart-macos +0 -0
- package/dart_analyzer/bin/sunlint_dart_analyzer.dart +124 -0
- package/dart_analyzer/lib/analyzer_service.dart +625 -0
- package/dart_analyzer/lib/json_rpc_server.dart +275 -0
- package/dart_analyzer/lib/models/rule.dart +67 -0
- package/dart_analyzer/lib/models/symbol_table.dart +607 -0
- package/dart_analyzer/lib/models/violation.dart +69 -0
- package/dart_analyzer/lib/rules/base_analyzer.dart +52 -0
- package/dart_analyzer/lib/rules/common/C002_no_duplicate_code.dart +344 -0
- package/dart_analyzer/lib/rules/common/C003_no_vague_abbreviations.dart +318 -0
- package/dart_analyzer/lib/rules/common/C006_function_naming.dart +219 -0
- package/dart_analyzer/lib/rules/common/C008_variable_declaration_locality.dart +205 -0
- package/dart_analyzer/lib/rules/common/C010_limit_block_nesting.dart +162 -0
- package/dart_analyzer/lib/rules/common/C012_command_query_separation.dart +214 -0
- package/dart_analyzer/lib/rules/common/C013_no_dead_code.dart +225 -0
- package/dart_analyzer/lib/rules/common/C014_dependency_injection.dart +249 -0
- package/dart_analyzer/lib/rules/common/C017_constructor_logic.dart +158 -0
- package/dart_analyzer/lib/rules/common/C018_no_throw_generic_error.dart +141 -0
- package/dart_analyzer/lib/rules/common/C019_log_level_usage.dart +165 -0
- package/dart_analyzer/lib/rules/common/C020_unused_imports.dart +128 -0
- package/dart_analyzer/lib/rules/common/C021_import_organization.dart +86 -0
- package/dart_analyzer/lib/rules/common/C023_no_duplicate_variable.dart +112 -0
- package/dart_analyzer/lib/rules/common/C024_no_scatter_hardcoded_constants.dart +79 -0
- package/dart_analyzer/lib/rules/common/C029_catch_block_logging.dart +81 -0
- package/dart_analyzer/lib/rules/common/C030_use_custom_error_classes.dart +77 -0
- package/dart_analyzer/lib/rules/common/C031_validation_separation.dart +90 -0
- package/dart_analyzer/lib/rules/common/C033_separate_service_repository.dart +80 -0
- package/dart_analyzer/lib/rules/common/C035_error_logging_context.dart +148 -0
- package/dart_analyzer/lib/rules/common/C040_centralized_validation.dart +84 -0
- package/dart_analyzer/lib/rules/common/C041_no_sensitive_hardcode.dart +103 -0
- package/dart_analyzer/lib/rules/common/C042_boolean_name_prefix.dart +105 -0
- package/dart_analyzer/lib/rules/common/C043_no_console_or_print.dart +101 -0
- package/dart_analyzer/lib/rules/common/C047_no_duplicate_retry_logic.dart +94 -0
- package/dart_analyzer/lib/rules/common/C048_no_bypass_architectural_layers.dart +132 -0
- package/dart_analyzer/lib/rules/common/C052_parsing_or_data_transformation.dart +95 -0
- package/dart_analyzer/lib/rules/common/C060_no_override_superclass.dart +81 -0
- package/dart_analyzer/lib/rules/common/C065_one_behavior_per_test.dart +83 -0
- package/dart_analyzer/lib/rules/common/C067_no_hardcoded_config.dart +89 -0
- package/dart_analyzer/lib/rules/common/C070_no_real_time_tests.dart +99 -0
- package/dart_analyzer/lib/rules/common/C072_single_test_behavior.dart +78 -0
- package/dart_analyzer/lib/rules/common/C073_validate_required_config_on_startup.dart +82 -0
- package/dart_analyzer/lib/rules/common/C075_explicit_return_types.dart +85 -0
- package/dart_analyzer/lib/rules/common/C076_explicit_function_types.dart +104 -0
- package/dart_analyzer/lib/rules/dart/D001_recommended_lint_rules.dart +309 -0
- package/dart_analyzer/lib/rules/dart/D002_dispose_resources.dart +338 -0
- package/dart_analyzer/lib/rules/dart/D003_prefer_widgets_over_methods.dart +273 -0
- package/dart_analyzer/lib/rules/dart/D004_avoid_shrinkwrap_listview.dart +154 -0
- package/dart_analyzer/lib/rules/dart/D005_limit_widget_nesting.dart +265 -0
- package/dart_analyzer/lib/rules/dart/D006_prefer_extracting_large_callbacks.dart +135 -0
- package/dart_analyzer/lib/rules/dart/D007_prefer_init_first_dispose_last.dart +150 -0
- package/dart_analyzer/lib/rules/dart/D008_avoid_long_functions.dart +394 -0
- package/dart_analyzer/lib/rules/dart/D009_limit_function_parameters.dart +179 -0
- package/dart_analyzer/lib/rules/dart/D010_limit_cyclomatic_complexity.dart +257 -0
- package/dart_analyzer/lib/rules/dart/D011_prefer_named_parameters.dart +152 -0
- package/dart_analyzer/lib/rules/dart/D012_prefer_named_boolean_parameters.dart +156 -0
- package/dart_analyzer/lib/rules/dart/D013_single_public_class.dart +246 -0
- package/dart_analyzer/lib/rules/dart/D014_unsafe_collection_access.dart +202 -0
- package/dart_analyzer/lib/rules/dart/D015_copywith_all_parameters.dart +125 -0
- package/dart_analyzer/lib/rules/dart/D016_project_should_have_tests.dart +134 -0
- package/dart_analyzer/lib/rules/dart/D017_pubspec_dependencies_review.dart +187 -0
- package/dart_analyzer/lib/rules/dart/D018_remove_commented_code.dart +196 -0
- package/dart_analyzer/lib/rules/dart/D019_avoid_single_child_multi_child_widget.dart +161 -0
- package/dart_analyzer/lib/rules/dart/D020_limit_if_else_branches.dart +125 -0
- package/dart_analyzer/lib/rules/dart/D021_avoid_negated_boolean_checks.dart +227 -0
- package/dart_analyzer/lib/rules/dart/D022_use_setstate_correctly.dart +269 -0
- package/dart_analyzer/lib/rules/dart/D023_avoid_unnecessary_method_overrides.dart +191 -0
- package/dart_analyzer/lib/rules/dart/D024_avoid_unnecessary_stateful_widget.dart +194 -0
- package/dart_analyzer/lib/rules/dart/D025_avoid_nested_conditional_expressions.dart +90 -0
- package/dart_analyzer/lib/rules/security/S001_backend_auth_communications.dart +155 -0
- package/dart_analyzer/lib/rules/security/S002_os_command_injection.dart +159 -0
- package/dart_analyzer/lib/rules/security/S003_open_redirect_protection.dart +208 -0
- package/dart_analyzer/lib/rules/security/S004_sensitive_data_logging.dart +391 -0
- package/dart_analyzer/lib/rules/security/S005_trusted_service_authorization.dart +182 -0
- package/dart_analyzer/lib/rules/security/S006_no_default_credentials.dart +208 -0
- package/dart_analyzer/lib/rules/security/S007_output_encoding.dart +224 -0
- package/dart_analyzer/lib/rules/security/S008_svg_content_sanitization.dart +211 -0
- package/dart_analyzer/lib/rules/security/S009_no_insecure_encryption.dart +160 -0
- package/dart_analyzer/lib/rules/security/S010_use_csprng.dart +184 -0
- package/dart_analyzer/lib/rules/security/S011_ech_tls_config.dart +175 -0
- package/dart_analyzer/lib/rules/security/S012_hardcoded_secrets.dart +255 -0
- package/dart_analyzer/lib/rules/security/S013_tls_enforcement.dart +148 -0
- package/dart_analyzer/lib/rules/security/S014_tls_version_enforcement.dart +117 -0
- package/dart_analyzer/lib/rules/security/S015_insecure_tls_certificate.dart +315 -0
- package/dart_analyzer/lib/rules/security/S016_no_sensitive_querystring.dart +244 -0
- package/dart_analyzer/lib/rules/security/S017_use_parameterized_queries.dart +191 -0
- package/dart_analyzer/lib/rules/security/S018_no_sensitive_browser_storage.dart +175 -0
- package/dart_analyzer/lib/rules/security/S019_smtp_injection_protection.dart +166 -0
- package/dart_analyzer/lib/rules/security/S020_no_eval_dynamic_code.dart +149 -0
- package/dart_analyzer/lib/rules/security/S021_referrer_policy.dart +146 -0
- package/dart_analyzer/lib/rules/security/S022_escape_output_context.dart +111 -0
- package/dart_analyzer/lib/rules/security/S023_no_json_injection.dart +550 -0
- package/dart_analyzer/lib/rules/security/S024_xpath_xxe_protection.dart +299 -0
- package/dart_analyzer/lib/rules/security/S025_server_side_validation.dart +140 -0
- package/dart_analyzer/lib/rules/security/S026_tls_all_connections.dart +196 -0
- package/dart_analyzer/lib/rules/security/S027_mtls_certificate_validation.dart +195 -0
- package/dart_analyzer/lib/rules/security/S028_file_upload_size_limits.dart +186 -0
- package/dart_analyzer/lib/rules/security/S029_csrf_protection.dart +171 -0
- package/dart_analyzer/lib/rules/security/S030_directory_browsing_protection.dart +144 -0
- package/dart_analyzer/lib/rules/security/S031_secure_session_cookies.dart +118 -0
- package/dart_analyzer/lib/rules/security/S032_httponly_session_cookies.dart +114 -0
- package/dart_analyzer/lib/rules/security/S033_samesite_session_cookies.dart +120 -0
- package/dart_analyzer/lib/rules/security/S034_host_prefix_session_cookies.dart +160 -0
- package/dart_analyzer/lib/rules/security/S035_separate_app_hostnames.dart +117 -0
- package/dart_analyzer/lib/rules/security/S036_lfi_rfi_protection.dart +188 -0
- package/dart_analyzer/lib/rules/security/S037_cache_headers.dart +113 -0
- package/dart_analyzer/lib/rules/security/S038_no_version_headers.dart +114 -0
- package/dart_analyzer/lib/rules/security/S039_tls_certificate_validation.dart +131 -0
- package/dart_analyzer/lib/rules/security/S040_session_fixation_protection.dart +155 -0
- package/dart_analyzer/lib/rules/security/S041_session_token_invalidation.dart +201 -0
- package/dart_analyzer/lib/rules/security/S042_require_re_authentication_for_long_lived.dart +158 -0
- package/dart_analyzer/lib/rules/security/S043_password_changes_invalidate_all_sessions.dart +88 -0
- package/dart_analyzer/lib/rules/security/S044_re_authentication_required.dart +119 -0
- package/dart_analyzer/lib/rules/security/S045_brute_force_protection.dart +253 -0
- package/dart_analyzer/lib/rules/security/S046_jwt_algorithm_allowlist.dart +113 -0
- package/dart_analyzer/lib/rules/security/S047_oauth_pkce_protection.dart +124 -0
- package/dart_analyzer/lib/rules/security/S048_oauth_redirect_uri_validation.dart +134 -0
- package/dart_analyzer/lib/rules/security/S049_short_validity_tokens.dart +145 -0
- package/dart_analyzer/lib/rules/security/S050_reference_tokens_entropy.dart +234 -0
- package/dart_analyzer/lib/rules/security/S051_password_length_policy.dart +171 -0
- package/dart_analyzer/lib/rules/security/S052_weak_otp_entropy.dart +107 -0
- package/dart_analyzer/lib/rules/security/S053_generic_error_messages.dart +159 -0
- package/dart_analyzer/lib/rules/security/S054_no_default_accounts.dart +141 -0
- package/dart_analyzer/lib/rules/security/S055_content_type_validation.dart +324 -0
- package/dart_analyzer/lib/rules/security/S056_log_injection_protection.dart +119 -0
- package/dart_analyzer/lib/rules/security/S057_utc_logging.dart +114 -0
- package/dart_analyzer/lib/rules/security/S058_no_ssrf.dart +175 -0
- package/dart_analyzer/lib/rules/security/S059_disable_debug_mode.dart +172 -0
- package/dart_analyzer/lib/rules/security/S060_password_minimum_length.dart +170 -0
- package/dart_analyzer/lib/symbol_table_extractor.dart +510 -0
- package/dart_analyzer/lib/utils/common_utils.dart +26 -0
- package/dart_analyzer/pubspec.lock +557 -0
- package/dart_analyzer/pubspec.yaml +39 -0
- package/dart_analyzer/test/fixtures/complex_code.dart +95 -0
- package/docs/GENERATED_FILE_HANDLING_SUMMARY.md +2 -2
- package/package.json +3 -2
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import 'package:analyzer/dart/ast/ast.dart';
|
|
2
|
+
import 'package:analyzer/dart/ast/visitor.dart';
|
|
3
|
+
import 'package:analyzer/source/line_info.dart';
|
|
4
|
+
|
|
5
|
+
import '../../models/rule.dart';
|
|
6
|
+
import '../../models/violation.dart';
|
|
7
|
+
import '../base_analyzer.dart';
|
|
8
|
+
|
|
9
|
+
/// S021: Set Referrer-Policy to prevent sensitive data leakage
|
|
10
|
+
/// Detect missing or weak Referrer-Policy headers
|
|
11
|
+
class S021ReferrerPolicyAnalyzer extends BaseAnalyzer {
|
|
12
|
+
@override
|
|
13
|
+
String get ruleId => 'S021';
|
|
14
|
+
|
|
15
|
+
// HTTP response/header patterns
|
|
16
|
+
static const _headerPatterns = [
|
|
17
|
+
'setheader',
|
|
18
|
+
'addheader',
|
|
19
|
+
'headers',
|
|
20
|
+
'response.',
|
|
21
|
+
'httpresponse',
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
// Weak referrer policies
|
|
25
|
+
static const _weakPolicies = [
|
|
26
|
+
'unsafe-url',
|
|
27
|
+
'no-referrer-when-downgrade',
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
// Recommended policies
|
|
31
|
+
static const _recommendedPolicies = [
|
|
32
|
+
'strict-origin-when-cross-origin',
|
|
33
|
+
'same-origin',
|
|
34
|
+
'no-referrer',
|
|
35
|
+
'strict-origin',
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
@override
|
|
39
|
+
List<Violation> analyze({
|
|
40
|
+
required CompilationUnit unit,
|
|
41
|
+
required String filePath,
|
|
42
|
+
required Rule rule,
|
|
43
|
+
required LineInfo lineInfo,
|
|
44
|
+
}) {
|
|
45
|
+
final violations = <Violation>[];
|
|
46
|
+
final visitor = _S021Visitor(
|
|
47
|
+
filePath: filePath,
|
|
48
|
+
lineInfo: lineInfo,
|
|
49
|
+
violations: violations,
|
|
50
|
+
analyzer: this,
|
|
51
|
+
);
|
|
52
|
+
unit.accept(visitor);
|
|
53
|
+
return violations;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
class _S021Visitor extends RecursiveAstVisitor<void> {
|
|
58
|
+
final String filePath;
|
|
59
|
+
final LineInfo lineInfo;
|
|
60
|
+
final List<Violation> violations;
|
|
61
|
+
final S021ReferrerPolicyAnalyzer analyzer;
|
|
62
|
+
|
|
63
|
+
_S021Visitor({
|
|
64
|
+
required this.filePath,
|
|
65
|
+
required this.lineInfo,
|
|
66
|
+
required this.violations,
|
|
67
|
+
required this.analyzer,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
@override
|
|
71
|
+
void visitMethodInvocation(MethodInvocation node) {
|
|
72
|
+
final source = node.toSource().toLowerCase();
|
|
73
|
+
final methodName = node.methodName.name.toLowerCase();
|
|
74
|
+
|
|
75
|
+
// Check for header setting operations
|
|
76
|
+
bool isHeaderOp = S021ReferrerPolicyAnalyzer._headerPatterns
|
|
77
|
+
.any((p) => source.contains(p) || methodName.contains(p));
|
|
78
|
+
|
|
79
|
+
if (isHeaderOp && source.contains('referrer')) {
|
|
80
|
+
// Check for weak policies
|
|
81
|
+
bool hasWeakPolicy = S021ReferrerPolicyAnalyzer._weakPolicies
|
|
82
|
+
.any((p) => source.contains(p));
|
|
83
|
+
|
|
84
|
+
if (hasWeakPolicy) {
|
|
85
|
+
violations.add(analyzer.createViolation(
|
|
86
|
+
filePath: filePath,
|
|
87
|
+
line: analyzer.getLine(lineInfo, node.offset),
|
|
88
|
+
column: analyzer.getColumn(lineInfo, node.offset),
|
|
89
|
+
message:
|
|
90
|
+
'Weak Referrer-Policy - use strict-origin-when-cross-origin or stricter policy',
|
|
91
|
+
));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
super.visitMethodInvocation(node);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@override
|
|
99
|
+
void visitMapLiteralEntry(MapLiteralEntry node) {
|
|
100
|
+
final key = node.key.toSource().toLowerCase();
|
|
101
|
+
final value = node.value.toSource().toLowerCase();
|
|
102
|
+
|
|
103
|
+
// Check for Referrer-Policy header
|
|
104
|
+
if (key.contains('referrer-policy') || key.contains('referrerpolicy')) {
|
|
105
|
+
// Check for weak policies
|
|
106
|
+
bool hasWeakPolicy = S021ReferrerPolicyAnalyzer._weakPolicies
|
|
107
|
+
.any((p) => value.contains(p));
|
|
108
|
+
|
|
109
|
+
if (hasWeakPolicy) {
|
|
110
|
+
violations.add(analyzer.createViolation(
|
|
111
|
+
filePath: filePath,
|
|
112
|
+
line: analyzer.getLine(lineInfo, node.offset),
|
|
113
|
+
column: analyzer.getColumn(lineInfo, node.offset),
|
|
114
|
+
message:
|
|
115
|
+
'Weak Referrer-Policy "$value" - use strict-origin-when-cross-origin instead',
|
|
116
|
+
));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
super.visitMapLiteralEntry(node);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@override
|
|
124
|
+
void visitAssignmentExpression(AssignmentExpression node) {
|
|
125
|
+
final left = node.leftHandSide.toSource().toLowerCase();
|
|
126
|
+
final right = node.rightHandSide.toSource().toLowerCase();
|
|
127
|
+
|
|
128
|
+
// Check for referrer policy assignments
|
|
129
|
+
if (left.contains('referrer')) {
|
|
130
|
+
bool hasWeakPolicy = S021ReferrerPolicyAnalyzer._weakPolicies
|
|
131
|
+
.any((p) => right.contains(p));
|
|
132
|
+
|
|
133
|
+
if (hasWeakPolicy) {
|
|
134
|
+
violations.add(analyzer.createViolation(
|
|
135
|
+
filePath: filePath,
|
|
136
|
+
line: analyzer.getLine(lineInfo, node.offset),
|
|
137
|
+
column: analyzer.getColumn(lineInfo, node.offset),
|
|
138
|
+
message:
|
|
139
|
+
'Weak Referrer-Policy setting - use strict-origin-when-cross-origin or no-referrer',
|
|
140
|
+
));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
super.visitAssignmentExpression(node);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import 'package:analyzer/dart/ast/ast.dart';
|
|
2
|
+
import 'package:analyzer/dart/ast/visitor.dart';
|
|
3
|
+
import 'package:analyzer/source/line_info.dart';
|
|
4
|
+
|
|
5
|
+
import '../../models/rule.dart';
|
|
6
|
+
import '../../models/violation.dart';
|
|
7
|
+
import '../base_analyzer.dart';
|
|
8
|
+
|
|
9
|
+
/// S022: Escape Output Context
|
|
10
|
+
/// Ensure proper output encoding/escaping to prevent XSS
|
|
11
|
+
class S022EscapeOutputContextAnalyzer extends BaseAnalyzer {
|
|
12
|
+
@override
|
|
13
|
+
String get ruleId => 'S022';
|
|
14
|
+
|
|
15
|
+
// HTML output contexts
|
|
16
|
+
static const _htmlMethods = [
|
|
17
|
+
'innerhtml', 'outerhtml', 'setinnerhtml', 'appendhtml',
|
|
18
|
+
'insertadjacenthtml', 'document.write', 'createelement',
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
@override
|
|
22
|
+
List<Violation> analyze({
|
|
23
|
+
required CompilationUnit unit,
|
|
24
|
+
required String filePath,
|
|
25
|
+
required Rule rule,
|
|
26
|
+
required LineInfo lineInfo,
|
|
27
|
+
}) {
|
|
28
|
+
final violations = <Violation>[];
|
|
29
|
+
final visitor = _S022Visitor(
|
|
30
|
+
filePath: filePath,
|
|
31
|
+
lineInfo: lineInfo,
|
|
32
|
+
violations: violations,
|
|
33
|
+
analyzer: this,
|
|
34
|
+
);
|
|
35
|
+
unit.accept(visitor);
|
|
36
|
+
return violations;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class _S022Visitor extends RecursiveAstVisitor<void> {
|
|
41
|
+
final String filePath;
|
|
42
|
+
final LineInfo lineInfo;
|
|
43
|
+
final List<Violation> violations;
|
|
44
|
+
final S022EscapeOutputContextAnalyzer analyzer;
|
|
45
|
+
|
|
46
|
+
_S022Visitor({
|
|
47
|
+
required this.filePath,
|
|
48
|
+
required this.lineInfo,
|
|
49
|
+
required this.violations,
|
|
50
|
+
required this.analyzer,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
@override
|
|
54
|
+
void visitAssignmentExpression(AssignmentExpression node) {
|
|
55
|
+
final leftSide = node.leftHandSide.toSource().toLowerCase();
|
|
56
|
+
|
|
57
|
+
// Check for innerHTML assignment
|
|
58
|
+
if (S022EscapeOutputContextAnalyzer._htmlMethods.any((m) => leftSide.contains(m))) {
|
|
59
|
+
final rightSide = node.rightHandSide;
|
|
60
|
+
|
|
61
|
+
// Check if right side is user input (not sanitized)
|
|
62
|
+
if (rightSide is StringInterpolation || rightSide is SimpleIdentifier) {
|
|
63
|
+
final rightSource = rightSide.toSource().toLowerCase();
|
|
64
|
+
bool isSanitized = rightSource.contains('escape') ||
|
|
65
|
+
rightSource.contains('sanitize') ||
|
|
66
|
+
rightSource.contains('encode') ||
|
|
67
|
+
rightSource.contains('htmlescape');
|
|
68
|
+
|
|
69
|
+
if (!isSanitized) {
|
|
70
|
+
violations.add(analyzer.createViolation(
|
|
71
|
+
filePath: filePath,
|
|
72
|
+
line: analyzer.getLine(lineInfo, node.offset),
|
|
73
|
+
column: analyzer.getColumn(lineInfo, node.offset),
|
|
74
|
+
message: 'XSS risk - escape/sanitize user input before HTML output',
|
|
75
|
+
));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
super.visitAssignmentExpression(node);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@override
|
|
84
|
+
void visitMethodInvocation(MethodInvocation node) {
|
|
85
|
+
final methodName = node.methodName.name.toLowerCase();
|
|
86
|
+
|
|
87
|
+
// Check for HTML manipulation methods
|
|
88
|
+
if (S022EscapeOutputContextAnalyzer._htmlMethods.any((m) => methodName.contains(m))) {
|
|
89
|
+
for (final arg in node.argumentList.arguments) {
|
|
90
|
+
if (arg is StringInterpolation || arg is SimpleIdentifier) {
|
|
91
|
+
final source = arg.toSource().toLowerCase();
|
|
92
|
+
bool isSanitized = source.contains('escape') ||
|
|
93
|
+
source.contains('sanitize') ||
|
|
94
|
+
source.contains('encode');
|
|
95
|
+
|
|
96
|
+
if (!isSanitized) {
|
|
97
|
+
violations.add(analyzer.createViolation(
|
|
98
|
+
filePath: filePath,
|
|
99
|
+
line: analyzer.getLine(lineInfo, node.offset),
|
|
100
|
+
column: analyzer.getColumn(lineInfo, node.offset),
|
|
101
|
+
message: 'XSS risk - sanitize input before inserting into HTML',
|
|
102
|
+
));
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
super.visitMethodInvocation(node);
|
|
110
|
+
}
|
|
111
|
+
}
|