Skip to main content

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}