diff --git a/.gitignore b/.gitignore
index bc78471..1699c0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -482,3 +482,105 @@ $RECYCLE.BIN/
# Vim temporary swap files
*.swp
+
+## ----------------------------------------------------------------------
+## .NET / Visual Studio / Rider
+## ----------------------------------------------------------------------
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# IDEs & Tooling
+.vs/
+.vscode/
+.idea/
+*.sln.iml
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+.dockerignore
+.env
+
+# Testing
+[Tt]est[Rr]esult*/
+BenchmarkDotNet.Artifacts/
+coverage*.json
+coverage*.xml
+*.coverage
+*.coveragexml
+
+## ----------------------------------------------------------------------
+## Python (Red Team Tools)
+## ----------------------------------------------------------------------
+
+# Byte-compiled / Optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# Virtual Environments
+.venv/
+venv/
+ENV/
+env/
+
+# Distribution / Packaging
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+## ----------------------------------------------------------------------
+## OS Specific
+## ----------------------------------------------------------------------
+.DS_Store
+Thumbs.db
+
+# ----------------------------------------------------------------------
+# Generated gRPC Python Files (Do Not Commit)
+# ----------------------------------------------------------------------
+attack/*_pb2.py
+attack/*_pb2_grpc.py
+attack/*.pyi
\ No newline at end of file
diff --git a/Rasp.Core.Tests/Rasp.Core.Tests.csproj b/Rasp.Core.Tests/Rasp.Core.Tests.csproj
new file mode 100644
index 0000000..34acbcb
--- /dev/null
+++ b/Rasp.Core.Tests/Rasp.Core.Tests.csproj
@@ -0,0 +1,25 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Rasp.Instrumentation.Grpc.Tests/Rasp.Instrumentation.Grpc.Tests.csproj b/Rasp.Instrumentation.Grpc.Tests/Rasp.Instrumentation.Grpc.Tests.csproj
new file mode 100644
index 0000000..1976419
--- /dev/null
+++ b/Rasp.Instrumentation.Grpc.Tests/Rasp.Instrumentation.Grpc.Tests.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Rasp.sln b/Rasp.sln
index 0e552e8..dfe45ce 100644
--- a/Rasp.sln
+++ b/Rasp.sln
@@ -27,6 +27,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibrarySystem.Domain", "mod
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibrarySystem.Persistence", "modules\dotnet-grpc-library-api\LibrarySystem.Persistence\LibrarySystem.Persistence.csproj", "{689C3555-DC1F-4A87-B349-500DF85B9F39}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7BF13981-E617-4FF6-9463-B251628BAF1E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rasp.Core.Tests", "Rasp.Core.Tests\Rasp.Core.Tests.csproj", "{9FF0212B-D43D-46D7-AAD4-3793958CA197}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rasp.Instrumentation.Grpc.Tests", "Rasp.Instrumentation.Grpc.Tests\Rasp.Instrumentation.Grpc.Tests.csproj", "{6F314CAD-E989-41CA-8C9C-9FD721F5DA50}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -145,6 +151,30 @@ Global
{689C3555-DC1F-4A87-B349-500DF85B9F39}.Release|x64.Build.0 = Release|Any CPU
{689C3555-DC1F-4A87-B349-500DF85B9F39}.Release|x86.ActiveCfg = Release|Any CPU
{689C3555-DC1F-4A87-B349-500DF85B9F39}.Release|x86.Build.0 = Release|Any CPU
+ {9FF0212B-D43D-46D7-AAD4-3793958CA197}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9FF0212B-D43D-46D7-AAD4-3793958CA197}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9FF0212B-D43D-46D7-AAD4-3793958CA197}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {9FF0212B-D43D-46D7-AAD4-3793958CA197}.Debug|x64.Build.0 = Debug|Any CPU
+ {9FF0212B-D43D-46D7-AAD4-3793958CA197}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {9FF0212B-D43D-46D7-AAD4-3793958CA197}.Debug|x86.Build.0 = Debug|Any CPU
+ {9FF0212B-D43D-46D7-AAD4-3793958CA197}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9FF0212B-D43D-46D7-AAD4-3793958CA197}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9FF0212B-D43D-46D7-AAD4-3793958CA197}.Release|x64.ActiveCfg = Release|Any CPU
+ {9FF0212B-D43D-46D7-AAD4-3793958CA197}.Release|x64.Build.0 = Release|Any CPU
+ {9FF0212B-D43D-46D7-AAD4-3793958CA197}.Release|x86.ActiveCfg = Release|Any CPU
+ {9FF0212B-D43D-46D7-AAD4-3793958CA197}.Release|x86.Build.0 = Release|Any CPU
+ {6F314CAD-E989-41CA-8C9C-9FD721F5DA50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6F314CAD-E989-41CA-8C9C-9FD721F5DA50}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6F314CAD-E989-41CA-8C9C-9FD721F5DA50}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {6F314CAD-E989-41CA-8C9C-9FD721F5DA50}.Debug|x64.Build.0 = Debug|Any CPU
+ {6F314CAD-E989-41CA-8C9C-9FD721F5DA50}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6F314CAD-E989-41CA-8C9C-9FD721F5DA50}.Debug|x86.Build.0 = Debug|Any CPU
+ {6F314CAD-E989-41CA-8C9C-9FD721F5DA50}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6F314CAD-E989-41CA-8C9C-9FD721F5DA50}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6F314CAD-E989-41CA-8C9C-9FD721F5DA50}.Release|x64.ActiveCfg = Release|Any CPU
+ {6F314CAD-E989-41CA-8C9C-9FD721F5DA50}.Release|x64.Build.0 = Release|Any CPU
+ {6F314CAD-E989-41CA-8C9C-9FD721F5DA50}.Release|x86.ActiveCfg = Release|Any CPU
+ {6F314CAD-E989-41CA-8C9C-9FD721F5DA50}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -160,5 +190,7 @@ Global
{D7B61D10-C142-47E1-99C6-31C582E330E9} = {87C876C9-7554-CEDF-203F-AF43B1B74702}
{BDCCB396-349D-42AF-990E-769813A6AC13} = {87C876C9-7554-CEDF-203F-AF43B1B74702}
{689C3555-DC1F-4A87-B349-500DF85B9F39} = {87C876C9-7554-CEDF-203F-AF43B1B74702}
+ {9FF0212B-D43D-46D7-AAD4-3793958CA197} = {7BF13981-E617-4FF6-9463-B251628BAF1E}
+ {6F314CAD-E989-41CA-8C9C-9FD721F5DA50} = {7BF13981-E617-4FF6-9463-B251628BAF1E}
EndGlobalSection
EndGlobal
diff --git a/attack/exploit_grpc.py b/attack/exploit_grpc.py
new file mode 100644
index 0000000..a7f5d28
--- /dev/null
+++ b/attack/exploit_grpc.py
@@ -0,0 +1,69 @@
+print("DEBUG: Script iniciando...") # <--- Adicione isso na linha 1
+import grpc
+import sys
+import os
+
+# Add current directory to path to import generated stubs
+sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+
+# Import generated modules (created by the build script)
+# Note: Names depend on how protoc generates files.
+# Assuming library.proto generates library_pb2.py and library_pb2_grpc.py
+try:
+ import library_pb2
+ import library_pb2_grpc
+except ImportError:
+ print("❌ Error: Compiled proto files not found.")
+ print("Run: bash generate_protos.sh")
+ sys.exit(1)
+
+def run_exploit(target):
+ print(f"[*] Connecting to target: {target}")
+
+ # Create insecure channel (for local PoC only)
+ with grpc.insecure_channel(target) as channel:
+ stub = library_pb2_grpc.LibraryStub(channel)
+
+ # --- SCENARIO 1: SQL Injection Payload ---
+ # This payload matches the regex defined in RegexDetectionEngine
+ payload = "' OR '1'='1"
+ print(f"[*] Sending malicious payload: \"{payload}\"")
+
+ try:
+ response = stub.CreateBook(library_pb2.CreateBookRequest(
+ title=payload, # <--- INJECTION POINT
+ author="Hacker",
+ publication_year=2025,
+ pages=100,
+ total_copies=10
+ ))
+ print("❌ FAILURE: Server accepted the payload! RASP did not block.")
+ print(f" Response: ID={response.id}, Title={response.title}")
+ return False
+
+ except grpc.RpcError as e:
+ # Check if blocked by RASP (PermissionDenied)
+ if e.code() == grpc.StatusCode.PERMISSION_DENIED:
+ print("✅ SUCCESS: RASP blocked the attack!")
+ print(f" Status: {e.code()}")
+ print(f" Details: {e.details()}")
+ return True
+ else:
+ print(f"⚠️ UNEXPECTED ERROR: Server returned an error, but not the expected block.")
+ print(f" Status: {e.code()}")
+ print(f" Details: {e.details()}")
+ return False
+
+if __name__ == '__main__':
+ # Default target: localhost:5001 (gRPC backend)
+ # Adjust if running via Docker or another port
+ target_url = 'localhost:5001'
+ if len(sys.argv) > 1:
+ target_url = sys.argv[1]
+
+ success = run_exploit(target_url)
+
+ if success:
+ sys.exit(0)
+ else:
+ sys.exit(1)
\ No newline at end of file
diff --git a/attack/generate_protos.sh b/attack/generate_protos.sh
new file mode 100644
index 0000000..e69de29
diff --git a/attack/requirements.txt b/attack/requirements.txt
new file mode 100644
index 0000000..2a8f38c
--- /dev/null
+++ b/attack/requirements.txt
@@ -0,0 +1,3 @@
+grpcio==1.60.0
+grpcio-tools==1.60.0
+protobuf==4.25.1
\ No newline at end of file
diff --git a/src/Rasp.Bootstrapper/Class1.cs b/src/Rasp.Bootstrapper/Class1.cs
deleted file mode 100644
index 45fb368..0000000
--- a/src/Rasp.Bootstrapper/Class1.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Rasp.Bootstrapper;
-
-public class Class1
-{
-
-}
diff --git a/src/Rasp.Bootstrapper/Configuration/RaspOptions.cs b/src/Rasp.Bootstrapper/Configuration/RaspOptions.cs
new file mode 100644
index 0000000..b78fa36
--- /dev/null
+++ b/src/Rasp.Bootstrapper/Configuration/RaspOptions.cs
@@ -0,0 +1,20 @@
+namespace Rasp.Bootstrapper.Configuration;
+
+///
+/// Global configuration options for the RASP.
+/// Allows adjusting security behavior without recompilation.
+///
+public class RaspOptions
+{
+ ///
+ /// If true, blocks detected attacks and throws an exception.
+ /// If false, only logs/monitors ("Audit" Mode).
+ /// Default: true (Secure by default).
+ ///
+ public bool BlockOnDetection { get; set; } = true;
+
+ ///
+ /// If true, enables detailed telemetry (may have performance impact).
+ ///
+ public bool EnableMetrics { get; set; } = true;
+}
\ No newline at end of file
diff --git a/src/Rasp.Bootstrapper/Rasp.Bootstrapper.csproj b/src/Rasp.Bootstrapper/Rasp.Bootstrapper.csproj
index b760144..c00d00f 100644
--- a/src/Rasp.Bootstrapper/Rasp.Bootstrapper.csproj
+++ b/src/Rasp.Bootstrapper/Rasp.Bootstrapper.csproj
@@ -6,4 +6,14 @@
enable
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Rasp.Bootstrapper/RaspDependencyInjection.cs b/src/Rasp.Bootstrapper/RaspDependencyInjection.cs
new file mode 100644
index 0000000..fd424c4
--- /dev/null
+++ b/src/Rasp.Bootstrapper/RaspDependencyInjection.cs
@@ -0,0 +1,41 @@
+using Grpc.AspNetCore.Server;
+using Microsoft.Extensions.DependencyInjection;
+using Rasp.Bootstrapper.Configuration;
+using Rasp.Core;
+using Rasp.Instrumentation.Grpc.Interceptors;
+
+namespace Rasp.Bootstrapper;
+
+///
+/// Provides extension methods to easily register RASP services.
+///
+public static class RaspDependencyInjection
+{
+ ///
+ /// Adds the RASP (Runtime Application Self-Protection) services to the DI container.
+ /// This method registers detection engines, telemetry, and gRPC interceptors.
+ ///
+ /// The application service collection.
+ /// Optional delegate to configure RASP behavior.
+ /// The service collection for chaining.
+ public static IServiceCollection AddRasp(
+ this IServiceCollection services,
+ Action? configureOptions = null)
+ {
+ if (configureOptions != null)
+ {
+ services.Configure(configureOptions);
+ }
+
+ services.AddRaspCore();
+
+ services.AddSingleton();
+
+ services.PostConfigure(options =>
+ {
+ options.Interceptors.Add();
+ });
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/src/Rasp.Core/Abstractions/IDetectionEngine.cs b/src/Rasp.Core/Abstractions/IDetectionEngine.cs
new file mode 100644
index 0000000..a96f133
--- /dev/null
+++ b/src/Rasp.Core/Abstractions/IDetectionEngine.cs
@@ -0,0 +1,20 @@
+using Rasp.Core.Models;
+
+namespace Rasp.Core.Abstractions;
+
+///
+/// Defines the contract for the core detection logic.
+/// Implementations of this interface are responsible for analyzing payloads
+/// and determining if they contain malicious patterns (e.g., SQLi, XSS).
+///
+public interface IDetectionEngine
+{
+ ///
+ /// Analyzes a generic text payload for potential threats.
+ /// This is the primary entry point for string-based inspections (gRPC fields, SQL queries).
+ ///
+ /// The content to inspect (e.g., user input, SQL command text).
+ /// Optional context about the source (e.g., "gRPC.BookService/CreateBook").
+ /// A indicating the verdict.
+ DetectionResult Inspect(string? payload, string context = "Unknown");
+}
\ No newline at end of file
diff --git a/src/Rasp.Core/DependencyInjection.cs b/src/Rasp.Core/DependencyInjection.cs
index 7d9bf29..c244a62 100644
--- a/src/Rasp.Core/DependencyInjection.cs
+++ b/src/Rasp.Core/DependencyInjection.cs
@@ -1,6 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Rasp.Core.Abstractions;
+using Rasp.Core.Engine;
using Rasp.Core.Telemetry;
namespace Rasp.Core;
@@ -14,7 +15,8 @@ public static class DependencyInjection
public static IServiceCollection AddRaspCore(this IServiceCollection services)
{
services.TryAddSingleton();
-
+ services.TryAddSingleton();
+
return services;
}
}
\ No newline at end of file
diff --git a/src/Rasp.Core/Engine/RegexDetectionEngine.cs b/src/Rasp.Core/Engine/RegexDetectionEngine.cs
new file mode 100644
index 0000000..12bfa36
--- /dev/null
+++ b/src/Rasp.Core/Engine/RegexDetectionEngine.cs
@@ -0,0 +1,43 @@
+using System.Text.RegularExpressions;
+using Rasp.Core.Abstractions;
+using Rasp.Core.Enums;
+using Rasp.Core.Models;
+
+namespace Rasp.Core.Engine;
+
+///
+/// A basic detection engine based on Regular Expressions.
+/// NOTE: In a real-world scenario, this would be much more sophisticated.
+/// For the PoC, we focus on blocking the most obvious SQLi patterns.
+///
+public partial class RegexDetectionEngine : IDetectionEngine
+{
+ // Otimização: Source Generator para Regex (Zero-Allocation na inicialização)
+ // Bloqueia: ' OR '1'='1 (e variações simples)
+ [GeneratedRegex(@"(?i)'\s*OR\s*'\d+'\s*=\s*'\d+", RegexOptions.Compiled | RegexOptions.CultureInvariant)]
+ private static partial Regex BasicSqlInjectionPattern();
+
+ public DetectionResult Inspect(string? payload, string context = "Unknown")
+ {
+ if (string.IsNullOrEmpty(payload))
+ {
+ return DetectionResult.Safe();
+ }
+
+ // 1. Check for Basic SQL Injection
+ if (BasicSqlInjectionPattern().IsMatch(payload))
+ {
+ return DetectionResult.Threat(
+ threatType: "SQL Injection",
+ description: "Detected basic SQLi tautology pattern.",
+ severity: ThreatSeverity.High,
+ confidence: 1.0,
+ matchedPattern: "BasicSqlInjectionPattern"
+ );
+ }
+
+ // 2. Future: Add more patterns here (XSS, RCE)
+
+ return DetectionResult.Safe();
+ }
+}
\ No newline at end of file
diff --git a/src/Rasp.Core/Enums/ThreatSeverity.cs b/src/Rasp.Core/Enums/ThreatSeverity.cs
new file mode 100644
index 0000000..2ead483
--- /dev/null
+++ b/src/Rasp.Core/Enums/ThreatSeverity.cs
@@ -0,0 +1,32 @@
+namespace Rasp.Core.Enums;
+
+///
+/// Defines the severity levels for detected threats.
+///
+public enum ThreatSeverity
+{
+ ///
+ /// Informational - suspicious but not necessarily malicious.
+ ///
+ Info = 0,
+
+ ///
+ /// Low severity - minor security concern.
+ ///
+ Low = 1,
+
+ ///
+ /// Medium severity - potential security issue.
+ ///
+ Medium = 2,
+
+ ///
+ /// High severity - likely attack attempt.
+ ///
+ High = 3,
+
+ ///
+ /// Critical severity - confirmed attack with high confidence.
+ ///
+ Critical = 4
+}
\ No newline at end of file
diff --git a/src/Rasp.Core/Models/DetectionResult.cs b/src/Rasp.Core/Models/DetectionResult.cs
new file mode 100644
index 0000000..2961311
--- /dev/null
+++ b/src/Rasp.Core/Models/DetectionResult.cs
@@ -0,0 +1,65 @@
+using Rasp.Core.Enums;
+
+namespace Rasp.Core.Models;
+
+public sealed record DetectionResult
+{
+ // OTIMIZAÇÃO: Cache da instância "Safe" para evitar alocação no hot path (99% das requisições).
+ private static readonly DetectionResult _safeInstance = new() { IsThreat = false };
+
+ ///
+ /// Indicates whether a threat was detected.
+ ///
+ public required bool IsThreat { get; init; }
+
+ ///
+ /// The type of threat detected (e.g., "SQL Injection", "XSS").
+ /// Null if no threat was detected.
+ ///
+ public string? ThreatType { get; init; }
+
+ ///
+ /// A detailed description of the detected threat.
+ ///
+ public string? Description { get; init; }
+
+ ///
+ /// The confidence level of the detection (0.0 to 1.0).
+ ///
+ public double Confidence { get; init; } = 1.0;
+
+ ///
+ /// The specific pattern or rule that triggered the detection.
+ ///
+ public string? MatchedPattern { get; init; }
+
+ ///
+ /// The severity level of the threat.
+ ///
+ public ThreatSeverity Severity { get; init; } = ThreatSeverity.Medium;
+
+ ///
+ /// Returns a cached result indicating no threat was detected.
+ /// ZERO ALLOCATION call.
+ ///
+ public static DetectionResult Safe() => _safeInstance;
+
+ ///
+ /// Creates a result indicating a threat was detected.
+ /// Allocations here are acceptable as we are likely about to block the request anyway.
+ ///
+ public static DetectionResult Threat(
+ string threatType,
+ string description,
+ ThreatSeverity severity = ThreatSeverity.High,
+ double confidence = 1.0,
+ string? matchedPattern = null) => new()
+ {
+ IsThreat = true,
+ ThreatType = threatType,
+ Description = description,
+ Severity = severity,
+ Confidence = confidence,
+ MatchedPattern = matchedPattern
+ };
+}
\ No newline at end of file
diff --git a/src/Rasp.Instrumentation.Grpc/Class1.cs b/src/Rasp.Instrumentation.Grpc/Class1.cs
deleted file mode 100644
index 51c2e64..0000000
--- a/src/Rasp.Instrumentation.Grpc/Class1.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Rasp.Instrumentation.Grpc;
-
-public class Class1
-{
-
-}
diff --git a/src/Rasp.Instrumentation.Grpc/Interceptors/SecurityInterceptor.cs b/src/Rasp.Instrumentation.Grpc/Interceptors/SecurityInterceptor.cs
new file mode 100644
index 0000000..94af490
--- /dev/null
+++ b/src/Rasp.Instrumentation.Grpc/Interceptors/SecurityInterceptor.cs
@@ -0,0 +1,61 @@
+using System.Diagnostics;
+using Grpc.Core;
+using Grpc.Core.Interceptors;
+using Rasp.Core.Abstractions;
+
+namespace Rasp.Instrumentation.Grpc.Interceptors;
+
+///
+/// The main RASP barrier for gRPC services.
+/// Intercepts every unary call, inspects the payload, and decides whether to proceed.
+///
+public class SecurityInterceptor : Interceptor
+{
+ private readonly IDetectionEngine _detectionEngine;
+ private readonly IRaspMetrics _metrics;
+
+ public SecurityInterceptor(IDetectionEngine detectionEngine, IRaspMetrics metrics)
+ {
+ _detectionEngine = detectionEngine;
+ _metrics = metrics;
+ }
+
+ public override async Task UnaryServerHandler(
+ TRequest request,
+ ServerCallContext context,
+ UnaryServerMethod continuation)
+ {
+ var sw = Stopwatch.StartNew();
+ var method = context.Method; // e.g., "/Library.Library/GetBookById"
+
+ try
+ {
+ // --- 1. INSPECTION PHASE ---
+ // For MVP: Convert request to string (Naive approach - Phase 2 optimization target)
+ // Warning: request.ToString() in Protobuf usually returns the JSON representation.
+ // This allocates memory! We will optimize this with Source Generators later.
+ string payload = request?.ToString() ?? string.Empty;
+
+ var result = _detectionEngine.Inspect(payload, method);
+
+ if (result.IsThreat)
+ {
+ // --- 2. BLOCKING PHASE ---
+ _metrics.ReportThreat("gRPC", result.ThreatType!, blocked: true);
+
+ // Fail Fast with PermissionDenied (or InvalidArgument)
+ throw new RpcException(new Status(
+ StatusCode.PermissionDenied,
+ $"RASP Security Alert: {result.Description}"));
+ }
+
+ // --- 3. EXECUTION PHASE ---
+ return await continuation(request, context);
+ }
+ finally
+ {
+ sw.Stop();
+ _metrics.RecordInspection("gRPC", sw.Elapsed.TotalMilliseconds);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Rasp.Instrumentation.Grpc/Rasp.Instrumentation.Grpc.csproj b/src/Rasp.Instrumentation.Grpc/Rasp.Instrumentation.Grpc.csproj
index b760144..f49a197 100644
--- a/src/Rasp.Instrumentation.Grpc/Rasp.Instrumentation.Grpc.csproj
+++ b/src/Rasp.Instrumentation.Grpc/Rasp.Instrumentation.Grpc.csproj
@@ -6,4 +6,12 @@
enable
+
+
+
+
+
+
+
+