Skip to content

Commit 68cfb17

Browse files
fix: secret leak when recovering (#965)
#### Migration notes None - [x] The change comes with new or modified tests - [ ] Hard-to-understand functions have explanatory comments - [ ] End-user documentation is updated to reflect the change <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes - **New Features** - Added a deep cloning utility function to prevent unintended data mutations - Introduced a new workflow management capability with a `sayHello` function - Enhanced replay request filtering in agent runtime - **Improvements** - Refined runtime configuration handling - Improved code modularity and error handling in agent runtime - **Testing** - Updated sync test configuration with new Redis backend - Added new workflow test script <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: michael-0acf4 <[email protected]>
1 parent 30d8b1e commit 68cfb17

File tree

7 files changed

+51
-11
lines changed

7 files changed

+51
-11
lines changed

src/typegate/src/runtimes/substantial.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,9 @@ export class SubstantialRuntime extends Runtime {
113113
const queue = "default";
114114

115115
const agentConfig = {
116-
pollIntervalSec: typegate.config.base.substantial_poll_interval_sec,
117-
leaseLifespanSec: typegate.config.base.substantial_lease_lifespan_sec,
118-
maxAcquirePerTick: typegate.config.base.substantial_max_acquire_per_tick,
116+
pollIntervalSec: typegate.config.base.substantial_poll_interval_sec!,
117+
leaseLifespanSec: typegate.config.base.substantial_lease_lifespan_sec!,
118+
maxAcquirePerTick: typegate.config.base.substantial_max_acquire_per_tick!,
119119
} satisfies AgentConfig;
120120

121121
const agent = new Agent(backend, queue, agentConfig);

src/typegate/src/runtimes/substantial/agent.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,7 @@ export class Agent {
140140
}
141141

142142
for (const workflow of this.workflows) {
143-
const requests = replayRequests.filter(
144-
({ run_id }) => getTaskNameFromId(run_id) == workflow.name,
145-
);
143+
const requests = this.#selectReplayRequestsFor(workflow.name, replayRequests);
146144

147145
while (requests.length > 0) {
148146
// this.logger.warn(`Run workflow ${JSON.stringify(next)}`);
@@ -166,6 +164,25 @@ export class Agent {
166164
}
167165
}
168166

167+
#selectReplayRequestsFor(workflowName: string, runsInScope: Array<NextRun>) {
168+
const runsToDo = [];
169+
for (const run of runsInScope) {
170+
try {
171+
if (getTaskNameFromId(run.run_id) == workflowName) {
172+
runsToDo.push(run);
173+
}
174+
} catch(err) {
175+
this.logger.warn(`Bad runId ${run.run_id}`);
176+
this.logger.error(err);
177+
178+
// TODO:
179+
// Force remove?
180+
}
181+
}
182+
183+
return runsToDo;
184+
}
185+
169186
async #tryAcquireNextRun() {
170187
const activeRunIds = await Meta.substantial.agentActiveLeases({
171188
backend: this.backend,

src/typegate/src/typegraph/mod.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type * as ast from "graphql/ast";
55
import { Kind } from "graphql";
66
import type { DenoRuntime } from "../runtimes/deno/deno.ts";
77
import type { Runtime } from "../runtimes/Runtime.ts";
8-
import { ensure, ensureNonNullable } from "../utils.ts";
8+
import { deepClone, ensure, ensureNonNullable } from "../utils.ts";
99
import { typegraph_validate } from "native";
1010
import Chance from "chance";
1111
import {
@@ -229,7 +229,7 @@ export class TypeGraph implements AsyncDisposable {
229229
typegraph,
230230
typegraphName,
231231
materializers,
232-
args: (runtime as any)?.data ?? {},
232+
args: deepClone((runtime as any)?.data ?? {}),
233233
secretManager,
234234
});
235235
}),

src/typegate/src/utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,5 @@ export function collectFieldNames(tg: TypeGraph, typeIdx: number) {
216216

217217
export const sleep = (ms: number) =>
218218
new Promise((resolve) => setTimeout(resolve, ms));
219+
220+
export const deepClone = <T>(clonable: T): T => JSON.parse(JSON.stringify(clonable)) as T;

tests/sync/scripts/workflow.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
interface Context {
5+
kwargs: {
6+
name: string;
7+
};
8+
}
9+
10+
export function sayHello(ctx: Context) {
11+
return `Hello ${ctx.kwargs.name}`;
12+
}

tests/sync/sync.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@
33

44
from typegraph import t, typegraph, Policy, Graph
55
from typegraph.runtimes.deno import DenoRuntime
6+
from typegraph.runtimes.substantial import Backend, SubstantialRuntime, WorkflowFile
67

78

89
@typegraph()
910
def sync(g: Graph):
1011
deno = DenoRuntime()
12+
backend = Backend.redis("SUB_REDIS")
13+
14+
file = WorkflowFile.deno(file="scripts/workflow.ts").import_(["sayHello"]).build()
15+
16+
sub = SubstantialRuntime(backend, [file])
1117
public = Policy.public()
1218

1319
g.expose(
@@ -17,5 +23,6 @@ def sync(g: Graph):
1723
name="hello",
1824
module="scripts/hello.ts",
1925
secrets=["ULTRA_SECRET"],
20-
).with_policy(public)
26+
).with_policy(public),
27+
helloWorkflow=sub.start(t.struct({"name": t.string()})),
2128
)

tests/sync/sync_force_remove_test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,15 @@ Meta.test(
8080
const _engine = await t.engine("sync/sync.py", {
8181
secrets: {
8282
ULTRA_SECRET:
83-
"if_you_can_read_me_on_an_ERROR_there_is_a_bug",
83+
"if_you_can_read_me_on_an_ERROR_there_is_a_bug",
84+
SUB_REDIS:
85+
"redis://:password@localhost:6380/0",
8486
},
8587
});
8688

8789
const s3 = new S3Client(syncConfig.s3);
8890
const initialObjects = await listObjects(s3, syncConfig.s3Bucket);
89-
assertEquals(initialObjects?.length, 3);
91+
assertEquals(initialObjects?.length, 4);
9092

9193
const gateNoRemove = await spawnGate(syncConfig);
9294
const namesNoRemove = gateNoRemove.register.list().map(({ name }) =>

0 commit comments

Comments
 (0)