Skip to content

Commit 2f7e64a

Browse files
authored
Merge pull request #148 from Lykhoyda/fix/134.5-workflow-and-correctness
fix: workflow + correctness sweep (Phase 134.5)
2 parents e50980a + 9520804 commit 2f7e64a

11 files changed

Lines changed: 157 additions & 18 deletions

File tree

.claude-plugin/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
{
1010
"name": "rn-dev-agent",
1111
"description": "AI agent that fully tests React Native features on simulator/emulator — navigates the app, verifies UI, walks user flows, and confirms internal state.",
12-
"version": "0.44.34",
12+
"version": "0.44.35",
1313
"source": "./",
1414
"category": "mobile-development",
1515
"homepage": "https://github.com/Lykhoyda/rn-dev-agent"

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "rn-dev-agent",
3-
"version": "0.44.34",
3+
"version": "0.44.35",
44
"description": "AI agent that fully tests React Native features on simulator/emulator — navigates the app, verifies UI, walks user flows, and confirms internal state.",
55
"author": {
66
"name": "Anton Lykhoyda",

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ jobs:
1515
working-directory: scripts/cdp-bridge
1616

1717
steps:
18-
- uses: actions/checkout@v4
18+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 — Phase 134.5 SHA pin
1919

20-
- uses: actions/setup-node@v4
20+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 — Phase 134.5 SHA pin
2121
with:
2222
node-version: 22
2323
cache: npm
@@ -38,5 +38,5 @@ jobs:
3838
name: Version sync check
3939
runs-on: ubuntu-latest
4040
steps:
41-
- uses: actions/checkout@v4
41+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 — Phase 134.5 SHA pin
4242
- run: bash scripts/sync-versions.sh

.github/workflows/deploy-docs.yml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ on:
1212
- 'CHANGELOG.md'
1313
- '.github/workflows/deploy-docs.yml'
1414

15+
# Phase 134.5 (deepsec MEDIUM): least-privilege permissions. The deploy
16+
# job is the ONLY step that needs `pages: write` + `id-token: write`.
17+
# Scoping those to that job alone prevents a compromised build step
18+
# from writing Pages content or minting an OIDC token.
1519
permissions:
1620
contents: read
17-
pages: write
18-
id-token: write
1921

2022
concurrency:
2123
group: pages
@@ -24,10 +26,12 @@ concurrency:
2426
jobs:
2527
build:
2628
runs-on: ubuntu-latest
29+
permissions:
30+
contents: read
2731
steps:
28-
- uses: actions/checkout@v4
32+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 — Phase 134.5 SHA pin
2933

30-
- uses: actions/setup-node@v4
34+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 — Phase 134.5 SHA pin
3135
with:
3236
node-version: '22'
3337
cache: 'npm'
@@ -41,16 +45,19 @@ jobs:
4145
working-directory: docs-site
4246
run: npm run build
4347

44-
- uses: actions/upload-pages-artifact@v3
48+
- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 — Phase 134.5 SHA pin
4549
with:
4650
path: docs-site/dist
4751

4852
deploy:
4953
needs: build
5054
runs-on: ubuntu-latest
55+
permissions:
56+
pages: write
57+
id-token: write
5158
environment:
5259
name: github-pages
5360
url: ${{ steps.deployment.outputs.page_url }}
5461
steps:
55-
- uses: actions/deploy-pages@v4
62+
- uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 — Phase 134.5 SHA pin
5663
id: deployment

CHANGELOG.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,44 @@ All notable changes to rn-dev-agent will be documented in this file.
44

55
Format follows [Keep a Changelog](https://keepachangelog.com/).
66

7+
## [0.44.35] — 2026-05-12
8+
9+
### Fixed (Phase 134.5 — workflow + correctness sweep, closes 3 MEDIUM + 2 BUG)
10+
11+
- **GitHub Actions pinned to immutable commit SHAs.** Both `ci.yml`
12+
and `deploy-docs.yml` previously used mutable `@v4` tags — a moved
13+
tag (or compromised maintainer account) would silently substitute
14+
different code on the next run. Now pinned to:
15+
- `actions/checkout@de0fac2e…` (v6.0.2)
16+
- `actions/setup-node@48b55a01…` (v6.4.0)
17+
- `actions/upload-pages-artifact@fc324d35…` (v5.0.0)
18+
- `actions/deploy-pages@cd2ce8fc…` (v5.0.0)
19+
20+
Per [GitHub's official security-hardening guide](https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions):
21+
"Pinning an action to a full-length commit SHA is currently the only
22+
way to use an action as an immutable release."
23+
- **`deploy-docs.yml` per-job least-privilege permissions.** Previously
24+
`pages: write` + `id-token: write` were granted at the workflow level,
25+
so the `build` job (which only needs `contents: read`) had Pages-write
26+
and OIDC capability it didn't use. Those permissions are now scoped
27+
to the `deploy` job only.
28+
- **`maestro_test_all`'s `pattern` arg now guards against regex DoS.**
29+
Caller-supplied `pattern` is length-capped (256 chars) and
30+
RegExp construction is try/catch wrapped; on any error, discovery
31+
proceeds without filtering rather than crashing.
32+
- **`cdp_connect`'s already-connected `bundleId` check uses
33+
word-boundary matching** instead of `includes()`. Prevents
34+
false-positive "already connected" when the live target is e.g.
35+
`com.example.app-test` and the caller asked for `com.example.app`.
36+
37+
### Internal
38+
39+
- Workflows: SHA-pin comments include the resolved version name
40+
(`# v6.0.2`) so Dependabot can read both and bump them together.
41+
- `tools/connection.ts` bundleId match uses a regex with non-bundle-id
42+
boundary characters (`[^A-Za-z0-9._-]`) — bundle IDs share `.` and
43+
`-` with their surrounding context, so `\b` alone wouldn't work.
44+
745
## [0.44.34] — 2026-05-12
846

947
### Fixed (Phase 134.4 — CDP multiplexer trust boundary, closes 1 HIGH)

scripts/cdp-bridge/dist/tools/connection.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,18 @@ export function createConnectHandler(getClient, setClient, createClient) {
1111
const haystack = `${target?.title ?? ''} ${target?.description ?? ''}`.toLowerCase();
1212
const portMismatch = typeof args.metroPort === 'number' && args.metroPort !== client.metroPort;
1313
const targetIdMismatch = typeof args.targetId === 'string' && args.targetId.length > 0 && args.targetId !== target?.id;
14-
const bundleMismatch = typeof args.bundleId === 'string' && args.bundleId.length > 0
15-
&& !haystack.includes(args.bundleId.toLowerCase());
14+
// Phase 134.5 (deepsec BUG: other-logic-bug): the prior substring
15+
// check would treat `com.example.app` as "already connected" when
16+
// the actual target is `com.example.app-test` or `com.example.app2`
17+
// — anything that contained the bundleId as a substring. Use a
18+
// word-boundary check anchored on non-bundle-id characters so
19+
// `com.example.app` matches `... com.example.app ...` but not
20+
// `... com.example.app-test ...`. Bundle IDs use `[A-Za-z0-9._-]`,
21+
// so the boundary must be anything outside that set.
22+
const bundleIdLower = typeof args.bundleId === 'string' ? args.bundleId.toLowerCase() : '';
23+
const bundleMatched = bundleIdLower.length > 0
24+
&& new RegExp(`(^|[^A-Za-z0-9._-])${bundleIdLower.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}([^A-Za-z0-9._-]|$)`).test(haystack);
25+
const bundleMismatch = typeof args.bundleId === 'string' && args.bundleId.length > 0 && !bundleMatched;
1626
let platformMismatch = false;
1727
if (typeof args.platform === 'string' && args.platform.length > 0) {
1828
const requestedPlatform = args.platform.toLowerCase();

scripts/cdp-bridge/dist/tools/maestro-test-all.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,21 @@ function discoverFlows(dir, pattern) {
1818
.map((f) => join(dir, f))
1919
.sort();
2020
if (pattern) {
21-
const re = new RegExp(pattern, 'i');
21+
// Phase 134.5 (deepsec BUG: regex-dos): a malicious or malformed
22+
// `pattern` arg could throw on invalid regex syntax or hang on
23+
// catastrophic backtracking (e.g. `(a+)+$` against a long input).
24+
// Cap the pattern length and wrap construction; on any error,
25+
// skip filtering rather than crash discovery.
26+
if (pattern.length > 256) {
27+
return yamls;
28+
}
29+
let re;
30+
try {
31+
re = new RegExp(pattern, 'i');
32+
}
33+
catch {
34+
return yamls;
35+
}
2236
return yamls.filter((f) => re.test(f));
2337
}
2438
return yamls;

scripts/cdp-bridge/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "rn-dev-agent-cdp",
3-
"version": "0.38.29",
3+
"version": "0.38.30",
44
"type": "module",
55
"main": "dist/index.js",
66
"scripts": {

scripts/cdp-bridge/src/tools/connection.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,18 @@ export function createConnectHandler(
3636

3737
const portMismatch = typeof args.metroPort === 'number' && args.metroPort !== client.metroPort;
3838
const targetIdMismatch = typeof args.targetId === 'string' && args.targetId.length > 0 && args.targetId !== target?.id;
39-
const bundleMismatch = typeof args.bundleId === 'string' && args.bundleId.length > 0
40-
&& !haystack.includes(args.bundleId.toLowerCase());
39+
// Phase 134.5 (deepsec BUG: other-logic-bug): the prior substring
40+
// check would treat `com.example.app` as "already connected" when
41+
// the actual target is `com.example.app-test` or `com.example.app2`
42+
// — anything that contained the bundleId as a substring. Use a
43+
// word-boundary check anchored on non-bundle-id characters so
44+
// `com.example.app` matches `... com.example.app ...` but not
45+
// `... com.example.app-test ...`. Bundle IDs use `[A-Za-z0-9._-]`,
46+
// so the boundary must be anything outside that set.
47+
const bundleIdLower = typeof args.bundleId === 'string' ? args.bundleId.toLowerCase() : '';
48+
const bundleMatched = bundleIdLower.length > 0
49+
&& new RegExp(`(^|[^A-Za-z0-9._-])${bundleIdLower.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}([^A-Za-z0-9._-]|$)`).test(haystack);
50+
const bundleMismatch = typeof args.bundleId === 'string' && args.bundleId.length > 0 && !bundleMatched;
4151
let platformMismatch = false;
4252
if (typeof args.platform === 'string' && args.platform.length > 0) {
4353
const requestedPlatform = args.platform.toLowerCase();

scripts/cdp-bridge/src/tools/maestro-test-all.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,20 @@ function discoverFlows(dir: string, pattern?: string): string[] {
4040
.sort();
4141

4242
if (pattern) {
43-
const re = new RegExp(pattern, 'i');
43+
// Phase 134.5 (deepsec BUG: regex-dos): a malicious or malformed
44+
// `pattern` arg could throw on invalid regex syntax or hang on
45+
// catastrophic backtracking (e.g. `(a+)+$` against a long input).
46+
// Cap the pattern length and wrap construction; on any error,
47+
// skip filtering rather than crash discovery.
48+
if (pattern.length > 256) {
49+
return yamls;
50+
}
51+
let re: RegExp;
52+
try {
53+
re = new RegExp(pattern, 'i');
54+
} catch {
55+
return yamls;
56+
}
4457
return yamls.filter((f) => re.test(f));
4558
}
4659
return yamls;

0 commit comments

Comments
 (0)