Skip to content

Security Model

Provara’s security policy follows a simple principle: deny by default, allow by category.

Command → DENY check (first) → ALLOW check → CWD check → Execute
↓ match = blocked ↓ no match = blocked

Every command must:

  1. Not match any deny pattern
  2. Match at least one allow pattern
  3. Execute from an approved working directory

Deny patterns are checked first. If any pattern matches (via re.search()), the command is immediately blocked — regardless of allow patterns.

There are 26 deny rules organized into 7 categories:

r"(?i)\b(schtasks|bcdedit|diskpart|cipher|takeown|icacls)\b"
r"(?i)\b(net\s+user|net\s+localgroup|sc\.exe|wmic)\b"
r"(?i)\breg\s+(add|delete|import|export|save|restore)\b"

Blocks: Task scheduling, boot configuration, disk management, file encryption, ownership changes, ACL modification, user management, service control, WMI, and registry modification.

r"(?i)\b(Add-MpPreference|Set-MpPreference|Remove-MpPreference)\b"
r"(?i)\b(Disable-WindowsOptionalFeature|Enable-WindowsOptionalFeature)\b"
r"(?i)(powershell|pwsh)(\.exe)?\s+.*-[eE]nc"
r"(?i)\b(frombase64string|iex|Invoke-Expression)\b"
r"(?i)\bStart-Process\b"

Blocks: Defender manipulation, Windows feature toggling, encoded commands, base64 execution, Invoke-Expression, and Start-Process.

r"(?i)\b(Invoke-WebRequest|curl\s|wget\s|bitsadmin|Start-BitsTransfer)\b"
r"(?i)\bInvoke-RestMethod\s+https?://(?!127\.0\.0\.1|localhost)"
r"(?i)\bInvoke-Command\s+-Computer"
r"(?i)\bEnter-PSSession\b"

Blocks: Web downloads, remote API calls (except localhost), remote command execution, and remote PowerShell sessions.

r"(?i)\b(Get-Credential|ConvertTo-SecureString|ConvertFrom-SecureString)\b"
r"(?i)\b(Get-ItemProperty|Set-ItemProperty)\s+(HKLM:|HKCU:|Registry::)"

Blocks: Credential prompts, secure string operations, and registry access via *-ItemProperty.

Destructive Operations (outside workspace)

Section titled “Destructive Operations (outside workspace)”
r"(?i)\b(Remove-Item|del\s|erase\s|rmdir)\b(?!.*[/\\]workspace[/\\])"
r"(?i)\bClear-Content\b(?!.*[/\\]workspace[/\\])"

Blocks: File/directory deletion and content clearing outside the workspace/ directory. Deletions within workspace are permitted.

r"(?i)\bOut-File\b(?!.*[/\\]agent-hub[/\\])"
r"(?i)\bExport-\w+\b(?!.*[/\\]agent-hub[/\\])"

Blocks: Writing files and exporting data outside the project directory.

r"(?i)\$env:\w+\s*="
r"(?i)\bSet-Variable\s+-Name\s+env:"

Blocks: All environment variable modifications.

After passing deny checks, a command must match at least one allow pattern (via re.fullmatch() with IGNORECASE).

Allow patterns are organized into 9 categories:

r"^(whoami|hostname|pwd|systeminfo)$"
r"^(Get-Date|\$PSVersionTable|\[Environment\]::OSVersion)$"
r"^git\s+--version$"
r"^echo\s+.+$"
r"^Write-Output\s+.+$"
r"^(dir|ls|Get-ChildItem)(\s+.*)?$"
r"^(type|cat|Get-Content)\s+.+$"
r"^(Test-Path|Resolve-Path|Get-Item)\s+.+$"
r"^Get-Process(\s+.*)?$"
r"^tasklist(\s+.*)?$"
r"^Get-Service(\s+.*)?$"

Category 5: Network Diagnostics (localhost only)

Section titled “Category 5: Network Diagnostics (localhost only)”
r"^Test-NetConnection\s+(127\.0\.0\.1|localhost)\s+-Port\s+\d+$"
r"^ping\s+(127\.0\.0\.1|localhost)(\s+.+)?$"
r"^.*python(\.exe)?\s+.*[/\\]workspace[/\\](inbox|outbox)[/\\].+\.py(\s+.*)?$"
r"^powershell(\.exe)?\s+.*-File\s+.*[/\\]workspace[/\\](inbox|outbox)[/\\].+\.ps1(\s+.*)?$"

Only scripts in workspace/inbox/ or workspace/outbox/ can be executed.

r"^New-Item\s+.*[/\\]workspace[/\\].+$"
r"^(Set-Content|Add-Content)\s+.*[/\\]workspace[/\\].+$"
r"^Remove-Item\s+.*[/\\]workspace[/\\].+$"

File creation, modification, and deletion — but only within workspace/.

r"^git\s+(status|log|diff|branch|remote|show|rev-parse|config\s+--get)(\s+.*)?$"

Only read-only git operations. No push, commit, checkout, or reset.

r"^Invoke-RestMethod\s+https?://(127\.0\.0\.1|localhost)(:\d+)?/\w+.*$"

Allows REST calls to local services only.

Even after passing deny and allow checks, the command’s working directory must be under the project root:

def is_path_under_project(path: str) -> bool:
resolved = Path(path).resolve()
project = _PROJECT_ROOT.resolve()
return str(resolved).lower().startswith(str(project).lower())

This prevents path traversal attacks where a command might operate on system directories.

For testing and debugging, patterns can be added at runtime:

from src.server.exec.policy import add_allow_pattern, add_deny_pattern
# Add a temporary allow pattern
add_allow_pattern(r"^my-custom-command\s+.+$")
# Add a temporary deny pattern (inserted at position 0)
add_deny_pattern(r"(?i)\bmy-dangerous-cmd\b")

All patterns are validated at import time:

def _validate_patterns():
for name, patterns in [("ALLOW", ALLOW), ("DENY", DENY)]:
for p in patterns:
try:
re.compile(p)
except re.error as e:
raise ValueError(f"Invalid {name} pattern: {p} - {e}")

If any pattern has invalid regex syntax, the server will fail to start with a clear error message.