Python: docs: add Vaultak runtime security integration (filter sample + guide)#14043
Python: docs: add Vaultak runtime security integration (filter sample + guide)#14043samueloladji-beep wants to merge 3 commits into
Conversation
Adds a security integration guide and a runnable Python sample showing how to wire Vaultak into Semantic Kernel using the native filter system (FunctionInvocationFilter + AutoFunctionInvocationFilter). Every plugin call is risk-scored before execution and PII is masked in outputs. - docs/VAULTAK_SECURITY.md — integration guide with quick-start, configuration table, event-coverage table, and links - python/samples/concepts/filtering/vaultak_security_filter.py — end-to-end sample with MathPlugin and TimePlugin Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds a runnable example and documentation for integrating Vaultak runtime security with Semantic Kernel filters to risk-score, enforce policy, and mask PII during tool/function invocation.
Changes:
- Added a Python sample demonstrating
FunctionInvocationFilterandAutoFunctionInvocationFilterwith Vaultak scoring, policy checks, and output masking. - Added documentation describing the integration, setup, and a “quick start” code snippet.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
| python/samples/concepts/filtering/vaultak_security_filter.py | New end-to-end sample wiring Vaultak into SK filters and demonstrating a short conversation. |
| docs/VAULTAK_SECURITY.md | New integration guide explaining Vaultak + SK filters with install steps and quick start snippet. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Collect function arguments as context for the risk scorer | ||
| args_context = {k: str(v) for k, v in (context.arguments or {}).items()} | ||
|
|
||
| result = vt.score_action(action=action, context=args_context) |
| ) | ||
|
|
||
| # Check against configured policy rules | ||
| vt.check_policy(tool_name=action, input_data=str(args_context)) |
| # Scan the function output for PII before it propagates | ||
| if context.result and context.result.value: | ||
| raw_output = str(context.result.value) | ||
| context.result._value = vt.mask_pii(raw_output) |
| if result.score >= RISK_THRESHOLD: | ||
| raise KernelFunctionCancelledError( | ||
| f"[Vaultak] Function '{action}' blocked — risk score {result.score:.1f}/10 " | ||
| f"exceeds threshold {RISK_THRESHOLD}. Review at app.vaultak.com" | ||
| ) |
| VAULTAK_API_KEY = os.environ.get("VAULTAK_API_KEY", "vtk_...") | ||
| RISK_THRESHOLD = 7.0 # Block function calls with a risk score >= this value | ||
|
|
||
| vt = Vaultak(api_key=VAULTAK_API_KEY, agent_name="sk-agent") |
| context: FunctionInvocationContext, | ||
| next: Callable[[FunctionInvocationContext], Coroutine[Any, Any, None]], | ||
| ) -> None: | ||
| action = f"{context.function.plugin_name}-{context.function.name}" |
| vt.check_policy(tool_name=action, input_data=str(context.arguments)) | ||
| await next(context) | ||
| if context.result and context.result.value: | ||
| context.result._value = vt.mask_pii(str(context.result.value)) |
| | `kernel.invoke()` (explicit call) | Risk-scores the plugin + function; blocks if above threshold | | ||
| | Auto function call selected by LLM | Risk-scores; terminates auto-invocation loop if above threshold | | ||
| | Plugin output returned | Scans for PII and masks before the result propagates | | ||
| | Policy check fails | Raises exception with dashboard URL | |
- Wrap synchronous vt.score_action(), vt.check_policy(), and vt.mask_pii() calls with asyncio.to_thread() to avoid blocking the event loop - Replace context.result._value (private attr) with the public FunctionResult constructor: context.result = FunctionResult(...) - Update threshold error messages to say "meets or exceeds" to accurately reflect the >= operator - Raise ValueError when VAULTAK_API_KEY env var is missing instead of silently defaulting to a placeholder string - Fix plugin_name None fallback in docs quick start to use `or 'kernel'` consistent with the sample file - Add dashboard URL to exception message in docs quick start snippet Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@microsoft-github-policy-service agree |
There was a problem hiding this comment.
Automated Code Review
Reviewers: 4 | Confidence: 95%
✗ Correctness
The sample file imports
KernelFunctionCancelledErrorfromsemantic_kernel.exceptions, but this class does not exist anywhere in the Semantic Kernel Python package. The only cancellation-related exception isOperationCancelledException. This will cause anImportErrorat module load time, making the sample completely non-functional. The same non-existent import appears in the documentation quick-start snippet.
✓ Security Reliability
The sample and docs import
KernelFunctionCancelledErrorwhich does not exist in thesemantic_kernel.exceptionsmodule (verified: onlyOperationCancelledExceptionexists), causing an ImportError at load time. Additionally, the PII masking viacontext.result._value = ...is silently non-functional:FunctionResultis a Pydantic v2 model where the field isvalue, not_value; setting_valuecreates an unused attribute without changing the result propagated downstream. This means the advertised PII protection does not actually work.
✓ Test Coverage
This PR adds a Vaultak security filter sample and documentation but provides zero test coverage. Every other filtering sample in
python/samples/concepts/filtering/is registered inpython/tests/samples/test_concepts.pyas a parameterized integration test case. The newvaultak_security_filter.pyis not added there, nor does it have any unit tests with mocked Vaultak dependencies. Given thatvaultakis an external third-party package not in the project's dependencies, the sample cannot even be imported without it — meaning CI would fail if it were naively added to test_concepts.py. At minimum, unit tests with mockedvaultakcalls should verify the filter logic (blocking on high scores, PII masking on output, terminate behavior).
✗ Design Approach
The main design issue is that the sample wires Vaultak into
FUNCTION_INVOCATION, which in Semantic Kernel also wraps prompt functions. That makes this integration risk-score and potentially blockkernel.invoke_prompt(...)turns before any plugin/tool call happens, which is broader than the PR describes and changes the behavior of the sample conversation itself.
Flagged Issues
-
KernelFunctionCanceledErrordoes not exist insemantic_kernel.exceptions(or anywhere in the SK Python package). The only cancellation exception isOperationCancelledException(defined inpython/semantic_kernel/exceptions/kernel_exceptions.py:55). Both the sample and the docs will fail withImportErrorat import time. -
vaultak_function_filteris attached at theFUNCTION_INVOCATIONlayer, which also covers prompt functions. Sincechat()useskernel.invoke_prompt(...)for every user turn, andinvoke_prompt()creates aKernelFunctionFromPromptthat goes throughself.invoke(...)(which always runs function-invocation filters), a high-risk-looking prompt can be blocked before any plugin function call or LM-selected tool call occurs. This is broader than the documented integration scope.
Suggestions
- Gate the Vaultak
FUNCTION_INVOCATIONfilter so it only applies to real plugin/tool functions (e.g., checkcontext.function.plugin_nameis not None), or move the blocking logic for tool use to the auto-function path, so the sample matches the stated 'plugin function call / LM tool selection' scope in the documentation.
Automated review by samueloladji-beep's agents
| from semantic_kernel.connectors.ai import FunctionChoiceBehavior | ||
| from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion, OpenAIChatPromptExecutionSettings | ||
| from semantic_kernel.contents import ChatHistory | ||
| from semantic_kernel.core_plugins import MathPlugin, TimePlugin |
There was a problem hiding this comment.
KernelFunctionCancelledError does not exist in semantic_kernel.exceptions. The module's __all__ exports only: KernelException, KernelFunctionAlreadyExistsError, KernelFunctionNotFoundError, KernelInvokeException, KernelPluginInvalidConfigurationError, KernelPluginNotFoundError, KernelServiceNotFoundError, OperationCancelledException. This import will raise ImportError at module load, making the sample completely non-functional.
| from semantic_kernel.core_plugins import MathPlugin, TimePlugin | |
| from semantic_kernel.exceptions import OperationCancelledException |
| _api_key = os.environ.get("VAULTAK_API_KEY") | ||
| if not _api_key: | ||
| raise ValueError( | ||
| "VAULTAK_API_KEY environment variable is not set. " |
There was a problem hiding this comment.
Same KernelFunctionCanceledError import issue as the sample — this class does not exist. The docs quick-start will fail at import. Use OperationCancelledException (or a custom subclass).
| "VAULTAK_API_KEY environment variable is not set. " | |
| from semantic_kernel.exceptions import OperationCancelledException |
| kernel.add_service(OpenAIChatCompletion(service_id="chat")) | ||
| kernel.add_plugin(MathPlugin(), plugin_name="math") | ||
| kernel.add_plugin(TimePlugin(), plugin_name="time") | ||
|
|
There was a problem hiding this comment.
Using FUNCTION_INVOCATION here makes Vaultak run on prompt functions too, not just plugin/tool calls. chat() drives every turn through kernel.invoke_prompt(...) (lines 123-129), which builds a prompt function and calls self.invoke(...) (kernel.py:248-255), always executing function-invocation filters. A risky user prompt can therefore be blocked before any plugin function call happens, which is broader than the docs describe.
- Replace KernelFunctionCancelledError (doesn't exist) with OperationCancelledException - Add plugin_name is None guard to FunctionInvocationFilter so only real plugin/tool calls are risk-scored, not inline prompt invocations Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@dluc @craigomatic @glahaye — ready for review. This adds a Vaultak runtime security filter sample and guide using SK's |
Summary
docs/VAULTAK_SECURITY.md— a concise integration guide explaining how to wire Vaultak into Semantic Kernel using the native filter system, with a quick-start snippet, configuration table, and event-coverage table.python/samples/concepts/filtering/vaultak_security_filter.py— a runnable end-to-end sample (MathPlugin + TimePlugin) demonstrating both filter types.Integration pattern: Vaultak registers as a
FunctionInvocationFilterand anAutoFunctionInvocationFilter. Every plugin function call is risk-scored (0–10) before it executes; calls above the threshold raiseKernelFunctionCancelledError(explicit invocations) or setcontext.terminate = True(auto function calling). Plugin outputs are passed throughmask_pii()before they propagate to the next step.Install
Get an API key at vaultak.com.
Test plan
python/samples/concepts/filtering/vaultak_security_filter.pywithOPENAI_API_KEYandVAULTAK_API_KEYsetKernelFunctionCancelledErrorsurfaces cleanly to the callerdocs/VAULTAK_SECURITY.mdrenders correctly on GitHub🤖 Generated with Claude Code