Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/strategy-release-runtime-contract.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ instrument/action 范围、policy_ref 和显式环境。账户真实映射由下
禁止传入账户选择器、账户别名、主机、凭据或自由文本。
目标和 release 使用既有公开构建器校验,control 使用既有 RuntimeControl 校验,
且必须 disabled。控制储备不是确认的预算。LIVE 只能表示请求意图,不能启用。
release_id 沿用发布合同的 3–128 字符格式,允许字母或数字开头及点、下划线、连字符;
strategy_revision 为 1–64 字符,允许相同字符及数字开头的提交版本。
两者均拒绝空白、路径分隔符、控制字符和超长输入,并保留共享敏感信息检查。
初始 revision 为 1;后续 revision 必须引用前一 subject 摘要。
摘要绑定内容,不认证所有者,也不匿名化真实资源。

Expand Down
7 changes: 4 additions & 3 deletions src/quant_platform_kit/common/automation_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ def _sha(value):
_require(isinstance(value, str) and re.fullmatch('[a-f0-9]{64}', value) is not None)


def _label(value):
def _label(value, pattern='[A-Za-z][A-Za-z0-9_-]{0,63}'):
# Contract labels, never account selectors, URLs, free text or hostnames.
_require(isinstance(value, str) and re.fullmatch('[A-Za-z][A-Za-z0-9_-]{0,63}', value) is not None)
_require(isinstance(value, str) and re.fullmatch(pattern, value) is not None)
_require(redact_sensitive_text(value) == value)


Expand Down Expand Up @@ -108,7 +108,8 @@ def _subject(value):
_require(type(t['dry_run_only']) is bool)
_require(t['execution_environment'] in ('dry_run', 'paper', 'live'))
release = t['strategy_release']; _object(release, _RELEASE)
for key in ('release_id', 'strategy_revision'): _label(release[key])
_label(release['release_id'], '[A-Za-z0-9][A-Za-z0-9._-]{2,127}')
_label(release['strategy_revision'], '[A-Za-z0-9][A-Za-z0-9._-]{0,63}')
for key in _RELEASE - {'release_id', 'strategy_revision', 'effective_session'}: _sha(release[key])
session = release['effective_session']
_require(isinstance(session, str) and re.fullmatch(r'[0-9]{4}-[0-9]{2}-[0-9]{2}', session) is not None)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@
"properties": {
"release_id": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_-]{0,63}$"
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{2,127}(?![\\s\\S])"
},
"strategy_revision": {
"type": "string",
"pattern": "^[A-Za-z][A-Za-z0-9_-]{0,63}$"
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}(?![\\s\\S])"
},
"effective_session": {
"type": "string",
Expand Down
55 changes: 55 additions & 0 deletions tests/test_automation_setup_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,3 +371,58 @@ def test_complete_decision_datetime_preserves_valid_strings(field, form):
validator.validate(obj.to_dict())
assert SetupIntent.from_dict(encoded).to_dict() == encoded
assert obj.to_dict()['grants_live'] is obj.to_dict()['grants_execution_authority'] is False


# Format-faithful fake counterpart of the dotted release in test_strategy_release.py.
@pytest.mark.parametrize('field,value', [
('release_id', 'fake-p2-v3.20261104'),
('release_id', '1' + 'r' * 127),
('strategy_revision', '2' + 'a' * 39),
])
def test_existing_release_identity_can_bind_setup(field, value):
from quant_platform_kit.common.strategy_release import build_strategy_release_identity

s = subject()
s['target']['strategy_release'][field] = value
identity = build_strategy_release_identity(s['target']['strategy_release'])
assert identity.to_dict()[field] == value
obj = SetupIntent.create(s)
assert obj.to_dict()['subject']['target']['strategy_release'][field] == value
assert not list(decision_schema_validator().iter_errors(obj.to_dict()))
assert obj.to_dict()['grants_live'] is obj.to_dict()['grants_execution_authority'] is False


@pytest.mark.parametrize('field,value', [
('release_id', 'fake-p2-v3.20261104'),
('release_id', '1' + 'r' * 127),
('strategy_revision', '2' + 'a' * 39),
])
def test_schema_accepts_existing_release_identity(field, value):
data = intent().to_dict()
data['subject']['target']['strategy_release'][field] = value
assert not list(decision_schema_validator().iter_errors(data))


@pytest.mark.parametrize('field,limit', [('release_id', 128), ('strategy_revision', 64)])
@pytest.mark.parametrize('value', ['', ' ', ' fake', 'fake ', 'fake/release',
r'fake\release', 'fake\n', 'fake\tvalue', 'fake\x00value', 'fake?token=fake', None])
def test_malformed_release_labels_fail_closed(field, limit, value):
s = subject()
s['target']['strategy_release'][field] = value
with pytest.raises(ValueError, match='^invalid setup intent$') as error:
SetupIntent.create(s)
assert error.value.__cause__ is None
data = intent().to_dict()
data['subject']['target']['strategy_release'][field] = value
assert list(decision_schema_validator().iter_errors(data))


@pytest.mark.parametrize('field,limit', [('release_id', 128), ('strategy_revision', 64)])
def test_overlength_release_labels_fail_closed(field, limit):
s = subject()
s['target']['strategy_release'][field] = 'r' * (limit + 1)
with pytest.raises(ValueError, match='^invalid setup intent$'):
SetupIntent.create(s)
data = intent().to_dict()
data['subject']['target']['strategy_release'][field] = 'r' * (limit + 1)
assert list(decision_schema_validator().iter_errors(data))
Loading
Loading