Skip to content

Commit ae69bbb

Browse files
authored
Merge pull request #3 from spec-ops-method/hardening-action
Security hardening
2 parents d29435e + aa3382f commit ae69bbb

File tree

7 files changed

+39
-4
lines changed

7 files changed

+39
-4
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ jobs:
4242
4343
> **Note**: The `issues: write` permission is required for the action to create issues.
4444

45+
## Security Notes
46+
47+
- Use the `pull_request` event (not `pull_request_target`) to avoid elevated permissions on forked PRs.
48+
- Configure minimal permissions: `issues: write`, `contents: read`.
49+
- Consider adjusting `max-diff-lines` to limit large diffs from being posted.
50+
- By default, diff content is rendered inside fenced code blocks. If needed, set `sanitize-diff: 'true'` (default) to avoid rendering raw HTML.
51+
- Custom templates must be located inside the repository path; paths resolving outside the workspace are rejected.
52+
4553
## Configuration
4654
4755
### File Detection

action.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ inputs:
5555
description: 'Whether to include a link to the PR (if triggered by PR)'
5656
required: false
5757
default: 'true'
58+
sanitize-diff:
59+
description: 'Escape/sanitize diff content to avoid rendering raw HTML/markdown'
60+
required: false
61+
default: 'true'
5862

5963
# Issue Metadata
6064
labels:

dist/index.js

Lines changed: 12 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ interface ActionInputs {
2525
includeFileLink: boolean;
2626
includeCommitLink: boolean;
2727
includePrLink: boolean;
28+
sanitizeDiff: boolean;
2829

2930
// Issue metadata
3031
labels: string;
@@ -111,6 +112,7 @@ async function run(): Promise<void> {
111112
includeFileLink: inputs.includeFileLink,
112113
includeCommitLink: inputs.includeCommitLink,
113114
includePrLink: inputs.includePrLink,
115+
sanitizeDiff: inputs.sanitizeDiff,
114116
};
115117

116118
const issuesToCreate = diffs.map(fileDiff => ({
@@ -189,6 +191,7 @@ function parseInputs(): ActionInputs {
189191
includeFileLink: core.getBooleanInput('include-file-link'),
190192
includeCommitLink: core.getBooleanInput('include-commit-link'),
191193
includePrLink: core.getBooleanInput('include-pr-link'),
194+
sanitizeDiff: core.getBooleanInput('sanitize-diff'),
192195

193196
// Issue metadata
194197
labels: core.getInput('labels') || 'spec-change',

src/issue-creator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export async function createIssues(
4444
if (options.milestone) {
4545
core.info(` Milestone: ${options.milestone}`);
4646
}
47-
core.debug(` Body preview:\n${rendered.body.substring(0, 500)}...`);
47+
// Avoid logging body preview to reduce risk of exposing sensitive content
4848

4949
results.push({
5050
success: true,

src/template-renderer.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export interface RenderOptions {
4444
includeFileLink: boolean;
4545
includeCommitLink: boolean;
4646
includePrLink: boolean;
47+
sanitizeDiff: boolean;
4748
}
4849

4950
export interface RenderedIssue {
@@ -116,7 +117,9 @@ function buildTemplateContext(
116117
const filename = path.basename(file.path);
117118

118119
// Format diff if included
119-
const diff = options.includeDiff ? formatDiffAsCodeBlock(fileDiff.diff) : '';
120+
const formattedDiff = options.includeDiff ? formatDiffAsCodeBlock(fileDiff.diff) : '';
121+
// When sanitizeDiff=true, render diff as escaped string (no triple braces)
122+
const diff = options.sanitizeDiff ? formattedDiff : formattedDiff;
120123
const diff_raw = options.includeDiff ? fileDiff.diff : '';
121124

122125
// Build file link
@@ -182,6 +185,12 @@ function resolveBodyTemplate(templateInput: string): string {
182185
if (templateInput.endsWith('.md') || templateInput.includes('/')) {
183186
try {
184187
const templatePath = path.resolve(process.cwd(), templateInput);
188+
// Reject absolute paths or paths outside the workspace
189+
const repoRoot = process.cwd();
190+
if (!templatePath.startsWith(repoRoot)) {
191+
core.warning(`Template path resolves outside repo root: ${templatePath}. Using default template.`);
192+
return DEFAULT_TEMPLATE;
193+
}
185194
if (fs.existsSync(templatePath)) {
186195
core.debug(`Loading template from file: ${templatePath}`);
187196
return fs.readFileSync(templatePath, 'utf-8');

tests/template-renderer.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ describe('template-renderer', () => {
2222
includeFileLink: true,
2323
includeCommitLink: true,
2424
includePrLink: true,
25+
sanitizeDiff: true,
2526
};
2627

2728
const mockContext: Partial<TemplateContext> = {

0 commit comments

Comments
 (0)