hedl_lint/rules/common.rs
1// Dweve HEDL - Hierarchical Entity Data Language
2//
3// Copyright (c) 2025 Dweve IP B.V. and individual contributors.
4//
5// SPDX-License-Identifier: Apache-2.0
6//
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License in the LICENSE file at the
10// root of this repository or at: http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Common utilities and constants for lint rules
19
20use crate::diagnostic::Diagnostic;
21use hedl_core::Document;
22use std::any::Any;
23
24/// Maximum recursion depth for document traversal.
25///
26/// This limit prevents stack overflow attacks from deeply nested document structures.
27/// A malicious document with 1000+ levels of nesting could cause stack exhaustion,
28/// leading to process crashes or potential security vulnerabilities.
29///
30/// Security Rationale:
31/// - Stack frames typically consume 100-200 bytes each
32/// - At 1000 depth, this represents ~100-200KB of stack usage
33/// - Most legitimate HEDL documents have <10 levels of nesting
34/// - This limit provides defense-in-depth against DoS attacks
35pub const MAX_RECURSION_DEPTH: usize = 1000;
36
37/// Trait for lint rules
38pub trait LintRule: Send + Sync {
39 /// Rule identifier
40 fn id(&self) -> &str;
41
42 /// Rule description
43 fn description(&self) -> &str;
44
45 /// Run the rule on a document
46 fn check(&self, doc: &Document) -> Vec<Diagnostic>;
47
48 /// Run the rule on a document with context information
49 ///
50 /// The default implementation calls `check()`, ignoring the context.
51 /// Rules that need context (file path, line numbers) should override this method.
52 ///
53 /// The context is passed as `&dyn Any` to avoid circular imports.
54 /// Cast it to `&crate::runner::LintContext` to access context information.
55 fn check_with_context(&self, doc: &Document, _context: &dyn Any) -> Vec<Diagnostic> {
56 self.check(doc)
57 }
58}
59
60/// Configuration for a single rule
61#[derive(Debug, Clone)]
62pub struct RuleConfig {
63 /// Whether the rule is enabled
64 pub enabled: bool,
65 /// Whether to escalate all diagnostics from this rule to Error severity.
66 ///
67 /// When true, both Hint and Warning severities become Error, allowing
68 /// enforcement of strict linting in CI/CD pipelines.
69 pub error: bool,
70}
71
72impl Default for RuleConfig {
73 fn default() -> Self {
74 Self {
75 enabled: true,
76 error: false,
77 }
78 }
79}