Skip to content

[Safe Outputs Conformance] SEC-005: setup_comment_memory_files.cjs supports target-repo without allowlist validation #27699

@github-actions

Description

@github-actions

Conformance Check Failure

Check ID: SEC-005
Severity: HIGH
Category: Security — Cross-Repository Validation

Problem Description

actions/setup/js/setup_comment_memory_files.cjs reads comment memory from a GitHub-configured target-repo, which can point to any repository. Unlike dispatch_workflow.cjs and dispatch_repository.cjs (which enforce an explicit allowed_repos allowlist via validateTargetRepo and throw E004 on violation), this handler resolves the target repository and accesses GitHub Issues API with no allowlist gate.

This violates the Safe Outputs Specification §3.2.6 (SP6): cross-repository access requires an explicit allowlist with default-deny semantics.

Affected Components

  • File: actions/setup/js/setup_comment_memory_files.cjs
  • Function: resolveTargetRepo (line ~54) and collectCommentMemoryFiles (line ~64)
🔍 Current vs Expected Behavior

Current Behavior

resolveTargetRepo accepts any target-repo value from config and constructs an { owner, repo, slug } object without validating it against an allowlist:

function resolveTargetRepo(commentMemoryConfig) {
  const configuredRepo = String(commentMemoryConfig?.["target-repo"] || "").trim();
  const repoSlug = configuredRepo || `\$\{context.repo.owner}/\$\{context.repo.repo}`;
  // No allowlist check here
  return { owner, repo, slug: ... };
}

Expected Behavior

When target-repo resolves to a repo other than the current context repo, an allowlist check must be performed (matching the pattern in dispatch_workflow.cjs):

if (isCrossRepo) {
  if (allowedRepos.size === 0) {
    throw new Error("E004: Cross-repo comment-memory access denied. No allowlist configured.");
  }
  const validation = validateTargetRepo(resolvedSlug, contextSlug, allowedRepos);
  if (!validation.valid) throw new Error(`E004: \$\{validation.error}`);
}

Alternatively, add a @safe-outputs-exempt SEC-005 annotation with a documented justification if the cross-repo access is intentionally unrestricted by design.

Remediation Steps

This task can be assigned to a Copilot coding agent with the following steps:

  1. In setup_comment_memory_files.cjs, compute isCrossRepo by comparing the resolved targetRepo.slug against \$\{context.repo.owner}/\$\{context.repo.repo}.
  2. If isCrossRepo is true, read an allowed_repos list from the handler config (consistent with how dispatch_workflow.cjs reads it).
  3. Call validateTargetRepo (from repo_helpers.cjs) and throw E004 if the resolved repo is not in the allowlist or no allowlist is set.
  4. If the feature is intentionally unrestricted, add // @safe-outputs-exempt SEC-005: <reason> and document why in the specification.

Verification

After remediation, verify by running:

bash scripts/check-safe-outputs-conformance.sh

Check SEC-005 should pass without errors.

References

  • Safe Outputs Specification: docs/src/content/docs/reference/safe-outputs-specification.md
  • Reference implementation: actions/setup/js/dispatch_workflow.cjs (lines 50–65)
  • Conformance Checker: scripts/check-safe-outputs-conformance.sh
  • Run ID: §24748719577
  • Date: 2026-04-21

Generated by Daily Safe Outputs Conformance Checker · ● 110.8K ·

  • expires on Apr 22, 2026, 10:07 PM UTC

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions