infralayer 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
infralayer/__init__.py ADDED
@@ -0,0 +1,107 @@
1
+ """
2
+ InfraLayer Python SDK
3
+ =====================
4
+
5
+ Pre-LLM security filtering, sanitization, and telemetry client.
6
+ Supports both local (in-process) and remote (API) modes.
7
+
8
+ Quick Start:
9
+ from infralayer import InfraLayer, create_local_infralayer, create_remote_infralayer
10
+
11
+ # Local mode (zero-dependency, in-process)
12
+ infra = create_local_infralayer()
13
+ result = await infra.sanitize(
14
+ "What is our PTO policy?",
15
+ user_context={"user_id": "user-123", "tenant_id": "tenant-456", "role": "employee", "permissions": ["read:documents"]}
16
+ )
17
+
18
+ # Remote mode (API)
19
+ infra = create_remote_infralayer(
20
+ api_url="https://api.infralayer.io/v1",
21
+ api_key="il_api_abc123...",
22
+ default_user_context={"user_id": "service-user", "tenant_id": "tenant-456", "role": "system", "permissions": ["read:all"]}
23
+ )
24
+ result = await infra.sanitize("What is our PTO policy?")
25
+ """
26
+
27
+ from .client import InfraLayer, create_infralayer
28
+ from .factory import create_local_infralayer, create_remote_infralayer
29
+ from .types import (
30
+ UserContext,
31
+ RawContext,
32
+ SanitizedContext,
33
+ SanitizeOptions,
34
+ SanitizeOptionsInner,
35
+ SanitizeResult,
36
+ PermissionEvaluation,
37
+ InjectionDetail,
38
+ InjectionPattern,
39
+ SecurityFlag,
40
+ SanitizeTelemetry,
41
+ SanitizationReason,
42
+ InjectionCategory,
43
+ InjectionSeverity,
44
+ SecurityFlagSeverity,
45
+ InfraLayerConfig,
46
+ SanitizeRequestPayload,
47
+ SanitizeResponse,
48
+ AuthRequestPayload,
49
+ AuthResponse,
50
+ HealthResponse,
51
+ )
52
+ from .exceptions import (
53
+ InfraLayerError,
54
+ AuthenticationError,
55
+ RateLimitError,
56
+ ValidationError,
57
+ SanitizationError,
58
+ PermissionDeniedError,
59
+ ConnectionError,
60
+ TimeoutError,
61
+ LocalModeError,
62
+ RemoteModeError,
63
+ )
64
+
65
+ __version__ = "0.2.0"
66
+
67
+ __all__ = [
68
+ # Main client
69
+ "InfraLayer",
70
+ # Factory functions
71
+ "create_infralayer",
72
+ "create_local_infralayer",
73
+ "create_remote_infralayer",
74
+ # Types
75
+ "UserContext",
76
+ "RawContext",
77
+ "SanitizedContext",
78
+ "SanitizeOptions",
79
+ "SanitizeOptionsInner",
80
+ "SanitizeResult",
81
+ "PermissionEvaluation",
82
+ "InjectionDetail",
83
+ "InjectionPattern",
84
+ "SecurityFlag",
85
+ "SanitizeTelemetry",
86
+ "SanitizationReason",
87
+ "InjectionCategory",
88
+ "InjectionSeverity",
89
+ "SecurityFlagSeverity",
90
+ "InfraLayerConfig",
91
+ "SanitizeRequestPayload",
92
+ "SanitizeResponse",
93
+ "AuthRequestPayload",
94
+ "AuthResponse",
95
+ "HealthResponse",
96
+ # Exceptions
97
+ "InfraLayerError",
98
+ "AuthenticationError",
99
+ "RateLimitError",
100
+ "ValidationError",
101
+ "SanitizationError",
102
+ "PermissionDeniedError",
103
+ "ConnectionError",
104
+ "TimeoutError",
105
+ "LocalModeError",
106
+ "RemoteModeError",
107
+ ]
infralayer/cli.py ADDED
@@ -0,0 +1,285 @@
1
+ """
2
+ InfraLayer Python SDK - CLI
3
+ ============================
4
+
5
+ Command-line interface for InfraLayer.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import asyncio
11
+ import json
12
+ import sys
13
+ from typing import Optional
14
+
15
+ import typer
16
+ from rich.console import Console
17
+ from rich.json import JSON
18
+ from rich.panel import Panel
19
+ from rich.syntax import Syntax
20
+
21
+ from . import (
22
+ create_local_infralayer,
23
+ create_remote_infralayer,
24
+ InfraLayer,
25
+ UserContext,
26
+ RawContext,
27
+ SanitizeOptionsInner,
28
+ )
29
+
30
+ app = typer.Typer(
31
+ name="infralayer",
32
+ help="InfraLayer CLI - Pre-LLM security filtering and sanitization",
33
+ add_completion=False,
34
+ )
35
+ console = Console()
36
+
37
+
38
+ @app.command()
39
+ def sanitize(
40
+ text: str = typer.Argument(..., help="Text to sanitize"),
41
+ user_id: str = typer.Option(..., "--user-id", "-u", help="User ID"),
42
+ tenant_id: str = typer.Option(..., "--tenant-id", "-t", help="Tenant ID"),
43
+ role: str = typer.Option("employee", "--role", "-r", help="User role"),
44
+ permissions: str = typer.Option("", "--permissions", "-p", help="Comma-separated permissions"),
45
+ department: Optional[str] = typer.Option(None, "--department", "-d", help="User department"),
46
+ access_level: Optional[int] = typer.Option(None, "--access-level", "-a", help="User access level"),
47
+ remote: bool = typer.Option(False, "--remote", help="Use remote API mode"),
48
+ api_url: Optional[str] = typer.Option(None, "--api-url", help="Remote API URL"),
49
+ api_key: Optional[str] = typer.Option(None, "--api-key", help="Remote API key"),
50
+ strict: bool = typer.Option(False, "--strict", help="Enable strict mode"),
51
+ json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
52
+ ) -> None:
53
+ """Sanitize text using InfraLayer."""
54
+ user_context = UserContext(
55
+ user_id=user_id,
56
+ tenant_id=tenant_id,
57
+ role=role,
58
+ department=department,
59
+ access_level=access_level,
60
+ permissions=permissions.split(",") if permissions else [],
61
+ )
62
+
63
+ if remote:
64
+ if not api_url or not api_key:
65
+ console.print("[red]Error: --api-url and --api-key required for remote mode[/red]")
66
+ sys.exit(1)
67
+ infra = create_remote_infralayer(api_url, api_key, user_context)
68
+ else:
69
+ infra = create_local_infralayer()
70
+
71
+ options = SanitizeOptionsInner(strict_mode=strict)
72
+
73
+ async def run() -> None:
74
+ try:
75
+ result = await infra.sanitize(text, user_context, options)
76
+ if json_output:
77
+ console.print(JSON.from_data(result.model_dump()))
78
+ else:
79
+ _print_result(result)
80
+ except Exception as e:
81
+ console.print(f"[red]Error: {e}[/red]")
82
+ sys.exit(1)
83
+ finally:
84
+ await infra.close()
85
+
86
+ asyncio.run(run())
87
+
88
+
89
+ @app.command()
90
+ def scan(
91
+ text: str = typer.Argument(..., help="Text to scan for injections"),
92
+ json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
93
+ ) -> None:
94
+ """Scan text for injection attempts (local mode only)."""
95
+ infra = create_local_infralayer()
96
+
97
+ async def run() -> None:
98
+ try:
99
+ details = await infra.scan_for_injections(text)
100
+ if json_output:
101
+ console.print(JSON.from_data([d.model_dump() for d in details]))
102
+ else:
103
+ _print_injection_details(details)
104
+ except Exception as e:
105
+ console.print(f"[red]Error: {e}[/red]")
106
+ sys.exit(1)
107
+ finally:
108
+ await infra.close()
109
+
110
+ asyncio.run(run())
111
+
112
+
113
+ @app.command()
114
+ def check_permission(
115
+ user_id: str = typer.Option(..., "--user-id", "-u", help="User ID"),
116
+ tenant_id: str = typer.Option(..., "--tenant-id", "-t", help="Tenant ID"),
117
+ role: str = typer.Option("employee", "--role", "-r", help="User role"),
118
+ permissions: str = typer.Option("", "--permissions", "-p", help="Comma-separated user permissions"),
119
+ required_permissions: str = typer.Option(..., "--required", help="Comma-separated required permissions"),
120
+ required_role: Optional[str] = typer.Option(None, "--required-role", help="Required role"),
121
+ required_access_level: Optional[int] = typer.Option(None, "--required-access-level", help="Required access level"),
122
+ required_department: Optional[str] = typer.Option(None, "--required-department", help="Required department"),
123
+ json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
124
+ ) -> None:
125
+ """Check if user has permission for specific action (local mode only)."""
126
+ user_context = UserContext(
127
+ user_id=user_id,
128
+ tenant_id=tenant_id,
129
+ role=role,
130
+ permissions=permissions.split(",") if permissions else [],
131
+ )
132
+
133
+ infra = create_local_infralayer()
134
+
135
+ async def run() -> None:
136
+ try:
137
+ result = await infra.check_permission(
138
+ user_context,
139
+ required_permissions.split(",") if required_permissions else [],
140
+ required_role,
141
+ required_access_level,
142
+ required_department,
143
+ )
144
+ if json_output:
145
+ console.print(JSON.from_data(result.model_dump()))
146
+ else:
147
+ _print_permission_result(result)
148
+ except Exception as e:
149
+ console.print(f"[red]Error: {e}[/red]")
150
+ sys.exit(1)
151
+ finally:
152
+ await infra.close()
153
+
154
+ asyncio.run(run())
155
+
156
+
157
+ @app.command()
158
+ def health(
159
+ remote: bool = typer.Option(False, "--remote", help="Check remote API health"),
160
+ api_url: Optional[str] = typer.Option(None, "--api-url", help="Remote API URL"),
161
+ api_key: Optional[str] = typer.Option(None, "--api-key", help="Remote API key"),
162
+ json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
163
+ ) -> None:
164
+ """Check health status."""
165
+ if remote:
166
+ if not api_url or not api_key:
167
+ console.print("[red]Error: --api-url and --api-key required for remote mode[/red]")
168
+ sys.exit(1)
169
+ infra = create_remote_infralayer(api_url, api_key)
170
+ else:
171
+ infra = create_local_infralayer()
172
+
173
+ async def run() -> None:
174
+ try:
175
+ result = await infra.health_check()
176
+ if json_output:
177
+ console.print(JSON.from_data(result.model_dump()))
178
+ else:
179
+ _print_health(result)
180
+ except Exception as e:
181
+ console.print(f"[red]Error: {e}[/red]")
182
+ sys.exit(1)
183
+ finally:
184
+ await infra.close()
185
+
186
+ asyncio.run(run())
187
+
188
+
189
+ @app.command()
190
+ def version() -> None:
191
+ """Show version information."""
192
+ from . import __version__
193
+ console.print(f"InfraLayer Python SDK v{__version__}")
194
+
195
+
196
+ def _print_result(result) -> None:
197
+ """Print sanitization result in human-readable format."""
198
+ if result.sanitized_context:
199
+ ctx = result.sanitized_context
200
+ console.print(Panel(
201
+ f"[green]Success[/green]\n\n"
202
+ f"Original length: {ctx.original_length}\n"
203
+ f"Sanitized length: {ctx.sanitized_length}\n"
204
+ f"Segments removed: {len(ctx.removed_segments)}\n"
205
+ f"Injections detected: {'Yes' if ctx.injection_detected else 'No'}\n"
206
+ f"Processing time: {result.telemetry.processing_time_ms}ms",
207
+ title="Sanitization Result",
208
+ border_style="green",
209
+ ))
210
+
211
+ if ctx.removed_segments:
212
+ console.print("\n[bold]Removed Segments:[/bold]")
213
+ for seg in ctx.removed_segments:
214
+ console.print(f" [{seg.reason.value}] {seg.original_text[:50]}...")
215
+
216
+ if ctx.injection_details:
217
+ console.print("\n[bold]Injection Details:[/bold]")
218
+ for det in ctx.injection_details:
219
+ console.print(f" [{det.severity.value}] {det.category.value}: {det.match}")
220
+
221
+ console.print("\n[bold]Sanitized Text:[/bold]")
222
+ console.print(Syntax(ctx.text, "text", theme="monokai", line_numbers=False))
223
+
224
+ if result.permission_evaluation:
225
+ perm = result.permission_evaluation
226
+ style = "green" if perm.allowed else "red"
227
+ console.print(Panel(
228
+ f"Allowed: {'Yes' if perm.allowed else 'No'}\n"
229
+ f"Reason: {perm.reason}\n"
230
+ f"Required: {', '.join(perm.required_permissions) or 'None'}\n"
231
+ f"User has: {', '.join(perm.user_permissions) or 'None'}",
232
+ title="Permission Evaluation",
233
+ border_style=style,
234
+ ))
235
+
236
+
237
+ def _print_injection_details(details: list) -> None:
238
+ """Print injection scan results."""
239
+ if not details:
240
+ console.print(Panel("No injections detected", title="Scan Result", border_style="green"))
241
+ return
242
+
243
+ console.print(Panel(
244
+ f"[red]Found {len(details)} injection attempt(s)[/red]",
245
+ title="Injection Scan",
246
+ border_style="red",
247
+ ))
248
+ for det in details:
249
+ console.print(f" [{det.severity.value}] {det.category.value}: {det.match} (pos: {det.position})")
250
+
251
+
252
+ def _print_permission_result(result) -> None:
253
+ """Print permission check result."""
254
+ style = "green" if result.allowed else "red"
255
+ console.print(Panel(
256
+ f"Allowed: {'Yes' if result.allowed else 'No'}\n"
257
+ f"Reason: {result.reason}\n"
258
+ f"Required permissions: {', '.join(result.required_permissions) or 'None'}\n"
259
+ f"User permissions: {', '.join(result.user_permissions) or 'None'}\n"
260
+ f"Missing: {', '.join(result.missing_permissions or []) or 'None'}",
261
+ title="Permission Check",
262
+ border_style=style,
263
+ ))
264
+
265
+
266
+ def _print_health(result) -> None:
267
+ """Print health check result."""
268
+ style = "green" if result.status == "healthy" else ("yellow" if result.status == "degraded" else "red")
269
+ console.print(Panel(
270
+ f"Status: {result.status}\n"
271
+ f"Version: {result.version}\n"
272
+ f"Timestamp: {result.timestamp}\n"
273
+ f"Checks: {', '.join(f'{k}: {v}' for k, v in result.checks.items())}",
274
+ title="Health Check",
275
+ border_style=style,
276
+ ))
277
+
278
+
279
+ def main() -> None:
280
+ """Entry point for CLI."""
281
+ app()
282
+
283
+
284
+ if __name__ == "__main__":
285
+ main()