Skip to content

Commit 2b4e359

Browse files
authored
Merge pull request #5783: Add backward compatibility to secret masking for output schema
2 parents 9abb54b + b54de92 commit 2b4e359

File tree

3 files changed

+57
-9
lines changed

3 files changed

+57
-9
lines changed

CHANGELOG.rst

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ Fixed
2323

2424
Contributed by @bharath-orchestral
2525

26-
2726
* Fixed ``st2client/st2client/base.py`` file to use ``https_proxy``(not ``http_proxy``) to check HTTPS_PROXY environment variables.
2827

2928
Contributed by @wfgydbu
@@ -39,10 +38,16 @@ Fixed
3938
* Fixed generation of `st2.conf.sample` to show correct syntax for `[sensorcontainer].partition_provider` (space separated `key:value` pairs). #5710
4039
Contributed by @cognifloyd
4140

42-
* Fix access to key-value pairs in workflow and action execution where RBAC rules did not get applied
41+
* Fix access to key-value pairs in workflow and action execution where RBAC rules did not get applied #5764
4342

4443
Contributed by @m4dcoder
4544

45+
* Add backward compatibility to secret masking introduced in #5319 to prevent security-relative issues.
46+
Migration to the new schema is required to take advantage of the full output schema validation. #5783
47+
48+
Contributed by @m4dcoder
49+
50+
4651
Added
4752
~~~~~
4853

st2common/st2common/util/output_schema.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,39 @@ def _output_schema_is_valid(_schema):
3636
if not isinstance(_schema, Mapping):
3737
# malformed schema
3838
return False
39+
40+
if "type" not in _schema:
41+
# legacy schema format
42+
return False
43+
3944
try:
45+
# the validator is smart enough to handle
46+
# schema that is similar to the input schema
4047
schema.validate(
4148
_schema,
4249
schema.get_action_output_schema(),
4350
cls=schema.get_validator("custom"),
4451
)
4552
except jsonschema.ValidationError as e:
4653
LOG.debug("output_schema not valid: %s", e)
47-
# likely a legacy partial object schema (only defines properties)
4854
return False
55+
4956
return True
5057

5158

59+
def _normalize_legacy_output_schema(_schema):
60+
if not isinstance(_schema, Mapping):
61+
return _schema
62+
63+
_normalized_schema = {
64+
"type": "object",
65+
"properties": _schema,
66+
"additionalProperties": True,
67+
}
68+
69+
return _normalized_schema
70+
71+
5272
def _validate_runner(runner_schema, result):
5373
LOG.debug("Validating runner output: %s", runner_schema)
5474

@@ -183,15 +203,31 @@ def mask_secret_output(ac_ex, output_value):
183203
or output_key not in output_value
184204
# no action output_schema defined
185205
or not output_schema
186-
# malformed action output_schema
187-
or not _output_schema_is_valid(output_schema)
188206
):
189207
# nothing to mask
190208
return output_value
191209

210+
# backward compatibility for legacy output_schema so secrets stay masked
211+
if not _output_schema_is_valid(output_schema):
212+
# normalized the legacy schema to a full JSON schema and check if it is valid
213+
normalized_output_schema = _normalize_legacy_output_schema(output_schema)
214+
215+
if not _output_schema_is_valid(normalized_output_schema):
216+
# nothing to mask
217+
return output_value
218+
219+
# mask secret for the legacy output schema
220+
output_value[output_key] = _get_masked_value(
221+
normalized_output_schema, output_value[output_key]
222+
)
223+
224+
return output_value
225+
226+
# mask secret for the output schema
192227
output_value[output_key] = _get_masked_value(
193228
output_schema, output_value[output_key]
194229
)
230+
195231
return output_value
196232

197233

st2common/tests/unit/test_util_output_schema.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ def test_mask_secret_output_noop(self):
389389
masked_output = output_schema.mask_secret_output(ac_ex, ac_ex_result)
390390
self.assertDictEqual(masked_output, expected_masked_output)
391391

392-
def test_mask_secret_output_noop_legacy_schema(self):
392+
def test_mask_secret_output_with_legacy_schema(self):
393393
ac_ex = {
394394
"action": {
395395
"output_schema": LEGACY_ACTION_OUTPUT_SCHEMA,
@@ -399,10 +399,17 @@ def test_mask_secret_output_noop_legacy_schema(self):
399399
"output_schema": RUNNER_OUTPUT_SCHEMA,
400400
},
401401
}
402-
ac_ex_result = {"output_1": "foobar"}
403-
expected_masked_output = {"output_1": "foobar"}
404402

405-
# Legacy schemas should be ignored since they aren't full json schemas.
403+
ac_ex_result = {OUTPUT_KEY: {"output_1": "foobar", "output_3": "fubar"}}
404+
405+
expected_masked_output = {
406+
OUTPUT_KEY: {
407+
"output_1": "foobar",
408+
"output_3": MASKED_ATTRIBUTE_VALUE,
409+
}
410+
}
411+
412+
# Legacy schemas are not full json schemas, but they should still mask secrets.
406413
masked_output = output_schema.mask_secret_output(ac_ex, ac_ex_result)
407414
self.assertDictEqual(masked_output, expected_masked_output)
408415

0 commit comments

Comments
 (0)