Skip to content

Commit c962a78

Browse files
authored
Merge pull request #397 from Azure/dev
Promote dev to main for release
2 parents 62c57bd + 95e794f commit c962a78

File tree

6 files changed

+127
-9
lines changed

6 files changed

+127
-9
lines changed

SECURITY.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.7 BLOCK -->
2+
3+
## Security
4+
5+
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
6+
7+
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
8+
9+
## Reporting Security Issues
10+
11+
**Please do not report security vulnerabilities through public GitHub issues.**
12+
13+
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
14+
15+
If you prefer to submit without logging in, send email to [[email protected]](mailto:[email protected]). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
16+
17+
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
18+
19+
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
20+
21+
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
22+
* Full paths of source file(s) related to the manifestation of the issue
23+
* The location of the affected source code (tag/branch/commit or direct URL)
24+
* Any special configuration required to reproduce the issue
25+
* Step-by-step instructions to reproduce the issue
26+
* Proof-of-concept or exploit code (if possible)
27+
* Impact of the issue, including how an attacker might exploit the issue
28+
29+
This information will help us triage your report more quickly.
30+
31+
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
32+
33+
## Preferred Languages
34+
35+
We prefer all communications to be in English.
36+
37+
## Policy
38+
39+
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
40+
41+
<!-- END MICROSOFT SECURITY.MD BLOCK -->

azure/durable_functions/models/Task.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,11 @@ def __init__(self, tasks: List[TaskBase], compound_action_constructor=None):
171171
if len(self.children) == 0:
172172
self.state = TaskState.SUCCEEDED
173173

174+
# Sub-tasks may have already completed, so we process them
175+
for child in self.children:
176+
if not(child.state is TaskState.RUNNING):
177+
self.handle_completion(child)
178+
174179
def handle_completion(self, child: TaskBase):
175180
"""Manage sub-task completion events.
176181

azure/durable_functions/models/TaskOrchestrationExecutor.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,22 @@ def execute(self, context: DurableOrchestrationContext,
6868
self.context = context
6969
evaluated_user_code = fn(context)
7070

71+
# The minimum History size is 2, in the shape: [OrchestratorStarted, ExecutionStarted].
72+
# At the start of replay, the `is_replaying` flag is determined from the
73+
# ExecutionStarted event.
74+
# For some reason, OrchestratorStarted does not update its `isPlayed` field.
75+
if len(history) < 2:
76+
err_message = "Internal Durable Functions error: "\
77+
+ f"received History array of size {len(history)} "\
78+
+ "when a minimum size of 2 is expected. "\
79+
+ "Please report this issue at "\
80+
+ "https://github.com/Azure/azure-functions-durable-python/issues."
81+
raise Exception(err_message)
82+
83+
# Set initial is_replaing state.
84+
execution_started_event = history[1]
85+
self.current_task.is_played = execution_started_event.is_played
86+
7187
# If user code is a generator, then it uses `yield` statements (the DF API)
7288
# and so we iterate through the DF history, generating tasks and populating
7389
# them with values when the history provides them

tests/orchestrator/test_is_replaying_flag.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def add_timer_action(state: OrchestratorState, fire_at: datetime):
4747

4848
def test_is_replaying_initial_value():
4949

50-
context_builder = ContextBuilder("")
50+
context_builder = ContextBuilder("", is_replaying=False)
5151
result = get_orchestration_property(
5252
context_builder, generator_function, "durable_context")
5353

tests/orchestrator/test_sequential_orchestrator.py

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,26 @@ def generator_function(context):
2424

2525
return outputs
2626

27+
def generator_function_multi_yield_when_all(context):
28+
outputs = []
29+
30+
task1 = context.call_activity("Hello", "Tokyo")
31+
yield context.task_all([task1])
32+
result = yield context.task_all([task1])
33+
34+
return result
35+
36+
def generator_function_is_replaying(context):
37+
outputs = []
38+
39+
outputs.append(context.is_replaying)
40+
yield context.call_activity("Hello", "Tokyo")
41+
outputs.append(context.is_replaying)
42+
yield context.call_activity("Hello", "Seattle")
43+
outputs.append(context.is_replaying)
44+
yield context.call_activity("Hello", "London")
45+
return outputs
46+
2747
def generator_function_no_yield(context):
2848
outputs = []
2949

@@ -150,11 +170,11 @@ def add_hello_action(state: OrchestratorState, input_: str):
150170
state.actions.append([action])
151171

152172
def add_hello_completed_events(
153-
context_builder: ContextBuilder, id_: int, result: str):
173+
context_builder: ContextBuilder, id_: int, result: str, is_played=False):
154174
context_builder.add_task_scheduled_event(name='Hello', id_=id_)
155175
context_builder.add_orchestrator_completed_event()
156176
context_builder.add_orchestrator_started_event()
157-
context_builder.add_task_completed_event(id_=id_, result=result)
177+
context_builder.add_task_completed_event(id_=id_, result=result, is_played=is_played)
158178

159179

160180
def add_hello_failed_events(
@@ -286,6 +306,42 @@ def test_tokyo_and_seattle_and_london_state():
286306
assert_valid_schema(result)
287307
assert_orchestration_state_equals(expected, result)
288308

309+
def test_multi_when_all_yield():
310+
context_builder = ContextBuilder('test_simple_function')
311+
add_hello_completed_events(context_builder, 0, "\"Hello Tokyo!\"")
312+
313+
result = get_orchestration_state_result(
314+
context_builder, generator_function_multi_yield_when_all)
315+
316+
expected_state = base_expected_state(
317+
['Hello Tokyo!'])
318+
add_hello_action(expected_state, 'Tokyo')
319+
expected_state._is_done = True
320+
expected = expected_state.to_json()
321+
322+
assert_valid_schema(result)
323+
assert_orchestration_state_equals(expected, result)
324+
325+
def test_sequential_is_replaying():
326+
context_builder = ContextBuilder('test_simple_function', is_replaying=True)
327+
add_hello_completed_events(context_builder, 0, "\"Hello Tokyo!\"", True)
328+
add_hello_completed_events(context_builder, 1, "\"Hello Seattle!\"", True)
329+
add_hello_completed_events(context_builder, 2, "\"Hello London!\"", True)
330+
331+
result = get_orchestration_state_result(
332+
context_builder, generator_function_is_replaying)
333+
334+
expected_state = base_expected_state(
335+
[True, True, True])
336+
add_hello_action(expected_state, 'Tokyo')
337+
add_hello_action(expected_state, 'Seattle')
338+
add_hello_action(expected_state, 'London')
339+
expected_state._is_done = True
340+
expected = expected_state.to_json()
341+
342+
assert_valid_schema(result)
343+
assert_orchestration_state_equals(expected, result)
344+
289345
def test_sequential_orchestration_no_yield():
290346
context_builder = ContextBuilder('test_simple_function')
291347
add_hello_completed_events(context_builder, 0, "\"Hello London!\"")

tests/test_utils/ContextBuilder.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515

1616
class ContextBuilder:
17-
def __init__(self, name: str="", increase_time: bool = True, starting_time: Optional[datetime] = None, replay_schema: ReplaySchema = ReplaySchema.V1):
17+
def __init__(self, name: str="", increase_time: bool = True, starting_time: Optional[datetime] = None, is_replaying=False, replay_schema: ReplaySchema = ReplaySchema.V1):
1818
self.increase_time = increase_time
1919
self.instance_id = uuid.uuid4()
2020
self.is_replaying: bool = False
@@ -28,7 +28,7 @@ def __init__(self, name: str="", increase_time: bool = True, starting_time: Opti
2828
self.upperSchemaVersion = replay_schema.value
2929

3030
self.add_orchestrator_started_event()
31-
self.add_execution_started_event(name)
31+
self.add_execution_started_event(name, is_played=is_replaying)
3232

3333
def get_base_event(
3434
self, event_type: HistoryEventType, id_: int = -1,
@@ -87,8 +87,8 @@ def add_task_scheduled_event(
8787
event.Input_ = input_
8888
self.history_events.append(event)
8989

90-
def add_task_completed_event(self, id_: int, result):
91-
event = self.get_base_event(HistoryEventType.TASK_COMPLETED)
90+
def add_task_completed_event(self, id_: int, result, is_played=False):
91+
event = self.get_base_event(HistoryEventType.TASK_COMPLETED, is_played=is_played)
9292
event.Result = result
9393
event.TaskScheduledId = id_
9494
self.history_events.append(event)
@@ -116,8 +116,8 @@ def add_timer_fired_event(self, id_: int, fire_at: str, is_played: bool = True):
116116
self.history_events.append(event)
117117

118118
def add_execution_started_event(
119-
self, name: str, version: str = '', input_=None):
120-
event = self.get_base_event(HistoryEventType.EXECUTION_STARTED, is_played=True)
119+
self, name: str, version: str = '', input_=None, is_played=True):
120+
event = self.get_base_event(HistoryEventType.EXECUTION_STARTED, is_played=is_played)
121121
event.orchestration_instance = OrchestrationInstance()
122122
self.instance_id = event.orchestration_instance.instance_id
123123
event.Name = name

0 commit comments

Comments
 (0)