Skip to content

Commit f82bc31

Browse files
committed
[ADD] Secret-controlled runtime hardening probes, external-worker sTask lifecycle verification, and integration with CI workflow
1 parent 84a24ba commit f82bc31

8 files changed

Lines changed: 58 additions & 11 deletions

File tree

.github/workflows/ci.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,16 @@ jobs:
166166
EMCP_MANAGER_PATH: ${{ secrets.EMCP_MANAGER_PATH }}
167167
EMCP_MANAGER_COOKIE: ${{ secrets.EMCP_MANAGER_COOKIE }}
168168
EMCP_DISPATCH_CHECK: ${{ secrets.EMCP_DISPATCH_CHECK }}
169+
EMCP_RUNTIME_NEGATIVE: ${{ secrets.EMCP_RUNTIME_NEGATIVE }}
170+
EMCP_RUNTIME_MODEL_SANITY: ${{ secrets.EMCP_RUNTIME_MODEL_SANITY }}
171+
EMCP_RUNTIME_NEGATIVE_REQUIRE_RATE_LIMIT: ${{ secrets.EMCP_RUNTIME_NEGATIVE_REQUIRE_RATE_LIMIT }}
172+
EMCP_TEST_JWT_SECRET: ${{ secrets.EMCP_TEST_JWT_SECRET }}
173+
EMCP_TEST_JWT_READ_TOKEN: ${{ secrets.EMCP_TEST_JWT_READ_TOKEN }}
174+
EMCP_STASK_LIFECYCLE_CHECK: ${{ secrets.EMCP_STASK_LIFECYCLE_CHECK }}
175+
EMCP_STASK_EXPECT_EXTERNAL_WORKER: ${{ secrets.EMCP_STASK_EXPECT_EXTERNAL_WORKER }}
176+
EMCP_STASK_WORKER_CMD: ${{ secrets.EMCP_STASK_WORKER_CMD }}
177+
EMCP_STASK_WORKER_CWD: ${{ secrets.EMCP_STASK_WORKER_CWD }}
178+
EMCP_STASK_POLL_ATTEMPTS: ${{ secrets.EMCP_STASK_POLL_ATTEMPTS }}
169179
steps:
170180
- name: Checkout
171181
uses: actions/checkout@v4
@@ -194,6 +204,15 @@ jobs:
194204
exit 1
195205
fi
196206
207+
lifecycle="${EMCP_STASK_LIFECYCLE_CHECK:-0}"
208+
external_worker="${EMCP_STASK_EXPECT_EXTERNAL_WORKER:-0}"
209+
if [ "$lifecycle" = "1" ] && [ "$external_worker" != "1" ]; then
210+
if [ -z "${EMCP_STASK_WORKER_CMD:-}" ] || [ -z "${EMCP_STASK_WORKER_CWD:-}" ]; then
211+
echo "EMCP_STASK_LIFECYCLE_CHECK=1 requires EMCP_STASK_EXPECT_EXTERNAL_WORKER=1 or both EMCP_STASK_WORKER_CMD and EMCP_STASK_WORKER_CWD." >&2
212+
exit 1
213+
fi
214+
fi
215+
197216
- name: Runtime integration checks
198217
run: |
199218
set -eu

OPERATIONS.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,16 @@ composer run test:integration:runtime
204204
Optional manager checks:
205205
- add `EMCP_MANAGER_PATH` and `EMCP_MANAGER_COOKIE`.
206206
- if both API and manager credentials are provided, script validates both paths.
207+
208+
Optional hardening checks:
209+
- set `EMCP_RUNTIME_NEGATIVE=1` for 401/403/413/415/429 probes.
210+
- set `EMCP_RUNTIME_MODEL_SANITY=1` for `evo.model.get(User)` leakage sanity.
211+
- set `EMCP_RUNTIME_NEGATIVE_REQUIRE_RATE_LIMIT=1` to fail if 429 is not observed.
212+
213+
Optional live sTask lifecycle proof:
214+
- enable with `EMCP_STASK_LIFECYCLE_CHECK=1`.
215+
- if target env has its own worker, also set `EMCP_STASK_EXPECT_EXTERNAL_WORKER=1`.
216+
- if worker must be started by the test host, set:
217+
- `EMCP_STASK_WORKER_CMD="php artisan stask:worker"`
218+
- `EMCP_STASK_WORKER_CWD="/path/to/evo/core"`
219+
- optional `EMCP_STASK_POLL_ATTEMPTS` (default: `20`).

PRD.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
- Додано clean-install validation script (`scripts/clean_install_validation.sh`) і інтеграцію в `demo-runtime-proof`.
2626
- Додано reproducible simulation benchmark suite + leaderboard artifacts (`scripts/benchmark/*`, `build/benchmarks/*`).
2727
- Додано optional advanced tree tools (`neighbors`, `prev/next siblings`, `children/siblings range`) з контрактними та runtime перевірками.
28+
- Додано secret-controlled live hardening probes у `runtime-integration` (negative checks, model sanity, optional `sTask` lifecycle з external-worker режимом).
2829

2930
Залишок до RC-1 (core platform hardening):
3031
- branch protection required-check enforcement для CI runtime jobs (`demo-runtime-proof`, `runtime-integration`) на `release/*`;

SPEC.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Current validation snapshot (2026-03-03):
2424
- Streaming policy now enforces proxy/FPM-safe SSE headers (`Content-Type: text/event-stream`, `Cache-Control: no-cache, no-transform`, `X-Accel-Buffering: no`) with dedicated tests.
2525
- CI includes migration matrix automation for `sqlite/mysql/pgsql`.
2626
- CI/check flow now produces reproducible benchmark and leaderboard artifacts.
27+
- Live runtime integration harness now supports secret-controlled hardening probes: negative transport checks, model-safety sanity, and optional `sTask` lifecycle verification with external-worker mode.
2728

2829
Open RC-1 validation scope:
2930
- live CI runtime integration jobs are wired for `release/*` pushes (`demo-runtime-proof`, `runtime-integration`), but branch-protection required-check enforcement must be configured in repository settings;

TASKS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ DoD:
234234
- [x] Integration tests for manager/API MCP endpoints.
235235
- [x] Add runtime integration harness script for manager/API/dispatch verification against deployed environment.
236236
- [x] Add release-branch CI runtime jobs (`demo-runtime-proof`, `runtime-integration`) with artifacts (`demo/logs.md`, `runtime-live.log`).
237+
- [x] Wire secret-controlled live runtime hardening probes (`negative`, `model sanity`, optional `sTask lifecycle`) in `runtime-integration` job.
237238
- [ ] Configure repository branch protection to require `demo-runtime-proof` and `runtime-integration` on `release/*`.
238239
- [x] Streaming tests under typical PHP-FPM constraints.
239240
- [x] Async tests for `sTask` path and failover.

scripts/demo_verify.sh

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,9 @@ dispatch_payload_a='{"jsonrpc":"2.0","id":"d1-doc","method":"tools/call","params
409409
dispatch_payload_b='{"jsonrpc":"2.0","id":"d2-doc","method":"tools/call","params":{"name":"evo.content.search","arguments":{"limit":2,"offset":0}}}'
410410
dispatch_url="${mcp_url}/dispatch"
411411
rate_limit_identity_type='api:jwt (sapi.jwt.user_id/sapi.jwt.sub; fallback ip)'
412+
dispatch_key_run="demo-verify-$(date +%s)-$$"
413+
dispatch_key_main="${dispatch_key_run}-k1"
414+
dispatch_key_lifecycle="${dispatch_key_run}-k2"
412415

413416
unauth_headers_file="${TMP_DIR}/unauth.headers"
414417
unauth_raw=$(curl -sS -D "${unauth_headers_file}" \
@@ -476,21 +479,21 @@ oversized_http_code=$(printf '%s' "${oversized_raw}" | sed -n 's/^__HTTP_CODE__:
476479
oversized_response=$(printf '%s' "${oversized_raw}" | sed '/^__HTTP_CODE__:/d')
477480

478481
dispatch_a_headers="${TMP_DIR}/dispatch-a.headers"
479-
dispatch_a_raw=$(mcp_post_with_token_optional_session "${probe_token}" "${dispatch_payload_a}" "${dispatch_a_headers}" "Idempotency-Key: demo-verify-k1" "${dispatch_url}")
482+
dispatch_a_raw=$(mcp_post_with_token_optional_session "${probe_token}" "${dispatch_payload_a}" "${dispatch_a_headers}" "Idempotency-Key: ${dispatch_key_main}" "${dispatch_url}")
480483
dispatch_a_http_code=$(printf '%s' "${dispatch_a_raw}" | sed -n 's/^__HTTP_CODE__://p' | tail -n 1)
481484
dispatch_a_response=$(printf '%s' "${dispatch_a_raw}" | sed '/^__HTTP_CODE__:/d')
482485

483486
dispatch_reuse_headers="${TMP_DIR}/dispatch-reuse.headers"
484-
dispatch_reuse_raw=$(mcp_post_with_token_optional_session "${probe_token}" "${dispatch_payload_a}" "${dispatch_reuse_headers}" "Idempotency-Key: demo-verify-k1" "${dispatch_url}")
487+
dispatch_reuse_raw=$(mcp_post_with_token_optional_session "${probe_token}" "${dispatch_payload_a}" "${dispatch_reuse_headers}" "Idempotency-Key: ${dispatch_key_main}" "${dispatch_url}")
485488
dispatch_reuse_http_code=$(printf '%s' "${dispatch_reuse_raw}" | sed -n 's/^__HTTP_CODE__://p' | tail -n 1)
486489
dispatch_reuse_response=$(printf '%s' "${dispatch_reuse_raw}" | sed '/^__HTTP_CODE__:/d')
487490

488491
dispatch_conflict_headers="${TMP_DIR}/dispatch-conflict.headers"
489-
dispatch_conflict_raw=$(mcp_post_with_token_optional_session "${probe_token}" "${dispatch_payload_b}" "${dispatch_conflict_headers}" "Idempotency-Key: demo-verify-k1" "${dispatch_url}")
492+
dispatch_conflict_raw=$(mcp_post_with_token_optional_session "${probe_token}" "${dispatch_payload_b}" "${dispatch_conflict_headers}" "Idempotency-Key: ${dispatch_key_main}" "${dispatch_url}")
490493
dispatch_conflict_http_code=$(printf '%s' "${dispatch_conflict_raw}" | sed -n 's/^__HTTP_CODE__://p' | tail -n 1)
491494
dispatch_conflict_response=$(printf '%s' "${dispatch_conflict_raw}" | sed '/^__HTTP_CODE__:/d')
492495

493-
dispatch_lifecycle_key='demo-verify-k2'
496+
dispatch_lifecycle_key="${dispatch_key_lifecycle}"
494497
dispatch_lifecycle_start_headers="${TMP_DIR}/dispatch-lifecycle-start.headers"
495498
dispatch_lifecycle_start_raw=$(mcp_post_with_token_optional_session "${probe_token}" "${dispatch_payload_a}" "${dispatch_lifecycle_start_headers}" "Idempotency-Key: ${dispatch_lifecycle_key}" "${dispatch_url}")
496499
dispatch_lifecycle_start_http_code=$(printf '%s' "${dispatch_lifecycle_start_raw}" | sed -n 's/^__HTTP_CODE__://p' | tail -n 1)

tests/Integration/RuntimeIntegrationHttpTest.php

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ function runShellCommand(string $command, string $cwd = ''): array
379379
$rateProbeMaxAttempts = 90;
380380
}
381381
$runStaskLifecycleCheck = getenv('EMCP_STASK_LIFECYCLE_CHECK') === '1';
382+
$staskExpectExternalWorker = getenv('EMCP_STASK_EXPECT_EXTERNAL_WORKER') === '1';
382383
$staskWorkerCommand = trim((string)getenv('EMCP_STASK_WORKER_CMD'));
383384
$staskWorkerCwd = trim((string)getenv('EMCP_STASK_WORKER_CWD'));
384385
$staskPollAttempts = (int)getenv('EMCP_STASK_POLL_ATTEMPTS');
@@ -760,7 +761,8 @@ function runShellCommand(string $command, string $cwd = ''): array
760761

761762
if ($runDispatchCheck) {
762763
$dispatchUrl = rtrim($url, '/') . '/dispatch';
763-
$key = 'runtime-k1';
764+
$runKey = bin2hex(random_bytes(6));
765+
$key = 'runtime-k1-' . $runKey;
764766

765767
$dispatchA = httpPostJson($dispatchUrl, [
766768
'jsonrpc' => '2.0',
@@ -836,11 +838,11 @@ function runShellCommand(string $command, string $cwd = ''): array
836838
}
837839

838840
if ($runStaskLifecycleCheck && $label === 'api') {
839-
if ($staskWorkerCommand === '' || $staskWorkerCwd === '') {
840-
fail("{$label} sTask lifecycle check requires EMCP_STASK_WORKER_CMD and EMCP_STASK_WORKER_CWD.");
841+
if (!$staskExpectExternalWorker && ($staskWorkerCommand === '' || $staskWorkerCwd === '')) {
842+
fail("{$label} sTask lifecycle check requires EMCP_STASK_WORKER_CMD and EMCP_STASK_WORKER_CWD, or EMCP_STASK_EXPECT_EXTERNAL_WORKER=1.");
841843
}
842844

843-
$lifecycleKey = 'runtime-k-lifecycle';
845+
$lifecycleKey = 'runtime-k-lifecycle-' . $runKey;
844846
$dispatchLifecycleStart = httpPostJson($dispatchUrl, [
845847
'jsonrpc' => '2.0',
846848
'id' => 'dl1',
@@ -864,9 +866,14 @@ function runShellCommand(string $command, string $cwd = ''): array
864866
fail("{$label} sTask lifecycle expected async task_id > 0, got " . (string)($dispatchLifecycleStartJson['task_id'] ?? 'null') . '.');
865867
}
866868

867-
$workerRun = runShellCommand($staskWorkerCommand, $staskWorkerCwd);
868-
if ($workerRun['exit'] !== 0) {
869-
fail("{$label} sTask worker command failed (exit {$workerRun['exit']}): " . $workerRun['output']);
869+
if ($staskExpectExternalWorker) {
870+
info("{$label} sTask lifecycle check: expecting external worker processing.");
871+
usleep(700000);
872+
} else {
873+
$workerRun = runShellCommand($staskWorkerCommand, $staskWorkerCwd);
874+
if ($workerRun['exit'] !== 0) {
875+
fail("{$label} sTask worker command failed (exit {$workerRun['exit']}): " . $workerRun['output']);
876+
}
870877
}
871878

872879
$completed = false;

tests/Phase6/CiWorkflowCoverageTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ function assertTrue(bool $condition, string $message): void
2020
'runtime-integration:',
2121
'demo-runtime-proof:',
2222
'migration-matrix:',
23+
'EMCP_STASK_LIFECYCLE_CHECK',
24+
'EMCP_STASK_EXPECT_EXTERNAL_WORKER',
2325
'benchmark-artifacts',
2426
'composer run test:integration:clean-install',
2527
'scripts/migration_matrix_check.sh sqlite',

0 commit comments

Comments
 (0)