Skip to content

Commit 77ba14b

Browse files
committed
feat(ls): allow the user to specify a regular expression that all rule identifiers must match.
If some rule has an identifier that doesn't match the regexp, a warning is shown.
1 parent 073056e commit 77ba14b

File tree

3 files changed

+72
-46
lines changed

3 files changed

+72
-46
lines changed

ls/editors/code/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@
9999
}
100100
}
101101
}
102+
},
103+
"YARA.ruleNameValidation": {
104+
"type": "string",
105+
"default": "",
106+
"description": "A regular expression that all rule names must match."
102107
}
103108
}
104109
},

ls/src/features/diagnostics.rs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use dashmap::mapref::one::Ref;
99
use serde::{Deserialize, Serialize};
1010

1111
use crate::documents::{document::Document, storage::DocumentStorage};
12-
use crate::server::MetadataValidationRule;
1312

1413
#[cfg(feature = "full-compiler")]
1514
use yara_x::linters;
@@ -27,12 +26,33 @@ pub struct Patch {
2726
pub replacement: String,
2827
}
2928

29+
/// Rule that describes a how to validate a metadata entry in a rule.
30+
#[derive(Deserialize, Debug, Clone, Default)]
31+
#[serde(rename_all = "camelCase")]
32+
pub struct MetadataValidationRule {
33+
/// Metadata identifier
34+
pub identifier: String,
35+
/// Whether the metadata entry is required or not.
36+
#[serde(default)]
37+
pub required: bool,
38+
/// Type of the metadata entry.
39+
#[serde(rename = "type")]
40+
pub ty: Option<String>,
41+
}
42+
43+
#[derive(Debug, Clone, Default, Deserialize)]
44+
#[serde(rename_all = "camelCase")]
45+
pub struct Settings {
46+
pub metadata_validation: Vec<MetadataValidationRule>,
47+
pub rule_name_validation: Option<String>,
48+
}
49+
3050
/// Returns a diagnostic vector for the given source code.
3151
#[allow(unused_variables)]
3252
pub fn diagnostics(
3353
documents: Arc<DocumentStorage>,
3454
uri: Url,
35-
meta_validation_rules: Vec<MetadataValidationRule>,
55+
settings: Settings,
3656
) -> Vec<Diagnostic> {
3757
#[allow(unused_mut)]
3858
let mut diagnostics: Vec<Diagnostic> = Vec::new();
@@ -41,29 +61,35 @@ pub fn diagnostics(
4161

4262
if let Some(doc) = doc {
4363
#[cfg(feature = "full-compiler")]
44-
diagnostics.extend(compiler_diagnostics(doc, meta_validation_rules));
64+
diagnostics.extend(compiler_diagnostics(doc, settings));
4565
}
4666

4767
diagnostics
4868
}
4969

5070
/// Return diagnostic vector for the given source code.
5171
///
52-
/// This function compiles the source code using the full YARA-X compiler
72+
/// This function compiles the source code using the full YARA-X 'compiler'
5373
/// and collects all errors and warnings as LSP diagnostics. This provides
5474
/// comprehensive feedback including type checking, semantic analysis,
5575
/// and pattern validation - not just syntax errors.
5676
#[cfg(feature = "full-compiler")]
5777
pub fn compiler_diagnostics(
5878
document: Ref<'_, Url, Document>,
59-
metadata_validation_rules: Vec<MetadataValidationRule>,
79+
settings: Settings,
6080
) -> Vec<Diagnostic> {
6181
let source_code = SourceCode::from(document.text.as_str())
6282
.with_origin(document.uri.clone());
6383

6484
let mut compiler = Compiler::new();
6585

66-
for validation_rule in metadata_validation_rules {
86+
if let Some(regex) = settings.rule_name_validation {
87+
if let Ok(linter) = linters::rule_name(regex) {
88+
compiler.add_linter(linter);
89+
}
90+
}
91+
92+
for validation_rule in settings.metadata_validation {
6793
let mut linter = linters::metadata(&validation_rule.identifier)
6894
.required(validation_rule.required);
6995

ls/src/server.rs

Lines changed: 35 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,14 @@ use async_lsp::lsp_types::{
3636
use async_lsp::router::Router;
3737
use async_lsp::{ClientSocket, LanguageClient, LanguageServer, ResponseError};
3838
use futures::future::BoxFuture;
39-
use serde::Deserialize;
39+
use serde_json::from_value;
4040

4141
use crate::documents::storage::DocumentStorage;
4242
use crate::features::code_action::code_actions;
4343
use crate::features::completion::completion;
44-
use crate::features::diagnostics::diagnostics;
44+
use crate::features::diagnostics::{
45+
diagnostics, MetadataValidationRule, Settings,
46+
};
4547
use crate::features::document_highlight::document_highlight;
4648
use crate::features::document_symbol::document_symbol;
4749
use crate::features::formatting::formatting;
@@ -55,20 +57,6 @@ use crate::features::semantic_tokens::{
5557
semantic_tokens, SEMANTIC_TOKEN_MODIFIERS, SEMANTIC_TOKEN_TYPES,
5658
};
5759

58-
/// Rule that describes a how to validate a metadata entry in a rule.
59-
#[derive(Deserialize, Debug, Clone, Default)]
60-
#[serde(rename_all = "camelCase")]
61-
pub struct MetadataValidationRule {
62-
/// Metadata identifier
63-
pub identifier: String,
64-
/// Whether the metadata entry is required or not.
65-
#[serde(default)]
66-
pub required: bool,
67-
/// Type of the metadata entry.
68-
#[serde(rename = "type")]
69-
pub ty: Option<String>,
70-
}
71-
7260
/// Represents a YARA language server.
7361
pub struct YARALanguageServer {
7462
/// Client socket for communication with the Development Tool.
@@ -426,17 +414,15 @@ impl LanguageServer for YARALanguageServer {
426414
let client = self.client.clone();
427415

428416
Box::pin(async move {
429-
let meta_specs =
430-
Self::get_meta_validation_rules(client.clone(), uri.clone())
431-
.await;
417+
let settings = Self::get_settings(client, uri.clone()).await;
432418

433419
Ok(DocumentDiagnosticReportResult::Report(
434420
async_lsp::lsp_types::DocumentDiagnosticReport::Full(
435421
RelatedFullDocumentDiagnosticReport {
436422
full_document_diagnostic_report:
437423
FullDocumentDiagnosticReport {
438424
result_id: None,
439-
items: diagnostics(documents, uri, meta_specs),
425+
items: diagnostics(documents, uri, settings),
440426
},
441427
related_documents: None,
442428
},
@@ -563,23 +549,39 @@ impl YARALanguageServer {
563549
})
564550
}
565551

566-
async fn get_meta_validation_rules(
552+
async fn get_settings(
567553
mut client: ClientSocket,
568554
scope_uri: Url,
569-
) -> Vec<MetadataValidationRule> {
555+
) -> Settings {
570556
client
571557
.configuration(ConfigurationParams {
572-
items: vec![ConfigurationItem {
573-
scope_uri: Some(scope_uri),
574-
section: Some("YARA.metadataValidation".to_string()),
575-
}],
558+
items: vec![
559+
ConfigurationItem {
560+
scope_uri: Some(scope_uri.clone()),
561+
section: Some("YARA.metadataValidation".to_string()),
562+
},
563+
ConfigurationItem {
564+
scope_uri: Some(scope_uri),
565+
section: Some("YARA.ruleNameValidation".to_string()),
566+
},
567+
],
576568
})
577569
.await
578570
.ok()
579-
.and_then(|mut v| v.pop())
580-
.and_then(|value| {
581-
serde_json::from_value::<Vec<MetadataValidationRule>>(value)
582-
.ok()
571+
.map(|mut v| {
572+
let rule_name_validation = v
573+
.pop()
574+
.and_then(|value| from_value::<Option<String>>(value).ok())
575+
.unwrap_or_default();
576+
577+
let metadata_validation = v
578+
.pop()
579+
.and_then(|value| {
580+
from_value::<Vec<MetadataValidationRule>>(value).ok()
581+
})
582+
.unwrap_or_default();
583+
584+
Settings { metadata_validation, rule_name_validation }
583585
})
584586
.unwrap_or_default()
585587
}
@@ -592,19 +594,12 @@ impl YARALanguageServer {
592594
let uri = uri.clone();
593595

594596
tokio::spawn(async move {
595-
let meta_validation_rules = Self::get_meta_validation_rules(
596-
client.clone(),
597-
uri.clone(),
598-
)
599-
.await;
597+
let settings =
598+
Self::get_settings(client.clone(), uri.clone()).await;
600599

601600
client.publish_diagnostics(PublishDiagnosticsParams {
602601
uri: uri.clone(),
603-
diagnostics: diagnostics(
604-
documents,
605-
uri,
606-
meta_validation_rules,
607-
),
602+
diagnostics: diagnostics(documents, uri, settings),
608603
version: None,
609604
})
610605
});

0 commit comments

Comments
 (0)