Skip to content

Commit 96b34f9

Browse files
authored
release/0.1.0-dev.6 (#17)
* feat: add core suppression infrastructure - Add Suppression class to parse shield_ignore comments - Support line-level and file-level suppression - Add toUnderscoreCase() method to RuleId enum - Handle underscore format rule IDs for consistency * feat: add mixed configuration format support - Add RuleConfig class for parsing string and object rule formats - Update LintRuleConverter to handle dynamic rule configurations - Support per-rule exclude patterns in configuration - Regenerate shield_config.g.dart with build_runner * feat: integrate suppression system with analysis engine - Add suppression checking in SecurityAnalyzer._analyzeUnit() - Implement file exclusion check in LintRule.check() method - Update LintIssue to use underscore format rule IDs - Support three-layer filtering: global, per-rule, and line-level * test: add comprehensive suppression system tests - Add unit tests for Suppression class parsing - Test line-level and file-level suppression - Test multiple rule suppression in single comment - Add integration test data file with example suppressions * test: update configuration tests and fix existing tests - Add tests for mixed format rule configuration parsing - Update existing tests to use underscore format rule IDs - Test string and object format rule configurations - Fix prefer_secure_random_test to expect correct rule ID format * docs: update example configuration with mixed format rules - Update example shield_options.yaml with mixed format rules - Include per-rule exclude patterns in configuration - Add comment examples showing new syntax * feat: add test reorganization plan and create fixtures/helpers structure - Add comprehensive test reorganization plan document - Create test/fixtures/ directory with sample Dart files and configs - Create test/helpers/ directory with test utilities - Add TestAnalyzer helper for AST analysis in tests - Add MockWorkspace helper for workspace mocking - Add TestDataBuilder helper for test data generation - Add sample vulnerable, secure, and mixed code files - Add sample configuration files (minimal, complete, invalid) - Add suppression example file for testing * test: add comprehensive unit tests for enums - Add RuleId enum tests covering fromYamlName and toUnderscoreCase methods - Add Severity enum tests covering values, analysisSeverity mapping, and comparison - Add RuleStatus enum tests covering lifecycle status management - Test edge cases, error handling, and enum integrity - Ensure 100% coverage for all enum types * test: add comprehensive unit tests for models - Add MatchingPattern tests covering constructor, regex property, fromJson factory - Add ShieldSecrets tests covering constructor, containsSecret method, YAML parsing - Add LintIssue tests covering constructor, withRule factory, JSON serialization - Test error handling, edge cases, and property validation - Ensure 100% coverage for all model classes * test: add unit tests for core components - Add RuleRegistry tests covering rule creation, registration, and immutability - Add Workspace tests covering path normalization, config management, and file operations - Test rule creation consistency and property validation - Test workspace edge cases including parent directory references - Ensure comprehensive coverage for core security analyzer components * refactor: move existing tests to new unit test structure - Move configuration tests to test/unit/security_analyzer/configuration/ - Move utility tests to test/unit/security_analyzer/utils/ and test/unit/utils/ - Update import paths to use new fixtures directory structure - Fix type inference issues in test files - Maintain all existing test functionality while improving organization * refactor: move PreferSecureRandom test to new structure - Move prefer_secure_random_test.dart to test/unit/security_analyzer/rules/rules_list/crypto/ - Maintain all existing test functionality - Improve test organization by categorizing security rules * cleanup: remove old test files after reorganization - Remove old test/data/ directory files - Remove old test/src/ directory structure - Complete migration to new test organization structure - All tests now properly organized in unit/, fixtures/, and helpers/ directories * style: formatting * feat!: convert configuration from kebab-case to snake_case BREAKING CHANGE: Configuration fields and rule names now use snake_case instead of kebab-case. - Change FieldRename from kebab to snake in configuration classes - Update RuleId.fromYamlName() to parse snake_case instead of kebab-case - Update error messages to use snake_case field names - Update rule config documentation examples This aligns the configuration format with Dart's analysis_options.yaml conventions. * build: regenerate code generation files for snake_case Update generated files to use snake_case field names instead of kebab-case. * refactor: update suppression and workspace for snake_case - Simplify suppression canonicalization to remove kebab-case compatibility - Update default config template to use snake_case rule names - Update suppression comments to use snake_case format * docs: update examples and documentation for snake_case - Update example/shield_options.yaml to use snake_case format - Update README.md configuration examples - Align documentation with new snake_case naming convention * test: update test fixtures for snake_case configuration - Update all YAML test fixtures to use snake_case format - Update inline config strings in test helpers - Ensure tests use the new snake_case naming convention * test: update unit tests for snake_case format - Update lint rule converter tests to expect snake_case rule names - Update rule ID tests to parse snake_case instead of kebab-case - Update suppression tests to use snake_case format - Update workspace tests to expect snake_case in default config - Remove backward compatibility tests for kebab-case * docs: update changelog for snake_case breaking change Document the breaking change from kebab-case to snake_case configuration format. Includes detailed migration guide for all affected configuration fields and rule names. * chore: update CHANGELOG * chore: raise version * style: formatting * chore: update description * chore: update todos * chore: README * chore: update README * chore: update todos
1 parent 1c25729 commit 96b34f9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2413
-104
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.1.0-dev.6] - 2025-10-23
9+
10+
### Breaking Changes
11+
- Configuration format converted from kebab-case to snake_case to align with Dart's `analysis_options.yaml` conventions
12+
13+
### Added
14+
- Comprehensive suppression system for ignoring specific rules or lines during analysis
15+
16+
### Changed
17+
- Test reorganization plan and improved test coverage
18+
19+
### Tests
20+
- Added comprehensive unit tests for suppression system
21+
- Enhanced test coverage for models and enums
22+
823
## [0.1.0-dev.5] - 2025-10-09
924

1025
### Added

README.md

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
<div align="center">
44
<picture>
5-
<source media="(prefers-color-scheme: light)" srcset="resources/img/shield-logo.svg">
6-
<img alt="Dart Shield" src="resources/img/shield-logo.svg" width="150">
5+
<img
6+
alt="Dart Shield"
7+
src="https://github.com/yardexx/dart_shield/blob/master/resources/img/shield-logo.svg"
8+
width="150"
9+
>
710
</picture>
811
<p>Dart-based security-focused code analyzer which analyzes your Dart code for potential security flaws.</p>
912
<a href="https://github.com/yardexx/dart_shield/actions/workflows/dart.yml"><img src="https://github.com/yardexx/dart_shield/actions/workflows/dart.yml/badge.svg" alt="Pipelines: GitHub Actions"/></a>
@@ -16,8 +19,7 @@
1619

1720
> 🚧 UNDER CONSTRUCTION 🚧
1821
>
19-
> Please note that this project is still under construction and **not yet ready for production use
20-
**.
22+
> Please note that this project is still under construction and not yet ready for production use.
2123
>
2224
> Full documentation will be available once the project is ready for production use. If you have
2325
> any questions, feel free to open an issue.
@@ -38,12 +40,13 @@ is similar to what you might expect.
3840
- Usage of insecure HTTP connections
3941

4042
# Installation
41-
42-
> **Note:** dart_shield is not yet available on pub.dev.
43-
4443
To install dart_shield, run the following command:
4544

4645
```bash
46+
# Using pub.dev
47+
dart pub global activate dart_shield
48+
49+
# Directly from GitHub
4750
dart pub global activate -s git https://github.com/yardexx/dart_shield
4851
```
4952

@@ -115,20 +118,20 @@ shield:
115118

116119
# List of rules that dart_shield will use to analyze your code
117120
rules:
118-
- prefer-https-over-http
119-
- avoid-hardcoded-secrets
121+
- prefer_https_over_http
122+
- avoid_hardcoded_secrets
120123

121124
# Some rules need more fine-tuning and are marked as experimental.
122-
# You can enable them by setting `enable-experimental` to `true`.
123-
enable-experimental: true
125+
# You can enable them by setting `enable_experimental` to `true`.
126+
enable_experimental: true
124127

125128
# List of experimental rules that dart_shield will use to analyze your code
126129
# ⚠️ Experimental rules are subject to change and may not be as stable as regular rules.
127-
# ⚠️ Using "experimental-rules" without setting "enable-experimental" to "true" will cause an error.
128-
experimental-rules:
129-
- avoid-hardcoded-urls
130-
- avoid-weak-hashing
131-
- prefer-secure-random
130+
# ⚠️ Using "experimental_rules" without setting "enable_experimental" to "true" will cause an error.
131+
experimental_rules:
132+
- avoid_hardcoded_urls
133+
- avoid_weak_hashing
134+
- prefer_secure_random
132135
```
133136
134137
# Rules
@@ -138,11 +141,11 @@ similar to how linter rules enforce code style.
138141
139142
## List of rules
140143
141-
- avoid-hardcoded-secrets: Detects hardcoded secrets, such as API keys and passwords.
142-
- avoid-hardcoded-urls: Detects hardcoded URLs.
143-
- prefer-https-over-http: Detects the use of insecure HTTP connections.
144-
- avoid-weak-hashing: Detects the use of weak hashing algorithms, such as MD5 and SHA-1.
145-
- prefer-secure-random: Detects the use of non-secure random number generators.
144+
- avoid_hardcoded_secrets: Detects hardcoded secrets, such as API keys and passwords.
145+
- avoid_hardcoded_urls: Detects hardcoded URLs.
146+
- prefer_https_over_http: Detects the use of insecure HTTP connections.
147+
- avoid_weak_hashing: Detects the use of weak hashing algorithms, such as MD5 and SHA-1.
148+
- prefer_secure_random: Detects the use of non-secure random number generators.
146149
147150
# Contributing
148151

analysis_options.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
include: package:very_good_analysis/analysis_options.yaml
22

33
analyzer:
4+
errors:
5+
document_ignores: ignore
46
exclude:
57
- '**.g.dart'
68
linter:

example/shield_options.yaml

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,24 @@ shield:
1515
- '**.g.dart'
1616

1717
# List of rules that dart_shield will use to analyze your code
18+
# You can use both simple string format and object format with exclusions
1819
rules:
19-
- prefer-https-over-http
20-
- avoid-hardcoded-secrets
20+
- prefer_https_over_http
21+
- avoid_hardcoded_secrets:
22+
exclude:
23+
- lib/config.dart
24+
- test/**
2125

2226
# Some rules need more fine-tuning and are marked as experimental.
23-
# You can enable them by setting `enable-experimental` to `true`.
24-
enable-experimental: true
27+
# You can enable them by setting `enable_experimental` to `true`.
28+
enable_experimental: true
2529

2630
# List of experimental rules that dart_shield will use to analyze your code
2731
# ⚠️ Experimental rules are subject to change and may not be as stable as regular rules.
28-
# ⚠️ Using "experimental-rules" without setting "enable-experimental" to "true" will cause an error.
29-
experimental-rules:
30-
- avoid-hardcoded-urls
31-
- avoid-weak-hashing
32-
- prefer-secure-random
32+
# ⚠️ Using "experimental_rules" without setting "enable_experimental" to "true" will cause an error.
33+
experimental_rules:
34+
- avoid_hardcoded_urls
35+
- avoid_weak_hashing:
36+
exclude:
37+
- lib/legacy/**
38+
- prefer_secure_random

lib/assets/shield_secrets_dart.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copy of [shield_secrets.yaml] because Dart doesn't support native assets yet.
22
// This is a workaround until Dart SDK adds native asset support.
33
// See: https://github.com/dart-lang/sdk/issues/53562
4-
// TODO: Remove this file and use native assets when Dart SDK supports it
4+
// TODO(yardex): Use native assets when Dart SDK supports it
55

66
const String shieldSecretsSource = r'''
77
shield_patterns:

lib/src/security_analyzer/configuration/lint_rule_converter.dart

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1+
import 'package:dart_shield/src/security_analyzer/configuration/rule_config.dart';
12
import 'package:dart_shield/src/security_analyzer/rules/enums/enums.dart';
23
import 'package:dart_shield/src/security_analyzer/rules/rule/rule.dart';
34
import 'package:dart_shield/src/security_analyzer/rules/rule_registry.dart';
5+
import 'package:glob/glob.dart';
46
import 'package:json_annotation/json_annotation.dart';
57

6-
class LintRuleConverter implements JsonConverter<LintRule, String> {
8+
class LintRuleConverter implements JsonConverter<LintRule, dynamic> {
79
const LintRuleConverter();
810

9-
// Todo: Implement file exclusion
1011
@override
11-
LintRule fromJson(String name) {
12+
LintRule fromJson(dynamic value) {
13+
final ruleConfig = RuleConfig.fromDynamic(value);
14+
final excludePatterns = ruleConfig.exclude.map(Glob.new).toList();
15+
1216
final rule = RuleRegistry.createRule(
13-
id: RuleId.fromYamlName(name),
14-
excludes: [],
17+
id: RuleId.fromYamlName(ruleConfig.name),
18+
excludes: excludePatterns,
1519
);
1620

1721
return rule;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/// Configuration for a single rule that supports both string and
2+
/// object formats.
3+
class RuleConfig {
4+
const RuleConfig({
5+
required this.name,
6+
this.exclude = const [],
7+
});
8+
9+
/// Creates a RuleConfig from a dynamic value that can be either a String
10+
/// or Map.
11+
factory RuleConfig.fromDynamic(dynamic value) {
12+
if (value is String) {
13+
return RuleConfig(name: value);
14+
}
15+
16+
if (value is Map) {
17+
final entries = value.entries.toList();
18+
if (entries.length != 1) {
19+
throw ArgumentError('Rule config map must have exactly one entry');
20+
}
21+
22+
final entry = entries.first;
23+
final name = entry.key.toString();
24+
final configValue = entry.value;
25+
26+
final exclude = <String>[];
27+
if (configValue is Map) {
28+
final excludeList = configValue['exclude'];
29+
if (excludeList is List) {
30+
exclude.addAll(excludeList.map((e) => e.toString()));
31+
}
32+
}
33+
34+
return RuleConfig(name: name, exclude: exclude);
35+
} else {
36+
throw ArgumentError(
37+
'Rule config must be a String or Map, got ${value.runtimeType}',
38+
);
39+
}
40+
}
41+
42+
/// The name of the rule (e.g., 'avoid_hardcoded_secrets').
43+
final String name;
44+
45+
/// List of file patterns to exclude for this rule.
46+
final List<String> exclude;
47+
48+
Map<String, dynamic> toJson() => {
49+
'name': name,
50+
'exclude': exclude,
51+
};
52+
}

lib/src/security_analyzer/configuration/shield_config.dart

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import 'package:yaml/yaml.dart';
1212
part 'shield_config.g.dart';
1313

1414
@JsonSerializable(
15-
fieldRename: FieldRename.kebab,
15+
fieldRename: FieldRename.snake,
1616
converters: [LintRuleConverter(), GlobConverter()],
1717
createToJson: false,
1818
)
@@ -63,7 +63,7 @@ class ShieldConfig {
6363
.join(', ');
6464
throw InvalidConfigurationException(
6565
'Found experimental rule(s) in the "rules" list: $ruleNames. '
66-
'Move these to "experimental-rules" list.',
66+
'Move these to "experimental_rules" list.',
6767
);
6868
}
6969

@@ -74,9 +74,9 @@ class ShieldConfig {
7474
.map((rule) => rule.id.name)
7575
.join(', ');
7676
throw InvalidConfigurationException(
77-
'Found experimental rule(s) in "experimental-rules" list: $ruleNames, '
78-
'but "enable-experimental" is set to false. '
79-
'Set "enable-experimental" to true to use these rules.',
77+
'Found experimental rule(s) in "experimental_rules" list: $ruleNames, '
78+
'but "enable_experimental" is set to false. '
79+
'Set "enable_experimental" to true to use these rules.',
8080
);
8181
}
8282

@@ -89,7 +89,7 @@ class ShieldConfig {
8989
.map((rule) => rule.id.name)
9090
.join(', ');
9191
throw InvalidConfigurationException(
92-
'Found non-experimental rule(s) in "experimental-rules" list: '
92+
'Found non-experimental rule(s) in "experimental_rules" list: '
9393
'$ruleNames. Move these to the "rules" list.',
9494
);
9595
}

lib/src/security_analyzer/configuration/shield_config.g.dart

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/src/security_analyzer/rules/enums/rule_id.dart

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,24 @@ enum RuleId {
66
preferSecureRandom;
77

88
static RuleId fromYamlName(String name) {
9-
// Convert kebab-case to camelCase
10-
final camelCaseName = name.replaceAllMapped(RegExp(r'-(\w)'), (match) {
9+
// Convert snake_case to camelCase
10+
final camelCaseName = name.replaceAllMapped(RegExp(r'_(\w)'), (match) {
1111
final matchStr = match.group(1);
1212
return matchStr != null ? matchStr.toUpperCase() : '';
1313
});
1414

1515
// Use RuleId.values.byName to get the enum value
1616
return RuleId.values.byName(camelCaseName);
1717
}
18+
19+
/// Converts the enum name to underscore format for use in suppression
20+
/// comments.
21+
/// Example: preferHttpsOverHttp -> prefer_https_over_http
22+
String toUnderscoreCase() {
23+
final name = this.name;
24+
// Convert camelCase to underscore_case
25+
return name.replaceAllMapped(RegExp('([a-z])([A-Z])'), (match) {
26+
return '${match.group(1)}_${match.group(2)!.toLowerCase()}';
27+
});
28+
}
1829
}

0 commit comments

Comments
 (0)