Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions actions/setup/js/check_daily_aic_workflow_guardrail.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,38 @@ function logDailyGuardrail(message, details) {
core.info(formatDailyGuardrailLogMessage(message, details));
}

/**
* Event types that indicate a user-initiated slash command trigger.
* When aw_context.event_type is one of these, the workflow was triggered by a user
* typing a slash command in a comment, and the daily guardrail should not be skipped.
*/
const SLASH_COMMAND_EVENT_TYPES = ["issue_comment", "pull_request_review_comment", "discussion_comment"];

/**
* @returns {boolean}
*/
function shouldSkipDailyAICGuardrail() {
const eventName = process.env.GITHUB_EVENT_NAME || "";
const isWorkflowCall = eventName === "workflow_call";
const isRepositoryDispatch = eventName === "repository_dispatch";
const hasDispatchContext = (process.env.GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT || "").trim() !== "";
return isWorkflowCall || isRepositoryDispatch || (eventName === "workflow_dispatch" && hasDispatchContext);
const rawContext = (process.env.GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT || "").trim();
const hasDispatchContext = rawContext !== "";
if (!(isWorkflowCall || isRepositoryDispatch || (eventName === "workflow_dispatch" && hasDispatchContext))) {
return false;
}
if (eventName === "workflow_dispatch" && hasDispatchContext) {
try {
const awContext = JSON.parse(rawContext);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

JSON.parse of the literal string "null" silently lands in the catch block via a TypeError, not a SyntaxError, making the fallback behaviour correct by accident rather than by intent.

💡 Suggested fix

Add a type guard after parsing so that null and other non-object JSON values are handled explicitly:

const awContext = JSON.parse(rawContext);
if (awContext !== null && typeof awContext === "object") {
  const isLabelCommand = typeof awContext.trigger_label === "string" && awContext.trigger_label.trim() !== "";
  const isSlashCommand = SLASH_COMMAND_EVENT_TYPES.includes(awContext.event_type);
  if (isLabelCommand || isSlashCommand) {
    return false;
  }
}

JSON.parse("null") returns null (valid JSON), then null.trigger_label throws TypeError. The catch {} catches it and falls through to return true, which happens to be the right behaviour, but the code comment says "Malformed aw_context" — null is not malformed JSON, so the reasoning is wrong even though the outcome is the same. A type guard makes the contract explicit and avoids confusion for future readers.

const isLabelCommand = typeof awContext.trigger_label === "string" && awContext.trigger_label.trim() !== "";
const isSlashCommand = SLASH_COMMAND_EVENT_TYPES.includes(awContext.event_type);
if (isLabelCommand || isSlashCommand) {
return false;
}
} catch {
// Malformed aw_context: fall through and skip as before.
}
}
return true;
}

/**
Expand Down
36 changes: 35 additions & 1 deletion actions/setup/js/check_daily_aic_workflow_guardrail.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,47 @@ describe("check_daily_aic_workflow_guardrail", () => {
expect(exports.shouldSkipDailyAICGuardrail()).toBe(true);

process.env.GITHUB_EVENT_NAME = "workflow_dispatch";
process.env.GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT = '{"item_number":123}';
process.env.GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT = '{"event_type":"schedule"}';
expect(exports.shouldSkipDailyAICGuardrail()).toBe(true);

process.env.GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT = '{"event_type":"workflow_dispatch"}';
expect(exports.shouldSkipDailyAICGuardrail()).toBe(true);

process.env.GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT = "";
expect(exports.shouldSkipDailyAICGuardrail()).toBe(false);
});

it("does not skip for label command triggers in aw_context", () => {
process.env.GITHUB_EVENT_NAME = "workflow_dispatch";
process.env.GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT = '{"event_type":"pull_request","trigger_label":"smoke"}';
expect(exports.shouldSkipDailyAICGuardrail()).toBe(false);

process.env.GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT = '{"event_type":"issues","trigger_label":"ci-doctor"}';
expect(exports.shouldSkipDailyAICGuardrail()).toBe(false);
});

it("does not skip for slash command triggers in aw_context", () => {
process.env.GITHUB_EVENT_NAME = "workflow_dispatch";
process.env.GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT = '{"event_type":"issue_comment","trigger_label":""}';
expect(exports.shouldSkipDailyAICGuardrail()).toBe(false);

process.env.GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT = '{"event_type":"pull_request_review_comment"}';
expect(exports.shouldSkipDailyAICGuardrail()).toBe(false);

process.env.GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT = '{"event_type":"discussion_comment"}';
expect(exports.shouldSkipDailyAICGuardrail()).toBe(false);
});

it("skips for workflow_dispatch with aw_context that has no trigger_label and non-slash event_type", () => {
process.env.GITHUB_EVENT_NAME = "workflow_dispatch";
process.env.GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT = '{"event_type":"push","trigger_label":""}';
expect(exports.shouldSkipDailyAICGuardrail()).toBe(true);

// Malformed JSON should still skip (safe fallback)
process.env.GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT = "not-json";
expect(exports.shouldSkipDailyAICGuardrail()).toBe(true);
});

it("matches usage artifacts only", () => {
expect(exports.matchesGuardrailArtifactName("usage")).toBe(true);
expect(exports.matchesGuardrailArtifactName("prefix-usage")).toBe(true);
Expand Down
Loading