ข้ามไปที่เนื้อหา

Security

OpenJarvis มีชั้นความปลอดภัยที่สแกน prompt และผลลัพธ์ของโมเดลหา secret, ข้อมูลระบุตัวตนส่วนบุคคล (PII) และ path ไฟล์ที่ sensitive ระบบนี้ออกแบบให้ ประกอบกันได้: scanner รันเป็น pipeline และ wrapper GuardrailsEngine วางไว้ หน้า inference backend ใดๆ ก็ได้ โดยไม่ต้องเปลี่ยนวิธีทำงานของโค้ดส่วนที่เหลือ

การรีวิวความปลอดภัยสามชั้น

OpenJarvis แยกท่าทีของ host, ขอบเขตข้อมูลระดับแอปพลิเคชัน และ guardrail prompt ตอน runtime ออกจากกัน:

ชั้น คำสั่ง / component สิ่งที่ตรวจสอบ
Host scan jarvis scan การเข้ารหัสดิสก์, cloud-sync agent, port ของ engine ที่เปิดออก, เครื่องมือเข้าถึงระยะไกล
Data-boundary scan jarvis scan --data-boundaries Inference, memory, trace, channel, tools และที่เก็บข้อมูลในเครื่องที่ตั้งค่าไว้
Runtime guardrails GuardrailsEngine / BoundaryGuard Secret, PII และการละเมิดนโยบายไฟล์ใน prompt และผลลัพธ์แบบ live

ใช้ host scan ก่อนเก็บข้อมูล sensitive ไว้บนเครื่อง ใช้ data-boundary scan เพื่อยืนยันว่า config.toml ของคุณเป็นแบบ local-only, รองรับ cloud หรือแบบผสม ใช้ BoundaryGuard ระหว่าง inference เมื่อคุณต้องการ redact หรือบล็อกแบบ live

ดู Data Boundary Scan สำหรับเครื่องมือวินิจฉัย config ของแอปพลิเคชัน และ BoundaryGuard ด้านล่างสำหรับการสแกน ตอน runtime


ภาพรวม

โมดูลความปลอดภัยมีองค์ประกอบสี่ตัวที่ใช้งานแยกอิสระได้:

  • GuardrailsEngine


    ห่อ InferenceEngine ใดๆ ด้วยการสแกนก่อนและหลังเรียก รองรับโหมด WARN, REDACT และ BLOCK

    ไปที่ GuardrailsEngine

  • SecretScanner


    ตรวจจับ API key, token, password และ connection string ในข้อความ

    ไปที่ SecretScanner

  • PIIScanner


    ตรวจจับอีเมล, เลขประกันสังคม (SSN), หมายเลขบัตรเครดิต, เบอร์โทรศัพท์ และ IP สาธารณะ

    ไปที่ PIIScanner

  • File Policy


    บล็อกการเข้าถึง .env, *.pem, id_rsa และไฟล์ credential อื่นๆ

    ไปที่ File Policy


GuardrailsEngine

GuardrailsEngine ห่อ InferenceEngine ใดๆ และสแกนทั้งข้อความ input และ เนื้อหา output มันไม่ได้ลงทะเบียนใน EngineRegistry — คุณสร้างมันโดยตรง ด้วยการห่อ instance ของ engine ที่มีอยู่แล้ว

โหมด

โหมด ค่าคงที่ พฤติกรรม
Warn RedactionMode.WARN เผยแพร่ event SECURITY_ALERT แต่ปล่อยข้อความผ่านโดยไม่เปลี่ยนแปลง ค่าเริ่มต้น
Redact RedactionMode.REDACT แทนที่สิ่งที่ตรงกันด้วย [REDACTED:pattern_name] ก่อนส่งเข้า/ออกจาก engine
Block RedactionMode.BLOCK สร้าง SecurityBlockError ทันทีเมื่อพบสิ่งที่ตรงกัน

การใช้งานพื้นฐาน

warn_mode.py
from openjarvis.engine.ollama import OllamaEngine
from openjarvis.security.guardrails import GuardrailsEngine
from openjarvis.security.types import RedactionMode
from openjarvis.core.types import Message, Role

engine = OllamaEngine()
guarded = GuardrailsEngine(engine)  # (1)!

messages = [Message(role=Role.USER, content="My API key is sk-abc123xyz")]
response = guarded.generate(messages, model="qwen3:8b")
# The key is logged as a warning but the text is passed unchanged
print(response["content"])
  1. ค่าเริ่มต้นคือ mode=RedactionMode.WARN, scan_input=True, scan_output=True
redact_mode.py
from openjarvis.engine.ollama import OllamaEngine
from openjarvis.security.guardrails import GuardrailsEngine
from openjarvis.security.types import RedactionMode
from openjarvis.core.types import Message, Role

engine = OllamaEngine()
guarded = GuardrailsEngine(engine, mode=RedactionMode.REDACT)  # (1)!

messages = [Message(role=Role.USER, content="My key is sk-abc123xyz, help me debug")]
response = guarded.generate(messages, model="qwen3:8b")
# Input sent to engine: "My key is [REDACTED:openai_key], help me debug"
  1. Pattern ที่ sensitive ในข้อความ input จะถูกแทนที่ก่อนถึงโมเดล
block_mode.py
from openjarvis.engine.ollama import OllamaEngine
from openjarvis.security.guardrails import GuardrailsEngine, SecurityBlockError
from openjarvis.security.types import RedactionMode
from openjarvis.core.types import Message, Role

engine = OllamaEngine()
guarded = GuardrailsEngine(engine, mode=RedactionMode.BLOCK)

try:
    messages = [Message(role=Role.USER, content="AKIA1234567890ABCDEF")]
    guarded.generate(messages, model="qwen3:8b")
except SecurityBlockError as exc:
    print(f"Blocked: {exc}")
    # Blocked: Security scan blocked input: 1 finding(s) detected

พารามิเตอร์ของ Constructor

พารามิเตอร์ ชนิด ค่าเริ่มต้น คำอธิบาย
engine InferenceEngine — Inference engine ที่ถูกห่อ
scanners list[BaseScanner] [SecretScanner(), PIIScanner()] Scanner ที่จะรัน
mode RedactionMode WARN การกระทำเมื่อพบสิ่งที่ตรงกัน
scan_input bool True สแกนข้อความ input
scan_output bool True สแกนเนื้อหา output
bus EventBus None Event bus สำหรับ event ด้านความปลอดภัย

การผสานกับ Event Bus

เมื่อระบุ bus ไว้ GuardrailsEngine จะเผยแพร่ event ทุกครั้งที่มีผลการสแกน:

Event เมื่อไหร่
SECURITY_ALERT พบสิ่งที่ตรงกันในโหมด WARN หรือ REDACT
SECURITY_BLOCK พบสิ่งที่ตรงกันในโหมด BLOCK

คุณ subscribe event เหล่านี้ด้วย AuditLogger เพื่อสร้าง security event log แบบถาวรได้ ดู Audit Logger ด้านล่าง

Scanner กำหนดเอง

คุณส่งชุด subclass ของ BaseScanner ใดๆ เพื่อจำกัดหรือขยายการสแกนได้:

custom_scanners.py
from openjarvis.security.guardrails import GuardrailsEngine
from openjarvis.security.scanner import SecretScanner
from openjarvis.security.types import RedactionMode

# Only scan for secrets, skip PII
guarded = GuardrailsEngine(
    engine,
    scanners=[SecretScanner()],
    mode=RedactionMode.REDACT,
)

Streaming

สำหรับการเรียกแบบ streaming GuardrailsEngine.stream() จะส่ง token แบบ real-time แล้วสแกนย้อนหลัง (post-hoc) บนผลลัพธ์ที่สะสมไว้เพื่อการบันทึก log เนื่องจาก token ถูกส่งให้ผู้เรียกไปแล้วก่อนที่การสแกนจะเสร็จ โหมด BLOCK จะ ใช้ได้เฉพาะฝั่ง input ระหว่าง streaming เท่านั้น

Streaming กับโหมด BLOCK

SecurityBlockError จะถูก raise ได้เฉพาะก่อนที่ stream จะเริ่ม (สำหรับการสแกน input) เท่านั้น การบล็อก output ระหว่าง streaming เป็นไปไม่ได้ — ใช้โหมด REDACT หากคุณต้องทำความสะอาดผลลัพธ์ของโมเดลในสถานการณ์ streaming


SecretScanner

SecretScanner ตรวจจับ API key, token, password และ credential อื่นๆ ด้วย regex pattern แต่ละ pattern มี ThreatLevel กำกับไว้

เอกสารอ้างอิง Pattern

ชื่อ Pattern ระดับภัยคุกคาม สิ่งที่ตรงกัน
openai_key CRITICAL sk- ตามด้วยตัวอักษร/ตัวเลข 20+ ตัว
anthropic_key CRITICAL sk-ant- ตามด้วยตัวอักษร 20+ ตัว
aws_access_key CRITICAL AKIA ตามด้วยตัวพิมพ์ใหญ่/ตัวเลข 16 ตัว
github_token CRITICAL ghp_, gho_, ghs_, ghr_, github_pat_ ตามด้วยตัวอักษร 36+ ตัว
stripe_key CRITICAL sk_live_, sk_test_, pk_live_, pk_test_ ตามด้วยตัวอักษร 20+ ตัว
private_key CRITICAL header ของ PEM private key -----BEGIN PRIVATE KEY-----
password_assignment HIGH password = "...", passwd: "..." ฯลฯ
db_connection_string HIGH URL แบบ postgres://, mysql://, mongodb://, redis://
slack_token HIGH xoxb-, xoxp-, xoxo-, xoxr-, xoxs- ตามด้วย token
generic_api_key HIGH api_key = "...", secret_key = "...", auth_token = "..."

การใช้งานโดยตรง

secret_scanner.py
from openjarvis.security.scanner import SecretScanner

scanner = SecretScanner()

# Scan text
result = scanner.scan("My key is sk-abc123xyz789 and it is secret")
print(result.clean)           # False
print(result.highest_threat)  # ThreatLevel.CRITICAL
for finding in result.findings:
    print(f"  {finding.pattern_name}: {finding.description} at [{finding.start}:{finding.end}]")

# Redact text
clean = scanner.redact("Token: sk-abc123xyz789")
print(clean)  # Token: [REDACTED:openai_key]

PIIScanner

PIIScanner ตรวจจับข้อมูลระบุตัวตนส่วนบุคคลด้วย regex pattern ที่ปรับให้ เหมาะกับรูปแบบทั่วไปของสหรัฐฯ

เอกสารอ้างอิง Pattern

ชื่อ Pattern ระดับภัยคุกคาม สิ่งที่ตรงกัน
us_ssn CRITICAL เลขประกันสังคมรูปแบบ XXX-XX-XXXX
credit_card_visa CRITICAL หมายเลขบัตร Visa (16 หลัก ขึ้นต้นด้วย 4)
credit_card_mastercard CRITICAL หมายเลขบัตร Mastercard (16 หลัก ขึ้นต้นด้วย 51–55)
credit_card_amex CRITICAL หมายเลขบัตร Amex (15 หลัก ขึ้นต้นด้วย 34 หรือ 37)
email MEDIUM ที่อยู่อีเมลมาตรฐาน
us_phone MEDIUM เบอร์โทรศัพท์สหรัฐฯ ในรูปแบบทั่วไป
ipv4_public LOW ที่อยู่ IPv4 สาธารณะ (ไม่รวมช่วง RFC1918)

ที่อยู่ IP แบบส่วนตัว

Pattern ipv4_public ตั้งใจไม่รวมช่วงส่วนตัว (10.x.x.x, 172.16–31.x.x, 192.168.x.x, 127.x.x.x) ที่อยู่ IP ภายในไม่ถูกจัดว่า sensitive เป็นค่าเริ่มต้น

การใช้งานโดยตรง

pii_scanner.py
from openjarvis.security.scanner import PIIScanner

scanner = PIIScanner()

text = "Contact john@example.com or call 555-867-5309"
result = scanner.scan(text)

for finding in result.findings:
    print(f"{finding.pattern_name}: threat={finding.threat_level.value}")
# email: threat=medium
# us_phone: threat=medium

clean = scanner.redact(text)
print(clean)
# Contact [REDACTED:email] or call [REDACTED:us_phone]

File Policy

โมดูล file policy ป้องกันการเข้าถึงไฟล์ credential และ key มันถูกใช้ภายใน โดย FileReadTool และเส้นทางการนำเข้าของ memory แต่คุณใช้มันโดยตรงก็ได้

Pattern ไฟล์ที่ Sensitive

frozenset DEFAULT_SENSITIVE_PATTERNS มี glob pattern ต่อไปนี้:

Pattern คำอธิบาย
.env, .env.*, *.env ไฟล์ environment variable
.secret, *.secrets ไฟล์ secret ทั่วไป
credentials.* ไฟล์ credential
*.pem, *.key ใบรับรอง TLS/SSL และ private key
*.p12, *.pfx, *.jks ไฟล์ PKCS และ Java keystore
id_rsa, id_ed25519 ไฟล์ SSH private key
.htpasswd ไฟล์ password ของ Apache
.pgpass ไฟล์ password ของ PostgreSQL
.netrc ไฟล์ credential ของ FTP/SSH

การใช้งาน

file_policy.py
from pathlib import Path
from openjarvis.security.file_policy import is_sensitive_file, filter_sensitive_paths

# Check a single file
print(is_sensitive_file(".env"))           # True
print(is_sensitive_file("server.key"))     # True
print(is_sensitive_file("README.md"))      # False

# Filter a list of paths
paths = [
    Path("README.md"),
    Path(".env"),
    Path("src/main.py"),
    Path("server.pem"),
]
safe = filter_sensitive_paths(paths)
print(safe)  # [PosixPath('README.md'), PosixPath('src/main.py')]

การผสานกับ FileReadTool

FileReadTool ในตัวเรียก is_sensitive_file() โดยอัตโนมัติก่อนอ่าน path ใดๆ ความพยายามอ่านไฟล์ sensitive จะสร้าง error แทนที่จะคืนเนื้อหาไฟล์ พฤติกรรมนี้ปิดไม่ได้ในระดับ tool — ตั้งค่าเอเจนต์ให้ไม่มี FileReadTool หากคุณต้องการการเข้าถึงไฟล์แบบไม่จำกัด


Audit Logger

AuditLogger บันทึก security event ลงฐานข้อมูล SQLite แบบ append-only มัน subscribe event bus เพื่อจับ event โดยอัตโนมัติได้ หรือคุณเรียก log() ด้วย ตนเองก็ได้

การผสานกับ Event Bus (อัตโนมัติ)

audit_bus.py
from openjarvis.core.events import EventBus
from openjarvis.security.audit import AuditLogger
from openjarvis.security.guardrails import GuardrailsEngine
from openjarvis.security.types import RedactionMode
from openjarvis.engine.ollama import OllamaEngine

bus = EventBus()

# AuditLogger subscribes to SECURITY_SCAN, SECURITY_ALERT, SECURITY_BLOCK
audit = AuditLogger(db_path="~/.openjarvis/audit.db", bus=bus)

engine = OllamaEngine()
guarded = GuardrailsEngine(
    engine,
    mode=RedactionMode.WARN,
    bus=bus,
)

# Security events are now persisted automatically

การบันทึกด้วยตนเอง

audit_manual.py
import time
from openjarvis.security.audit import AuditLogger
from openjarvis.security.types import SecurityEvent, SecurityEventType

audit = AuditLogger(db_path="./audit.db")

event = SecurityEvent(
    event_type=SecurityEventType.SECRET_DETECTED,
    timestamp=time.time(),
    findings=[],
    content_preview="sk-...",
    action_taken="redacted",
)
audit.log(event)

การ Query Audit Log

audit_query.py
from openjarvis.security.audit import AuditLogger

audit = AuditLogger(db_path="~/.openjarvis/audit.db")

# Recent events
events = audit.query(limit=20)

# Filter by event type
secret_events = audit.query(event_type="secret_detected")

# Filter by time range
import time
recent = audit.query(since=time.time() - 3600)  # last hour

# Count total events
print(f"Total events: {audit.count()}")

audit.close()

Configuration

การตั้งค่าความปลอดภัยอยู่ในหมวด [security] ของ ~/.openjarvis/config.toml

~/.openjarvis/config.toml
[security]
enabled = true
scan_input = true
scan_output = true
mode = "warn"               # "warn" | "redact" | "block"
secret_scanner = true
pii_scanner = true
audit_log_path = "~/.openjarvis/audit.db"
enforce_tool_confirmation = true

เอกสารอ้างอิง Configuration

คีย์ ชนิด ค่าเริ่มต้น คำอธิบาย
enabled bool true เปิดใช้งานระบบย่อยความปลอดภัย
scan_input bool true สแกนข้อความ input ของผู้ใช้
scan_output bool true สแกนเนื้อหา output ของโมเดล
mode str "warn" การกระทำเมื่อพบสิ่งที่ตรงกัน: warn, redact หรือ block
secret_scanner bool true รัน SecretScanner บนข้อความทั้งหมด
pii_scanner bool true รัน PIIScanner บนข้อความทั้งหมด
audit_log_path str ~/.openjarvis/audit.db Path ของ audit log แบบ SQLite
enforce_tool_confirmation bool true config loader ยอมรับค่านี้ได้แต่ ยังไม่บังคับใช้จริงในตอนนี้ ดู System Access ว่าเมื่อไหร่ prompt จะเกิดขึ้นจริง

เริ่มจาก Warn แล้วค่อยเข้มงวดขึ้น

mode = "warn" เป็นจุดเริ่มต้นที่ดี ช่วยให้คุณสังเกตว่า pattern ไหนถูก trigger โดยไม่รบกวนการใช้งานปกติ สลับไปเป็น "redact" เมื่อคุณมั่นใจแล้วว่า scanner ไม่ได้สร้าง false positive มากเกินไปสำหรับงานของคุณ


เขียน Scanner กำหนดเอง

Implement BaseScanner แล้วส่ง instance ให้ GuardrailsEngine:

custom_scanner.py
import re
from openjarvis.security._stubs import BaseScanner
from openjarvis.security.types import ScanFinding, ScanResult, ThreatLevel


class InternalUrlScanner(BaseScanner):
    """Detect internal service URLs that should not be shared externally."""

    scanner_id = "internal_urls"

    PATTERN = re.compile(r"https?://internal\.[a-z0-9.-]+\.[a-z]{2,}")

    def scan(self, text: str) -> ScanResult:
        findings = []
        for match in self.PATTERN.finditer(text):
            findings.append(ScanFinding(
                pattern_name="internal_url",
                matched_text=match.group(0),
                threat_level=ThreatLevel.MEDIUM,
                start=match.start(),
                end=match.end(),
                description="Internal service URL",
            ))
        return ScanResult(findings=findings)

    def redact(self, text: str) -> str:
        return self.PATTERN.sub("[REDACTED:internal_url]", text)


# Use with GuardrailsEngine
from openjarvis.security.guardrails import GuardrailsEngine
from openjarvis.security.types import RedactionMode

guarded = GuardrailsEngine(
    engine,
    scanners=[InternalUrlScanner()],
    mode=RedactionMode.REDACT,
)

Data Boundary Scan

ดู Data Boundary Scan สำหรับเครื่องมือวินิจฉัย config ของแอปพลิเคชัน (jarvis scan --data-boundaries)


ดูเพิ่มเติม

  • Data Boundary Scan — เครื่องมือวินิจฉัย config ของแอปพลิเคชันและที่เก็บข้อมูลในเครื่อง (jarvis scan --data-boundaries)
  • Architecture: Security — การออกแบบ pipeline, การไหลของ event และการผสาน file policy
  • API Reference: Security — signature ของคลาสและฟังก์ชันแบบเต็ม
  • Tools — FileReadTool ใช้ file policy อย่างไร
  • Configuration — เอกสารอ้างอิง config แบบเต็ม