Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Aug 19, 2025

This PR implements support for exposing outputs from agentic engines as workflow output variables, enabling downstream workflows and jobs to access results generated by AI agents. The implementation includes comprehensive security measures to protect against adversarial LLM outputs.

Implementation

The feature adds a complete pipeline for capturing agent outputs:

Pre-step: Output file setup

  • Uses actions/github-script@v7 to generate a random temp file under /tmp
  • Sets the GITHUB_AW_OUTPUT environment variable for subsequent steps
  • Creates an empty output file with robust directory and permission handling
  • Ensures /tmp directory exists and verifies successful file creation for reliable LLM appending

Prompt injection

  • Automatically injects instructions into the agent prompt explaining how to use the output file
  • Provides clear guidance on when and how to write meaningful output
  • Maintains backward compatibility with existing workflows

Environment variable passing

  • Ensures GITHUB_AW_OUTPUT is passed to both command-based and action-based engine execution
  • Works seamlessly with Claude, Codex, and other agentic engines

Post-step: Secure output collection

  • Reads content from the GITHUB_AW_OUTPUT file after agent execution
  • Applies comprehensive content sanitization to protect against adversarial LLM outputs:
    • Length protection: Content limited to 0.5MB to prevent DoS attacks
    • Line protection: Limited to 65,000 lines to prevent log flooding
    • Character filtering: Strips control characters and ANSI escape sequences
    • Markdown support: Preserves backticks for proper markdown formatting
    • Safe logging: Only first 200 characters logged to prevent sensitive data exposure
  • Sets the sanitized content as a GitHub Actions output under the key "output"
  • Gracefully handles missing or empty files

Job output declaration

  • Declares the "output" in the main job's outputs section
  • Makes the agent output available to dependent jobs via ${{ needs.job-name.outputs.output }}

Security Features

The sanitization implementation protects against common adversarial LLM attack vectors while allowing legitimate content:

// Example of the sanitization in action
function sanitizeContent(content) {
  // Remove control characters (except newlines and tabs)
  let sanitized = content.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
  
  // Limit total length to prevent DoS (0.5MB max)
  if (sanitized.length > 524288) {
    sanitized = sanitized.substring(0, 524288) + '\n[Content truncated due to length]';
  }
  
  // Limit lines to prevent log flooding (65k max)
  const lines = sanitized.split('\n');
  if (lines.length > 65000) {
    sanitized = lines.slice(0, 65000).join('\n') + '\n[Content truncated due to line count]';
  }
  
  // Remove ANSI escape sequences
  sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, '');
  
  // Preserve backticks for markdown support
  return sanitized.trim();
}

The implementation balances security with flexibility by removing overly restrictive pattern matching that could interfere with legitimate agent outputs while maintaining essential protections against DoS attacks and malicious content injection.

Example Usage

After this change, agentic workflows automatically support secure output collection:

# In a dependent job
needs: my-agentic-job
steps:
  - name: Use agent output
    run: echo "Agent said: ${{ needs.my-agentic-job.outputs.output }}"

The agent can write to the output file when it has meaningful results to share:

# Agent writes output (will be automatically sanitized)
echo "Analysis complete: 15 issues found, 3 critical" > $GITHUB_AW_OUTPUT

Demonstration

The test-claude workflow demonstrates this functionality by instructing the agent to write a haiku about analyzed PR changes to the action output. This provides a creative example of how agents can produce structured output that other workflows can consume safely.

Testing

Added comprehensive test coverage with TestAgenticOutputCollection() that verifies:

  • Correct step generation and ordering
  • Environment variable setup and passing
  • Prompt injection functionality
  • Job output declarations
  • Robust file creation and verification
  • Content sanitization functionality

All existing tests continue to pass, ensuring backward compatibility.

Fixes #115.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] [copilot] agent output collection Implement agentic output collection for workflow outputs Aug 19, 2025
Copilot AI requested a review from pelikhan August 19, 2025 17:13
// Set the environment variable for subsequent steps
const fs2 = require('fs');
fs2.appendFileSync(process.env.GITHUB_ENV, `GITHUB_AW_OUTPUT=${outputFile}\n`);
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot use actions/core helper to set the environment variable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done! Updated the implementation to use core.exportVariable('GITHUB_AW_OUTPUT', outputFile); instead of manually writing to process.env.GITHUB_ENV. This is cleaner and follows GitHub Actions best practices. Changes applied in commit dbb0a27.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Aug 19, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.contoso.com
    • Triggering command: /tmp/go-build1842958547/b278/cli.test -test.testlogfile=/tmp/go-build1842958547/b278/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true (dns block)
    • Triggering command: /tmp/go-build3576642426/b278/cli.test -test.testlogfile=/tmp/go-build3576642426/b278/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from pelikhan August 19, 2025 17:24
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Aug 19, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.contoso.com
    • Triggering command: /tmp/go-build2006687506/b278/cli.test -test.testlogfile=/tmp/go-build2006687506/b278/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true (dns block)
    • Triggering command: /tmp/go-build1348972971/b278/cli.test -test.testlogfile=/tmp/go-build1348972971/b278/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from pelikhan August 19, 2025 17:36
@pelikhan
Copy link
Contributor

@copilot update the prompt of test-claude.md and ask to respond in the action output with a haiku.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Aug 19, 2025

@copilot update the prompt of test-claude.md and ask to respond in the action output with a haiku.

Updated the test-claude.md prompt to include instructions for the agent to write a haiku about the analyzed PR changes to the action output. The agent will now create a creative and thoughtful poetic interpretation of the code changes and write it to the GITHUB_AW_OUTPUT file, demonstrating the new output collection functionality. Changes committed in a9d7d2c.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.contoso.com
    • Triggering command: /tmp/go-build3271223036/b278/cli.test -test.testlogfile=/tmp/go-build3271223036/b278/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title Implement agentic output collection for workflow outputs Implement agentic output collection for workflow outputs with haiku demonstration Aug 19, 2025
@pelikhan
Copy link
Contributor

@copilot update Claude agentic engine generated code/configuration to allow writing to the file pointed by GITHUB_AW_OUTPUT env variable.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Aug 19, 2025

@copilot update Claude agentic engine generated code/configuration to allow writing to the file pointed by GITHUB_AW_OUTPUT env variable.

Updated the Claude engine to pass the GITHUB_AW_OUTPUT environment variable to the Claude action through the claude_env input parameter. Claude agents can now write to the output file specified by this environment variable and have their output captured as workflow outputs. Changes committed in 243a9ef.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.contoso.com
    • Triggering command: /tmp/go-build3107026304/b278/cli.test -test.testlogfile=/tmp/go-build3107026304/b278/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true (dns block)
    • Triggering command: /tmp/go-build2613811768/b278/cli.test -test.testlogfile=/tmp/go-build2613811768/b278/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true (dns block)
    • Triggering command: /tmp/go-build3614438541/b278/cli.test -test.testlogfile=/tmp/go-build3614438541/b278/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

yaml.WriteString(" let sanitized = content.replace(/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]/g, '');\n")
yaml.WriteString(" \n")
yaml.WriteString(" // Limit total length to prevent DoS (64KB max)\n")
yaml.WriteString(" const maxLength = 65536;\n")
Copy link
Contributor

Choose a reason for hiding this comment

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

Allow ~1Mb

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated the length limit from 64KB to 1MB (1,048,576 bytes) as requested. Changes committed in c51f5f0.

yaml.WriteString(" \n")
yaml.WriteString(" // Limit number of lines to prevent log flooding (1000 max)\n")
yaml.WriteString(" const lines = sanitized.split('\\n');\n")
yaml.WriteString(" const maxLines = 1000;\n")
Copy link
Contributor

Choose a reason for hiding this comment

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

Allow ~65k lines

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated the line limit from 1,000 to 65,000 lines as requested. Changes committed in c51f5f0.

yaml.WriteString(" \n")
yaml.WriteString(" // Remove potential shell injection patterns\n")
yaml.WriteString(" sanitized = sanitized.replace(/\\$\\([^)]*\\)/g, '[shell-command-removed]');\n")
yaml.WriteString(" sanitized = sanitized.replace(/`[^`]*`/g, '[backtick-content-removed]');\n")
Copy link
Contributor

Choose a reason for hiding this comment

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

allow backtick (allow markdown)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed the backtick sanitization to allow markdown formatting. Backticks are now preserved in the output. Changes committed in c51f5f0.

Copy link
Contributor

@pelikhan pelikhan left a comment

Choose a reason for hiding this comment

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

@copilot apply comments

…s, and markdown backticks

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Aug 19, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.contoso.com
    • Triggering command: /tmp/go-build2047769255/b278/cli.test -test.testlogfile=/tmp/go-build2047769255/b278/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from pelikhan August 19, 2025 23:44
yaml.WriteString(" // Remove control characters (except newlines and tabs)\n")
yaml.WriteString(" let sanitized = content.replace(/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]/g, '');\n")
yaml.WriteString(" \n")
yaml.WriteString(" // Limit total length to prevent DoS (1MB max)\n")
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot reduce max length to 0.5mb

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reduced the maximum content length limit from 1MB to 0.5MB (524,288 bytes) in the content sanitization function. Changes committed in b737de5.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Aug 19, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.contoso.com
    • Triggering command: /tmp/go-build2787588644/b278/cli.test -test.testlogfile=/tmp/go-build2787588644/b278/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from pelikhan August 19, 2025 23:54
@pelikhan pelikhan marked this pull request as ready for review August 19, 2025 23:57
yaml.WriteString(" }\n")
yaml.WriteString(" \n")
yaml.WriteString(" // Remove potential shell injection patterns\n")
yaml.WriteString(" sanitized = sanitized.replace(/\\$\\([^)]*\\)/g, '[shell-command-removed]');\n")
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot remove this line

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed the shell injection pattern sanitization line as requested. This allows more flexibility in legitimate agent outputs that may include command-like patterns while maintaining other security protections. Changes committed in f539410.

@pelikhan
Copy link
Contributor

@copilot remove the shell $ sanitization line

- Removed the line that removes $(command) patterns from sanitized content
- Allows more flexibility in legitimate agent outputs that may include command-like patterns
- Maintains other security protections (length limits, line limits, control character removal)
- Updated all compiled workflow files to reflect the change

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Aug 20, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.contoso.com
    • Triggering command: /tmp/go-build1551828233/b278/cli.test -test.testlogfile=/tmp/go-build1551828233/b278/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copy link
Contributor Author

Copilot AI commented Aug 20, 2025

@copilot remove the shell $ sanitization line

The shell $ sanitization line has already been removed in commit f539410. The line that previously removed $(command) patterns from the sanitized content is no longer present in the code. The current sanitization function only includes control character removal and ANSI escape sequence removal, with no shell-related sanitization remaining.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.contoso.com
    • Triggering command: /tmp/go-build507411193/b278/cli.test -test.testlogfile=/tmp/go-build507411193/b278/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true (dns block)
    • Triggering command: /tmp/go-build813945597/b278/cli.test -test.testlogfile=/tmp/go-build813945597/b278/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from pelikhan August 20, 2025 00:48
@pelikhan pelikhan merged commit 79ef37c into main Aug 20, 2025
9 checks passed
@pelikhan pelikhan deleted the copilot/fix-115 branch August 20, 2025 00:51
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.

[copilot] agent output collection

2 participants