@@ -438,7 +438,9 @@ impl Default for ProvidersConfig {
438438}
439439
440440fn default_enabled_providers ( ) -> Vec < ProviderType > {
441- vec ! [ ProviderType :: Rma ]
441+ // Rma: built-in rules for all languages
442+ // Oxc: native JS/TS linting (no external binary required)
443+ vec ! [ ProviderType :: Rma , ProviderType :: Oxc ]
442444}
443445
444446/// PMD Java provider configuration
@@ -768,6 +770,77 @@ fn default_baseline_file() -> PathBuf {
768770 PathBuf :: from ( ".rma/baseline.json" )
769771}
770772
773+ // =============================================================================
774+ // SUPPRESSION DATABASE CONFIGURATION
775+ // =============================================================================
776+
777+ /// Configuration for the suppression database
778+ #[ derive( Debug , Clone , Serialize , Deserialize ) ]
779+ pub struct SuppressionConfig {
780+ /// Path to the suppression database (relative to project root)
781+ #[ serde( default = "default_suppression_database" ) ]
782+ pub database : PathBuf ,
783+
784+ /// Default expiration period for new suppressions (e.g., "90d", "30d", "7d")
785+ #[ serde( default = "default_expiration" ) ]
786+ pub default_expiration : String ,
787+
788+ /// Whether a ticket reference is required for new suppressions
789+ #[ serde( default ) ]
790+ pub require_ticket : bool ,
791+
792+ /// Maximum expiration period allowed (e.g., "365d")
793+ #[ serde( default = "default_max_expiration" ) ]
794+ pub max_expiration : String ,
795+
796+ /// Whether to enable the database suppression source
797+ #[ serde( default = "default_enabled" ) ]
798+ pub enabled : bool ,
799+ }
800+
801+ impl Default for SuppressionConfig {
802+ fn default ( ) -> Self {
803+ Self {
804+ database : default_suppression_database ( ) ,
805+ default_expiration : default_expiration ( ) ,
806+ require_ticket : false ,
807+ max_expiration : default_max_expiration ( ) ,
808+ enabled : true ,
809+ }
810+ }
811+ }
812+
813+ fn default_suppression_database ( ) -> PathBuf {
814+ PathBuf :: from ( ".rma/suppressions.db" )
815+ }
816+
817+ fn default_expiration ( ) -> String {
818+ "90d" . to_string ( )
819+ }
820+
821+ fn default_max_expiration ( ) -> String {
822+ "365d" . to_string ( )
823+ }
824+
825+ fn default_enabled ( ) -> bool {
826+ true
827+ }
828+
829+ /// Parse a duration string (e.g., "90d", "30d", "7d") to days
830+ pub fn parse_expiration_days ( s : & str ) -> Option < u32 > {
831+ let s = s. trim ( ) . to_lowercase ( ) ;
832+ if let Some ( days_str) = s. strip_suffix ( 'd' ) {
833+ days_str. parse ( ) . ok ( )
834+ } else if let Some ( weeks_str) = s. strip_suffix ( 'w' ) {
835+ weeks_str. parse :: < u32 > ( ) . ok ( ) . map ( |w| w * 7 )
836+ } else if let Some ( months_str) = s. strip_suffix ( 'm' ) {
837+ months_str. parse :: < u32 > ( ) . ok ( ) . map ( |m| m * 30 )
838+ } else {
839+ // Try parsing as plain number (days)
840+ s. parse ( ) . ok ( )
841+ }
842+ }
843+
771844/// Current supported config version
772845pub const CURRENT_CONFIG_VERSION : u32 = 1 ;
773846
@@ -813,6 +886,10 @@ pub struct RmaTomlConfig {
813886 /// Analysis providers configuration
814887 #[ serde( default ) ]
815888 pub providers : ProvidersConfig ,
889+
890+ /// Suppression database configuration
891+ #[ serde( default ) ]
892+ pub suppressions : SuppressionConfig ,
816893}
817894
818895/// Result of loading a config file
@@ -1294,10 +1371,11 @@ mode = "all"
12941371# Providers can be enabled/disabled individually.
12951372
12961373[providers]
1297- # List of enabled providers (default: only "rma" built-in rules)
1298- enabled = ["rma"]
1299- # To enable PMD for Java: enabled = ["rma", "pmd"]
1300- # To enable Oxlint for JS/TS: enabled = ["rma", "oxlint"]
1374+ # List of enabled providers
1375+ # Default: ["rma", "oxc"] - built-in rules + native JS/TS linting
1376+ enabled = ["rma", "oxc"]
1377+ # To add PMD for Java: enabled = ["rma", "oxc", "pmd"]
1378+ # To add external Oxlint: enabled = ["rma", "oxc", "oxlint"]
13011379
13021380# -----------------------------------------------------------------------------
13031381# PMD Provider - Java Static Analysis (optional)
@@ -1941,6 +2019,8 @@ pub enum SuppressionSource {
19412019 Preset ,
19422020 /// Suppressed by baseline
19432021 Baseline ,
2022+ /// Suppressed by database entry
2023+ Database ,
19442024}
19452025
19462026impl std:: fmt:: Display for SuppressionSource {
@@ -1951,6 +2031,7 @@ impl std::fmt::Display for SuppressionSource {
19512031 SuppressionSource :: PathRule => write ! ( f, "path-rule" ) ,
19522032 SuppressionSource :: Preset => write ! ( f, "preset" ) ,
19532033 SuppressionSource :: Baseline => write ! ( f, "baseline" ) ,
2034+ SuppressionSource :: Database => write ! ( f, "database" ) ,
19542035 }
19552036 }
19562037}
@@ -1963,6 +2044,7 @@ impl std::fmt::Display for SuppressionSource {
19632044/// - Inline suppressions (rma-ignore comments)
19642045/// - Default test/example presets for PR/CI mode
19652046/// - Baseline filtering
2047+ /// - Database suppressions (when enabled)
19662048pub struct SuppressionEngine {
19672049 /// Global ignore paths
19682050 global_ignore_paths : Vec < String > ,
@@ -1980,6 +2062,8 @@ pub struct SuppressionEngine {
19802062 test_patterns : Vec < regex:: Regex > ,
19812063 /// Compiled regex patterns for default example paths
19822064 example_patterns : Vec < regex:: Regex > ,
2065+ /// Optional suppression store for database-backed suppressions
2066+ suppression_store : Option < std:: sync:: Arc < crate :: suppression:: SuppressionStore > > ,
19832067}
19842068
19852069impl SuppressionEngine {
@@ -2027,6 +2111,7 @@ impl SuppressionEngine {
20272111 rule_patterns,
20282112 test_patterns,
20292113 example_patterns,
2114+ suppression_store : None ,
20302115 }
20312116 }
20322117
@@ -2041,6 +2126,23 @@ impl SuppressionEngine {
20412126 self
20422127 }
20432128
2129+ /// Set the suppression store for database-backed suppressions
2130+ pub fn with_store ( mut self , store : crate :: suppression:: SuppressionStore ) -> Self {
2131+ self . suppression_store = Some ( std:: sync:: Arc :: new ( store) ) ;
2132+ self
2133+ }
2134+
2135+ /// Set a shared suppression store reference
2136+ pub fn with_store_ref ( mut self , store : std:: sync:: Arc < crate :: suppression:: SuppressionStore > ) -> Self {
2137+ self . suppression_store = Some ( store) ;
2138+ self
2139+ }
2140+
2141+ /// Get a reference to the suppression store (if available)
2142+ pub fn store ( & self ) -> Option < & crate :: suppression:: SuppressionStore > {
2143+ self . suppression_store . as_ref ( ) . map ( |s| s. as_ref ( ) )
2144+ }
2145+
20442146 /// Compile a glob pattern to a regex
20452147 ///
20462148 /// Handles cases like:
@@ -2208,6 +2310,19 @@ impl SuppressionEngine {
22082310 }
22092311 }
22102312
2313+ // 6. Check database suppressions (applies to all rules including always-enabled)
2314+ if let Some ( ref store) = self . suppression_store
2315+ && let Some ( fp) = fingerprint
2316+ {
2317+ if let Ok ( Some ( entry) ) = store. is_suppressed ( fp) {
2318+ return SuppressionResult :: suppressed (
2319+ SuppressionSource :: Database ,
2320+ entry. reason . clone ( ) ,
2321+ format ! ( "database:{}" , entry. id) ,
2322+ ) ;
2323+ }
2324+ }
2325+
22112326 SuppressionResult :: not_suppressed ( )
22122327 }
22132328
@@ -2250,6 +2365,18 @@ impl SuppressionEngine {
22502365 "suppression_source" . to_string ( ) ,
22512366 serde_json:: json!( source. to_string( ) ) ,
22522367 ) ;
2368+
2369+ // For database suppressions, extract the suppression_id
2370+ if * source == SuppressionSource :: Database {
2371+ if let Some ( ref location) = result. location {
2372+ if let Some ( id) = location. strip_prefix ( "database:" ) {
2373+ properties. insert (
2374+ "suppression_id" . to_string ( ) ,
2375+ serde_json:: json!( id) ,
2376+ ) ;
2377+ }
2378+ }
2379+ }
22532380 }
22542381 if let Some ( ref location) = result. location {
22552382 properties. insert (
0 commit comments