Python: NAMED_ARG_REGEX character-class bug [${1}] lets non-$-prefixed values ({, 1, }) be silently parsed as variable references
メンテナーはふだん 2 日以内に返信
まだ誰も着手していません。
評価
調査の方向性
python/semantic_kernel/template_engine/blocks/named_arg_block.py の21行目から始め、列挙されている NamedArgBlock のケースを再現して正規表現の動作を確認してください。変数形式のマッチングを更新し、リテラルの $ プレフィックスだけが受け入れられるようにしてから、$var は引き続きパースされ、1var、{var、}var は NamedArgBlockSyntaxError を発生させることを確認してください。
索引モデルが issue の本文から書いたものです。
説明
What happens
NamedArgBlock's parsing regex, NAMED_ARG_REGEX, is supposed to require a named argument's variable form to start with a literal $ (per the grammar: [parameter] ::= [variable] | [value] and [variable] ::= "$" [valid-name]). Because of a regex mistake, it instead accepts a variable reference whose first character is $, {, 1, or } and always treats the rest as the variable name, silently dropping that first character. This means unquoted, non-$-prefixed argument text that starts with 1, {, or } is silently reinterpreted as a variable substitution instead of raising a syntax error, and the value actually used at render time comes from an unrelated kernel argument instead of the text the template author wrote.
Where
python/semantic_kernel/template_engine/blocks/named_arg_block.py, line 21:
NAMED_ARG_REGEX = r"^(?P<name>[0-9A-Za-z_]+)[=]{1}(?P<value>[${1}](?P<var_name>[0-9A-Za-z_]+)|(?P<quote>[\"'])(?P<val>.[^\"^']*)(?P=quote))$"
The variable-form alternative is [${1}](?P<var_name>...). Inside a character class, {1} is not a quantifier, it is three literal characters {, 1, }. So [${1}] is a 4-character class matching any one of $, {, 1, } — not "a literal $, exactly once" as the {1} clearly was intended to express (that would need to be written outside the brackets, e.g. \$ or [$]).
Why
Confirmed directly against the regex and the real class, on semantic-kernel==1.44.1 (byte-identical to current main for this file):
from semantic_kernel.template_engine.blocks.named_arg_block import NamedArgBlock
for content in ["arg1=$var", "arg1={var", "arg1=1var", "arg1=}var"]:
b = NamedArgBlock(content=content)
print(content, "-> name=", b.name, "variable=", b.variable)
Output:
arg1=$var -> name= arg1 variable= content='$var' name='var'
arg1={var -> name= arg1 variable= content='{var' name='var'
arg1=1var -> name= arg1 variable= content='1var' name='var'
arg1=}var -> name= arg1 variable= content='}var' name='var'
All four are accepted and all four produce a VarBlock pointing at the variable named var, even though only the first one actually starts with $. The {, 1, and } prefixes are silently swallowed instead of causing a NamedArgBlockSyntaxError (which is what happens for any other non-$/non-quote prefix, e.g. arg1=Xvar is correctly rejected).
Full end-to-end repro (real rendering, not just the regex)
import asyncio
from semantic_kernel import Kernel
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.functions.kernel_function_decorator import kernel_function
from semantic_kernel.prompt_template.kernel_prompt_template import KernelPromptTemplate
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
class MyPlugin:
@kernel_function(name="echo")
def echo(self, arg1: str) -> str:
return f"GOT[{arg1}]"
kernel = Kernel()
kernel.add_plugin(MyPlugin(), plugin_name="my")
async def main():
args = KernelArguments(var="SECRET_VALUE")
template_str = "{{ my.echo arg1=1var }}" # arg1 is meant to be the literal text "1var"
tpl = KernelPromptTemplate(prompt_template_config=PromptTemplateConfig(template=template_str))
print(await tpl.render(kernel, args))
asyncio.run(main())
Output:
GOT[SECRET_VALUE]
Expected vs actual
- Expected:
arg1=1vardoes not match the[variable]grammar rule ("$" [valid-name]), so it should either be rejected withNamedArgBlockSyntaxError(consistent witharg1=Xvar, which is correctly rejected) or, if unquoted plain-text arguments were meant to be legal, treated as the literal text1var. Either wayarg1should never end up holding the value of an unrelated kernel argument calledvar. - Actual: the call silently succeeds and
arg1is substituted withargs["var"]("SECRET_VALUE"), not the text the template author wrote. The same happens for any unquoted, non-quoted value that happens to start with{or}.
Suggested fix
Move the quantifier out of the character class so only a literal $ starts the variable form, e.g.:
NAMED_ARG_REGEX = r"^(?P<name>[0-9A-Za-z_]+)=(?P<value>\$(?P<var_name>[0-9A-Za-z_]+)|(?P<quote>[\"'])(?P<val>.[^\"^']*)(?P=quote))$"
Environment
semantic-kernel1.44.1 (PyPI), verified byte-for-byte identical againstpython/semantic_kernel/template_engine/blocks/named_arg_block.pyon the currentmainbranch.- Python 3.13, Windows.
- 主要言語
- C#
- スター
- 28.6k
- フォーク
- 4.8k
- 平均マージ
- 12時間 20分
- マージ済み PR(30日)
- 12
環境構築
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
microsoft/semantic-kernel のほかの issue
-
python triage
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
microsoft/semantic-kernel#14490 · コメント 1 件 ·
メンテナーはふだん 2 日以内に返信
-
Python: [Python] structured_outputs_transform reuses ChatHistory across calls (prompt pollution)オープンpython triage
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
microsoft/semantic-kernel#14483 · コメント 1 件 ·
メンテナーはふだん 2 日以内に返信
-
.NET python triage
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
microsoft/semantic-kernel#14482 · コメント 2 件 ·
メンテナーはふだん 2 日以内に返信
-
python triage
難易度 2/5 1〜3時間 初心者へのやさしさ 85/100
microsoft/semantic-kernel#14481 ·
メンテナーはふだん 2 日以内に返信
-
python triage
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
microsoft/semantic-kernel#14460 ·
メンテナーはふだん 2 日以内に返信
microsoft/semantic-kernel の issue をすべて見る
似ている issue
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
fluentassertions/fluentassertions#3353 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
-
難易度 2/5 半日 初心者へのやさしさ 78/100
unoplatform/uno#24769 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
AvaloniaUI/Avalonia#22323 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
microsoft/onnxruntime-genai#2633 ·
メンテナーはふだん 1 日以内に返信