Skip to content

fix: track scalar prompt dependencies - #977

Closed
mldangelo-oai wants to merge 84 commits into
mainfrom
mdangelo/codex/sec-scalar-prompt-dependencies
Closed

fix: track scalar prompt dependencies#977
mldangelo-oai wants to merge 84 commits into
mainfrom
mdangelo/codex/sec-scalar-prompt-dependencies

Conversation

@mldangelo-oai

@mldangelo-oai mldangelo-oai commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Track supported prompt, provider/target, test/scenario/generator, vars/assertion, grading-provider, transform/rubric/scoring, extension-hook, Nunjucks-filter, HTTP auth/TLS/signature/multipart/transform, ElevenLabs audio, and executable dependencies across scalar, array, map, glob, selector, and structured-file forms, including provider and assertion $ref fragments.
  • Preserve renamed, deleted, whitespace, special-character, and external-config environment-templated paths across PR/push/manual events; keep logs workflow-command-safe, constrain lexical and physical workspace paths, and fail closed for unreadable, oversized, malformed, cyclic, or externally structured dependencies.
  • Evaluate the complete action-input prompt set on config/dependency changes, preserve prompt-only filtering, and validate exactly the selected prompt paths before execution/reporting.
  • Bound action/config brace expansion, selector/template matching, structured reads, and compiled dependency matching while retaining valid extglobs, character classes, escaped literals, nested paths, spreadsheet selectors, executable arguments, and legacy YAML compatibility. Enforce one cumulative traversal budget across every config, dependency, and action-prompt glob, prune escaping symlinks before descent, and enumerate only verified canonical directories.

Security impact

Fixes Codex Security finding csf_cedb9155ed2446aef69f420c / occurrence occ_740baac430d067e471c18aa5.

Previously, supported dependencies could be omitted from change detection, allowing relevant changes to take the successful no-change path. The follow-up also closes path-escape, workflow-command, cyclic/oversized traversal, and adversarial-matching risks without exposing sensitive paths in diagnostics.

Validation

  • Failing-first regressions cover singleton/map/cyclic YAML; bare/file-backed vars and ElevenLabs audio; nested structured and unstructured prompts, tests, scenarios, generator configs, filters, CSV/XLS/XLSX/JSONL and selectors; directory-backed suites, external defaultTest, assertion-value arrays, nested $ref, and response-format schemas; remote structured datasets and Windows drive paths; HTTP computed discriminators, auth/TLS/signature/multipart and transforms plus structurally nested body fields; direct/default/options/assertion grading providers, Python/JavaScript transforms/rubrics/scoring, and extension hooks; provider-prefix/runtime selector compatibility; executable prompts/providers/targets, path-qualified interpreters, loaders/preloads, and arguments; in-checkout/external-config generic/HTTP/prompt/exec environment templates including leading-slash, comma-env, spaced/parenthesized/NBSP computed-env forms; NUL/whitespace/CRLF/rename/delete paths; workspace/symlink/dangling/EACCES containment; nonblocking FIFO/device, descriptor identity/TOCTOU, and bounded-growth reads; numeric/class-hidden/padded/parity/deep/comma/alphabetic brace, optimized [{] and escaped-literal classes, extglob, and traversal cases; Windows odd/even separator parity and linear normalization; runtime backslash-activated brace ranges/traversal with distinct raw action-input POSIX escape semantics; direct POSIX auth/TLS/signature/multipart/transform backslash-and-colon filenames, decoded multipart URLs, and supported file-URL shorthand; adversarial template/selector/spreadsheet lengths; unstructured and inline-HTTP Nunjucks include/extends/import/from after long and NBSP whitespace without widening ordinary HTTP bodies; deleted action-prompt glob matches with and without surviving siblings; bounded single-directory and recursive zero-match enumeration, cross-config/two-glob cumulative budgets, pre-descent symlink pruning, and canonical-directory retarget safety; compiled POSIX dependency matching at the PR-file cap; relative/deduplicated prompt selection, unused config-prompt isolation, and accurate PR/workflow reporting.
  • Full gate: build, Biome, typecheck, package, and tests pass — 10 test files, 824 tests, 100% statements/branches/functions/lines. The post-artifact build/Biome/typecheck/test gate and byte comparisons also pass.
  • Independent compiled adversarial review is clean, including FIFO/TOCTOU and containment probes, external-config fail-closed ./ behavior, actual Promptfoo nested-HTTP-body runtime parity, direct-read/glob parity, optimized/escaped-literal classes, pre-descent and canonical-directory containment, cumulative traversal budgets, Windows separator safety, and performance probes.
  • Trusted Check dist artifact 8358320613 from run 29453153941 (sha256:89b2f49cdf05dfeb9515ccd0562fdbb39d4c8ad2ffa16464b0fadaba12aec088) was verified from source 6532032a21f5b1d82cdfdc4015db32156cab87ea merged into base 77edad74c1051fcce344a1baaab27ce98c95acb7, compared byte-for-byte, and shipped in bundle commit ee658a509889954416d95ff04a889dc6f3f49a51.

@mldangelo-oai
mldangelo-oai requested a review from Copilot July 8, 2026 07:49

Copy link
Copy Markdown
Contributor Author

@codex review

Please review the exact current head: e0574c0b1e080c00228ebce991fdac7e81600d6b, focusing on scalar prompt normalization and preservation of array/object behavior.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the action’s dependency-extraction logic to correctly handle Promptfoo configs where prompts is provided as a scalar string (e.g., prompts: file://prompt.txt), ensuring prompt-only changes are detected and evaluated instead of incorrectly taking the “no changes” path.

Changes:

  • Extend the config model so prompts can be either a scalar string or an array, and normalize scalars to an array before dependency extraction.
  • Add regression tests covering scalar file prompts, scalar glob prompts, and inline scalar prompts.
  • Regenerate the bundled dist/ output to ship the fix.

Reviewed changes

Copilot reviewed 2 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/utils/config.ts Accepts scalar prompts and normalizes to an array before extracting dependencies.
__tests__/utils/config.test.ts Adds coverage for scalar prompt dependency extraction (file, glob, inline).
dist/index.js Updates the shipped bundle to include the new scalar prompt handling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/utils/config.ts
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: e0574c0b1e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

mldangelo and others added 2 commits July 9, 2026 00:19
…alar-prompt-dependencies

# Conflicts:
#	dist/index.js.map
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 84f2622786

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/utils/config.ts
Comment thread src/utils/config.ts
Comment thread src/utils/config.ts
@mldangelo-oai
mldangelo-oai requested a review from Copilot July 15, 2026 01:06

Copy link
Copy Markdown
Contributor Author

@codex review

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 6 changed files in this pull request and generated 1 comment.

Comment thread src/utils/config.ts Outdated
@mldangelo-oai
mldangelo-oai requested a review from Copilot July 15, 2026 01:20

Copy link
Copy Markdown
Contributor Author

@codex review

main.ts duplicated config.ts's glob-magic options (with the derived brace
limit hardcoded as 1025). Export GLOB_MAGIC_OPTIONS from config.ts and import
it in main.ts so the dependency producer and matcher can't drift out of sync.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 59ad02d759

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/main.ts Outdated
) {
throw new Error('Config glob is malformed or exceeds supported limits');
}
if (/[*?[\]{}()]/.test(configPattern)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve literal config paths with glob characters

When the configured config file is literally named with glob metacharacters, such as promptfoo[prod].yaml, this branch now treats the documented config path input as a glob. glob.sync will either fail to find the existing literal file or match a different file like promptfoop.yaml, so existing workflows with bracketed config filenames can fail before evaluation or run against the wrong config; only enter glob expansion when the input is intentionally a glob or preserve a literal-path fallback.

Useful? React with 👍 / 👎.

Comment thread src/utils/config.ts Outdated
Comment on lines +2001 to +2002
extractTestValues(config.tests);
extractTestValues(config.scenarios);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Track top-level assertion files

Promptfoo supports suite-level assert and assertScoringFunction, but after handling defaultTest this only walks tests and scenarios. In configs that keep a shared validator at the top level, such as assert: [{ type: javascript, value: file://validators/check.js }], changing validators/check.js will not set dependencyChanged, so workflows with prompts globs can skip an evaluation even though Promptfoo would load the changed assertion file.

Useful? React with 👍 / 👎.

Comment thread src/main.ts Outdated
Comment on lines +782 to +784
!promptPrefixes.some((prefix) =>
promptCandidate.startsWith(prefix),
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize prompt prefixes before deletion checks

When the action prompt glob is written with a leading ./, such as ./prompts/*.txt, GitHub reports a deleted file as prompts/deleted.txt while promptPrefixes keeps ./prompts/. This prefix guard returns false before the matcher runs, so a PR that only deletes a watched prompt can still take the no-change skip path; normalize the expanded prefix the same way promptCandidate is normalized before comparing.

Useful? React with 👍 / 👎.

@mldangelo

Copy link
Copy Markdown
Member

Review + fix applied (59ad02d)

Pushed a de-duplication to this branch. npm run all passes (10 test files, 829 tests, 100% coverage) and CI is green (check-dist + CI Success).

Share GLOB_MAGIC_OPTIONS between config.ts and main.ts

main.ts defined DEPENDENCY_GLOB_MAGIC_OPTIONS = { magicalBraces: true, nonegate: true, braceExpandMax: 1025 }, a value-for-value copy of config.ts's GLOB_MAGIC_OPTIONS (braceExpandMax: MAX_BRACE_EXPANSIONS + 1, where MAX_BRACE_EXPANSIONS = 1024). These two must stay in lockstep — config.ts decides which dependency strings are emitted as glob patterns, and main.ts re-runs glob.hasMagic on the same strings to pick the path.matchesGlob branch. If nonegate or the brace limit drifted on one side, a !(one|two) dependency could silently stop matching.

Exported GLOB_MAGIC_OPTIONS from config.ts and imported it in main.ts, deleting the local copy and its hardcoded 1025.

Did NOT delete legacySetTag (correcting my earlier review)

My original review suggested legacySetTag re-implements js-yaml's built-in setTag and could be removed. On closer inspection that's not safe: legacySetTag parses !!set into a plain traversable Record (via legacyMapTag.addPair(..., null)), whereas the built-in setTag yields a JS Set that your isTraversableRecord excludes from traversal. Swapping them would change whether file:// dependencies nested inside a !!set are followed — a behavior change, not a pure cleanup. Left as-is.

Also noted (not changed)

  • A deleted structured prompt (prompts: file://configs/prompts.yaml) still runs the nested-inspection loop, which readFileSync-throws and logs a spurious Failed to inspect… warning — exactly the deleted-prompt case this PR targets. An if (!fs.existsSync(absolutePromptFile)) continue; at the top of that try would silence it.
  • glob.hasMagic (a pure function) is hand-mocked in every config test, so the classification core never runs against real minimatch. One it.each that installs the real impl via vi.importActual('glob') would lock the behavior down.

Bigger picture

One of seven competing config.ts rewrites — consolidation is the top recommendation for the family.

Original inline comments predate a restack and may show as "outdated".

…t path

An inline prompt like "Explain the code in utils.py" (no separator, not a
template) was classified as a file dependency via hasPromptFileExtension and
the has-short-extension fallback, yielding a bogus dependency plus a './'
whole-repo watch, so the eval re-ran on every push. Gate both on a new
referenceCouldBePath (no whitespace, or has a path separator, or is a
template), which preserves real paths with spaces and templated paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mldangelo

Copy link
Copy Markdown
Member

Correctness fix pushed (389d62a) — inline prose misclassified as a prompt path

A fresh correctness pass on the current head found a real P2 bug (separate from the earlier GLOB_MAGIC_OPTIONS cleanup), now fixed. npm run all passes (10 files, 831 tests, 100% coverage).

The bug

An inline scalar prompt that merely ends in a filename-like token — e.g. prompts: "Explain the code in utils.py" or "Find the bug in server.js" — was classified as a file dependency. In processPromptReference, hasPromptFileExtension(reference) (in looksLikePath) and the hasShortExtension fallback (inside the !looksLikePath block) were both ungated by whitespace, unlike the adjacent path-separator clause. Result: the prose was added as a bogus dependency string and — because it ends in .py — the executable-extension check appended "./", so extractFileDependencies returned ["Explain the code in utils.py", "./"] instead of []. The "./" makes main.ts treat every changed file as a match, re-running the full eval on every push. This directly contradicts the PR's own should ignore an inline scalar prompt tests.

The fix

Whitespace alone is the wrong discriminator — a real path can contain spaces (prompt files/main.txt), and templated paths contain whitespace inside {{ }}. So I introduced referenceCouldBePath = !/\s/ || has-separator || isTemplated and gated both the hasPromptFileExtension and hasShortExtension checks with it. This ignores multi-word prose while preserving: bare filenames (prompt.txt), spaced paths (prompt files/main.txt), and root-templated paths ({{ env.X }}/prompt.txt). Added "Explain the code in utils.py" / "Find the bug in server.js" to the inline-prompt matrix. It errs toward over-running (never misses an eval), which is why it's P2 not P1.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-24T08:10:22.083519Z efcd39a New commits
🔒 Security Review Completed 2026-08-24T08:10:07.990149Z efcd39a New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8cc7fd6e50

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/utils/config.ts Outdated
Comment on lines 159 to 160
for (const prompt of prompts) {
if (typeof prompt === 'string' && prompt.startsWith('file://')) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Track bare scalar prompt paths

When a supported scalar prompt is declared without the optional file:// prefix, such as prompts: prompts/main.txt, the new normalization reaches this loop but the following startsWith('file://') guard discards it. If only prompts/main.txt changes and the Action's own prompt glob does not match it, dependencyChanged remains false and the evaluation can be skipped even though Promptfoo loads that prompt file.

AGENTS.md reference: AGENTS.md:L5-L9

Useful? React with 👍 / 👎.

Comment thread src/utils/config.ts Outdated
Comment on lines 159 to 161
for (const prompt of prompts) {
if (typeof prompt === 'string' && prompt.startsWith('file://')) {
processFileUrl(prompt);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Strip function selectors from scalar prompt dependencies

For a scalar function prompt such as prompts: file://prompts/build.py:create_prompt, this passes the complete selector-bearing string to processFileUrl(), which records prompts/build.py:create_prompt rather than the actual prompts/build.py file. A PR changing only the Python or JavaScript module therefore cannot match the recorded dependency and may take the no-change skip path; remove the supported function selector before resolving the filesystem path.

AGENTS.md reference: AGENTS.md:L5-L9

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 69affddd1b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/utils/config.ts
Comment on lines +157 to +158
const prompts =
typeof config.prompts === 'string' ? [config.prompts] : config.prompts;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize mapping-form prompts before iterating

When prompts uses Promptfoo's mapping form, for example prompts: { file://prompts/main.txt: Main }, this expression leaves the parsed object unchanged and the following for...of throws because the object is not iterable. The outer catch then returns an empty dependency list, discarding both the mapped prompt and dependencies already found in providers or tests, so a dependency-only change can incorrectly skip evaluation; convert mapping keys to prompt entries before iterating.

AGENTS.md reference: AGENTS.md:L7-L9

Useful? React with 👍 / 👎.

Comment thread src/utils/config.ts Outdated
typeof config.prompts === 'string' ? [config.prompts] : config.prompts;
for (const prompt of prompts) {
if (typeof prompt === 'string' && prompt.startsWith('file://')) {
processFileUrl(prompt);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve absolute scalar prompt paths

When a scalar prompt uses an in-workspace absolute file URL such as prompts: file:///workspace/repo/prompts/main.txt, this passes it to processFileUrl, which strips the scheme and then combines the resulting absolute-looking path with configDir via path.join. Node consequently resolves a duplicated path such as /workspace/repo/workspace/repo/prompts/main.txt, so editing the actual prompt does not match the recorded dependency and the action can incorrectly skip evaluation; resolve absolute prompt paths directly before applying the config-directory base.

AGENTS.md reference: AGENTS.md:L7-L9

Useful? React with 👍 / 👎.

Comment thread src/utils/config.ts Outdated
typeof config.prompts === 'string' ? [config.prompts] : config.prompts;
for (const prompt of prompts) {
if (typeof prompt === 'string' && prompt.startsWith('file://')) {
processFileUrl(prompt);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Watch the config root for root-level scalar globs

When the scalar declaration is a root-level file glob such as prompts: file://*.txt, processFileUrl stops at the first path component because it is magical, leaving basePath empty and adding no directory dependency. If a matching root prompt is added or deleted, it is absent from the current glob matches and no root sentinel can match the changed filename, so an action prompt glob that does not independently cover it can take the no-change path; retain the config directory as the base watch when magic appears in the first component.

AGENTS.md reference: AGENTS.md:L7-L9

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bbc61ec496

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/utils/config.ts
Comment on lines +170 to +173
} else if (
!/\s/.test(prompt) &&
/\.(?:txt|md|json|ya?ml|py|[cm]?[jt]s|njk)$/i.test(prompt)
) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Track exec-backed scalar prompt scripts

When a configuration uses an executable scalar prompt such as prompts: exec:./prompts/generate.sh, this branch neither recognizes the exec: prefix nor accepts the .sh suffix, so the script is absent from dependencies. In workflows whose action-level prompt glob does not include the script, a change to generate.sh can therefore take the no-change skip path even though Promptfoo executes that file; strip the exec: prefix and track the referenced executable.

AGENTS.md reference: AGENTS.md:L7-L9

Useful? React with 👍 / 👎.

Comment thread src/utils/config.ts
) {
processFileUrl(`file://${prompt}`);
}
} else if (prompt.file) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Inspect raw and id prompt object references

When a prompt object carries its backing reference in raw or id, for example { raw: 'exec:./prompts/generate.sh' } or { id: 'file://prompts/main.txt' }, this condition only considers prompt.file and silently ignores the referenced file. If the action-level prompt glob does not independently match that file, editing it will not set dependencyChanged and the evaluation can be skipped; process supported raw and id references as well.

AGENTS.md reference: AGENTS.md:L7-L9

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 01c9782aab

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/utils/config.ts Outdated
Comment on lines +170 to +171
} else if (prompt.startsWith('exec:')) {
processFileUrl(`file://${prompt.slice('exec:'.length)}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse executable scalar prompt commands

When a scalar executable prompt includes normal command arguments or quoting, such as prompts: "exec:./scripts/generate.sh --tone formal", this passes the entire command remainder to processFileUrl(), recording a nonexistent filename containing the arguments rather than scripts/generate.sh. If the action-level prompt glob does not cover the script, changing only the script will not set dependencyChanged, so the evaluation can be skipped; extract the executable path from the command before tracking it.

AGENTS.md reference: AGENTS.md:L7-L9

Useful? React with 👍 / 👎.

Comment thread src/utils/config.ts Outdated
/\.(?:py|[cm]?[jt]s)$/i.test(promptPath.slice(0, selector))
? promptPath.slice(0, selector)
: promptPath;
processFileUrl(`file://${filePath}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Traverse references in structured scalar prompts

When a scalar prompt points to a structured YAML or JSON prompt file that itself contains a runtime file reference, such as prompts: file://configs/prompts.yaml with content: file://prompts/system.txt, this records only the outer structured file and never inspects its contents. If only prompts/system.txt changes and the action-level prompt glob does not match it, the action can take the no-change path even though Promptfoo reloads that content; traverse supported file references in structured prompt files while guarding cycles.

AGENTS.md reference: AGENTS.md:L7-L9

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a7073836e8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/utils/config.ts Outdated
.trimStart()
.match(/^(['"])(.*?)\1|^(\S+)/);
if (executable) {
processFileUrl(`file://${executable[2] ?? executable[3]}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Track scripts passed to prompt interpreters

When a scalar executable prompt uses an interpreter command such as prompts: 'exec:python scripts/generate.py', this parser selects only python and records it as the dependency, ignoring scripts/generate.py. If the action-level prompts glob does not include that script, changing it will not set dependencyChanged, so the action can take the no-change path even though Promptfoo executes the modified script.

AGENTS.md reference: AGENTS.md:L7-L9

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cecc8a028d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/utils/config.ts Outdated

try {
const values: unknown[] = [
loadYaml(fs.readFileSync(absolutePath, 'utf8'), {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve structured prompt symlinks before reading

When an untrusted checkout contains an in-workspace .yaml symlink whose target is outside the workspace, the lexical containment check passes and this new readFileSync dereferences the external target. If parsing fails, the catch forwards String(error) to the workflow log, and the bundled YAML parser includes source snippets in that error, potentially exposing target contents. Fresh evidence beyond the earlier direct-dependency symlink report is that this path actually reads and logs diagnostics from the target; resolve and validate the physical path before reading and avoid logging parser content.

AGENTS.md reference: AGENTS.md:L56-L60

Useful? React with 👍 / 👎.

Comment thread src/utils/config.ts Outdated
Comment on lines +155 to +158
!absolutePath ||
inspectedStructuredPrompts.has(absolutePath) ||
!fs.existsSync(absolutePath)
) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Inspect every structured prompt glob match

When a scalar prompt is a structured glob such as file://prompts/*.yaml, processFileUrl expands and records the matches, but this check probes the unresolved glob path itself and returns without inspecting any matched YAML. Consequently, a change only to a nested dependency outside prompts/, such as shared/system.txt, can still take the no-change skip path. Fresh evidence beyond the earlier structured-scalar report is the combined glob case: traverse each safe glob.sync match rather than testing the literal pattern.

AGENTS.md reference: AGENTS.md:L7-L9

Useful? React with 👍 / 👎.

Comment thread src/utils/config.ts Outdated
Comment on lines +145 to +147
const filePath = fileUrl.slice('file://'.length);
if (!/\.(?:json|ya?ml)$/i.test(filePath)) {
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Track dependencies imported by text prompt templates

When a scalar text prompt such as file://prompts/main.txt, main.md, or main.j2 contains a Nunjucks {% include %}, {% extends %}, or {% import %} directive, this extension guard returns without inspecting the template. A PR that changes only an imported partial outside the action-level prompt glob can therefore take the no-change skip path even though Promptfoo reloads that partial while rendering the prompt; inspect supported text templates or conservatively watch their dependency root when import directives are present.

AGENTS.md reference: AGENTS.md:L7-L9

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bfd156471b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/utils/config.ts Outdated
const contents = fs.readFileSync(physicalPath, 'utf8');
if (isTextTemplate) {
for (const match of contents.matchAll(
/\{%-?\s*(?:include|extends|import|from)\s+(['"])(.*?)\1/g,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Watch dynamic template imports

When a text prompt uses a valid dynamic Nunjucks import such as {% include partialName %}, this regex only recognizes directives whose argument begins with a quote and adds no conservative directory dependency for the unmatched directive. If partialName resolves to a partial outside the action-level prompt glob, changing that partial will not set dependencyChanged, so the evaluation can be skipped even though Promptfoo loads the modified template.

AGENTS.md reference: AGENTS.md:L7-L9

Useful? React with 👍 / 👎.

Comment thread src/utils/config.ts Outdated
Comment on lines +207 to +208
if (typeof value === 'string' && value.startsWith('file://')) {
processPromptFile(value);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan structured prompt strings for template imports

When a scalar YAML or JSON prompt contains a Nunjucks directive inside a prompt string, for example a YAML content block with {% include "shared/system.txt" %}, the structured traversal only processes strings beginning with file://. A change to the included partial can therefore miss dependencyChanged and take the no-change path when the action-level prompt glob does not cover that partial, even though the prompt renders it at evaluation time.

AGENTS.md reference: AGENTS.md:L7-L9

Useful? React with 👍 / 👎.

Comment thread src/utils/config.ts
/\.(?:py|[cm]?[jt]s)$/i.test(promptPath.slice(0, selector))
? promptPath.slice(0, selector)
: promptPath;
processPromptFile(`file://${filePath}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve action prompt overrides after dependency changes

When use-config-prompts is false and a PR changes only this newly tracked scalar config prompt dependency, src/main.ts sets dependencyChanged but its promptFiles list contains only changed matches from the action-level prompt globs. Because that list is empty, it omits --prompts and Promptfoo evaluates the config's prompts instead of the action-input prompt suite; if a prompt and dependency both change, it similarly evaluates only the changed prompt rather than every prompt affected by the dependency.

AGENTS.md reference: AGENTS.md:L7-L9

Useful? React with 👍 / 👎.

Comment thread src/utils/config.ts
Comment on lines +210 to +215
value &&
typeof value === 'object' &&
!inspectedValues.has(value)
) {
inspectedValues.add(value);
values.push(...Object.values(value));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid enumerating binary values during prompt traversal

When a structured YAML prompt contains binary metadata such as a large !!binary value, the YAML loader produces a typed byte array, but this branch treats it as a generic object and pushes every byte from Object.values onto values. A moderately sized embedded image can therefore create millions of queue entries and consume excessive memory and CPU during dependency extraction; recurse only through arrays and plain records, skipping typed arrays and other scalar object types.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: efcd39ac9f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/utils/config.ts
Comment on lines +279 to +281
.match(/^(['"])(.*?)\1|^(\S+)/);
if (executable) {
processPromptFile(`file://${executable[2] ?? executable[3]}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Track file arguments passed to executable prompts

When use-config-prompts is enabled and an executable scalar prompt receives a repository file, such as exec:./scripts/generate.sh ./templates/input.txt, this branch processes only the first command token. A PR changing only templates/input.txt can therefore take the no-change path when the action-level prompt glob does not cover it, even though the executed prompt consumes the modified file; inspect path-like command arguments or conservatively watch their directory.

AGENTS.md reference: AGENTS.md:L7-L9

Useful? React with 👍 / 👎.

Comment thread src/utils/config.ts
}

inspectedPromptFiles.add(absolutePath);
const contents = fs.readFileSync(physicalPath, 'utf8');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound structured prompt reads before parsing

When a config references a large checked-in structured prompt, every PR with changed-file metadata reaches this unconditional whole-file read before the action decides whether any monitored file changed. The subsequent YAML parsing and object traversal can consume excessive memory and CPU—or crash the action—even for an unrelated README-only PR; verify that the target is a regular file and enforce a size/traversal budget before reading it.

AGENTS.md reference: AGENTS.md:L7-L9

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants