diff --git a/AppInspector.RulesEngine/Schema/RuleSchemaProvider.cs b/AppInspector.RulesEngine/Schema/RuleSchemaProvider.cs index c3214d70..d0902414 100644 --- a/AppInspector.RulesEngine/Schema/RuleSchemaProvider.cs +++ b/AppInspector.RulesEngine/Schema/RuleSchemaProvider.cs @@ -35,6 +35,34 @@ public RuleSchemaProvider(string? customSchemaPath = null) } } + /// + /// Private constructor for factory methods + /// + private RuleSchemaProvider(JsonSchema schema) + { + _schema = schema; + } + + /// + /// Create a schema provider from schema content string + /// + /// The JSON schema content as a string + /// A new RuleSchemaProvider instance + public static RuleSchemaProvider FromSchemaContent(string schemaContent) + { + if (schemaContent is null) + { + throw new ArgumentNullException(nameof(schemaContent)); + } + if (string.IsNullOrWhiteSpace(schemaContent)) + { + throw new ArgumentException("Schema content cannot be empty or whitespace.", nameof(schemaContent)); + } + + var schema = LoadSchemaFromContent(schemaContent); + return new RuleSchemaProvider(schema); + } + private JsonSchema LoadEmbeddedSchema() { var assembly = Assembly.GetExecutingAssembly(); @@ -42,13 +70,18 @@ private JsonSchema LoadEmbeddedSchema() ?? throw new InvalidOperationException($"Could not find embedded schema resource: {SCHEMA_RESOURCE_NAME}"); using var reader = new StreamReader(stream); var schemaJson = reader.ReadToEnd(); - return JsonSchema.FromText(schemaJson); + return LoadSchemaFromContent(schemaJson); } private JsonSchema LoadSchemaFromFile(string path) { var schemaJson = File.ReadAllText(path); - return JsonSchema.FromText(schemaJson); + return LoadSchemaFromContent(schemaJson); + } + + private static JsonSchema LoadSchemaFromContent(string schemaContent) + { + return JsonSchema.FromText(schemaContent); } /// diff --git a/AppInspector.Tests/RuleProcessor/SchemaValidationTests.cs b/AppInspector.Tests/RuleProcessor/SchemaValidationTests.cs index ea7c18b1..03df7e2f 100644 --- a/AppInspector.Tests/RuleProcessor/SchemaValidationTests.cs +++ b/AppInspector.Tests/RuleProcessor/SchemaValidationTests.cs @@ -26,6 +26,92 @@ public void SchemaProvider_LoadsEmbeddedSchema() Assert.NotNull(schema); } + [Fact] + public void SchemaProvider_FromSchemaContent_LoadsSchema() + { + // Load embedded schema content first to use as test data + var defaultProvider = new RuleSchemaProvider(); + var schemaJson = JsonSerializer.Serialize(defaultProvider.GetSchema()); + + // Create provider from schema content + var provider = RuleSchemaProvider.FromSchemaContent(schemaJson); + var schema = provider.GetSchema(); + + Assert.NotNull(schema); + } + + [Fact] + public void SchemaProvider_FromSchemaContent_WithNullContent_ThrowsException() + { + Assert.Throws(() => RuleSchemaProvider.FromSchemaContent(null)); + } + + [Fact] + public void SchemaProvider_FromSchemaContent_WithEmptyContent_ThrowsException() + { + Assert.Throws(() => RuleSchemaProvider.FromSchemaContent(string.Empty)); + } + + [Fact] + public void SchemaProvider_FromSchemaContent_ValidatesRules() + { + // Use a minimal schema that requires id and name + var minimalSchema = @"{ + ""$schema"": ""http://json-schema.org/draft-07/schema#"", + ""type"": ""array"", + ""items"": { + ""type"": ""object"", + ""required"": [""id"", ""name"", ""tags"", ""patterns""], + ""properties"": { + ""id"": { ""type"": ""string"", ""minLength"": 1 }, + ""name"": { ""type"": ""string"" }, + ""description"": { ""type"": ""string"" }, + ""tags"": { + ""type"": ""array"", + ""minItems"": 1, + ""items"": { ""type"": ""string"" } + }, + ""patterns"": { + ""type"": ""array"", + ""minItems"": 1, + ""items"": { + ""type"": ""object"", + ""required"": [""pattern"", ""type""], + ""properties"": { + ""pattern"": { ""type"": ""string"" }, + ""type"": { ""type"": ""string"" } + } + } + } + } + } + }"; + + var provider = RuleSchemaProvider.FromSchemaContent(minimalSchema); + + var validRule = new Rule + { + Id = "TEST001", + Name = "Test Rule", + Description = "A test rule", + Tags = new[] { "Test.Tag" }, + Patterns = new[] + { + new SearchPattern + { + Pattern = "test", + PatternType = PatternType.String, + Scopes = new[] { PatternScope.Code }, + Confidence = Confidence.High + } + } + }; + + var result = provider.ValidateRule(validRule); + + Assert.True(result.IsValid); + } + [Fact] public void ValidRule_PassesSchemaValidation() {