From fbbe4562d9fe0577e0c80ac98bfc87899f44d1b1 Mon Sep 17 00:00:00 2001 From: mc <42146119+mchammer01@users.noreply.github.com> Date: Fri, 15 May 2026 09:45:24 +0100 Subject: [PATCH 1/2] Add GitHub Secret Protection adoption landing page (#61160) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../secret-security/secret-leakage-risks.md | 2 + ...ctices-for-selecting-pilot-repositories.md | 2 + content/code-security/tutorials/index.md | 1 + .../secret-protection-adoption-path.md | 72 +++++++++++++++++++ .../preparing-for-security-incidents.md | 2 + 5 files changed, 79 insertions(+) create mode 100644 content/code-security/tutorials/secret-protection-adoption-path.md diff --git a/content/code-security/concepts/secret-security/secret-leakage-risks.md b/content/code-security/concepts/secret-security/secret-leakage-risks.md index 270b70c58e17..769790d1ab75 100644 --- a/content/code-security/concepts/secret-security/secret-leakage-risks.md +++ b/content/code-security/concepts/secret-security/secret-leakage-risks.md @@ -7,6 +7,8 @@ versions: ghec: '*' ghes: '*' contentType: concepts +category: + - Protect your secrets --- ## What are secrets? diff --git a/content/code-security/concepts/security-at-scale/best-practices-for-selecting-pilot-repositories.md b/content/code-security/concepts/security-at-scale/best-practices-for-selecting-pilot-repositories.md index 18ade67c6b74..f91a3e8b2dbf 100644 --- a/content/code-security/concepts/security-at-scale/best-practices-for-selecting-pilot-repositories.md +++ b/content/code-security/concepts/security-at-scale/best-practices-for-selecting-pilot-repositories.md @@ -7,6 +7,8 @@ versions: ghec: '*' ghes: '*' contentType: concepts +category: + - Secure at scale --- Before enabling {% data variables.product.prodname_GH_secret_protection %} organization-wide, run a pilot to validate the solution with a small set of repositories. A pilot helps you refine your rollout strategy, identify workflow adjustments, and demonstrate security value to stakeholders. This article will help you choose the best repositories for your pilot. diff --git a/content/code-security/tutorials/index.md b/content/code-security/tutorials/index.md index 5f51bc16da54..0285d5ebedfb 100644 --- a/content/code-security/tutorials/index.md +++ b/content/code-security/tutorials/index.md @@ -8,6 +8,7 @@ versions: ghec: '*' contentType: tutorials children: + - /secret-protection-adoption-path - /trialing-github-advanced-security - /adopting-github-advanced-security-at-scale - /secure-your-organization diff --git a/content/code-security/tutorials/secret-protection-adoption-path.md b/content/code-security/tutorials/secret-protection-adoption-path.md new file mode 100644 index 000000000000..7ce578a7a2f2 --- /dev/null +++ b/content/code-security/tutorials/secret-protection-adoption-path.md @@ -0,0 +1,72 @@ +--- +title: Secure your secrets at scale with GitHub +shortTitle: Secret protection +allowTitleToDifferFromFilename: true +intro: 'Leaked credentials expose your organization to data breaches. GitHub Secret Protection detects and prevents secret leaks automatically. Follow this adoption path to assess risk, pilot the solution, and scale protection organization-wide.' +layout: journey-landing +versions: + feature: secret-risk-assessment +contentType: tutorials +category: + - Secure at scale + - Protect your secrets +journeyArticlesHeading: 'All secret protection articles' +journeyTracks: + - id: 'quick_start' + title: 'Quick start: Essential reading' + description: 'New to secret protection? Start here for the most important concepts and procedures. These articles provide foundational knowledge to help you understand secret risks, evaluate GitHub Secret Protection (GHSP), and begin your adoption journey.' + guides: + - href: '/code-security/concepts/secret-security/secret-leakage-risks' + - href: '/code-security/concepts/secret-security/about-secret-scanning' + - href: '/code-security/tutorials/secure-your-organization/interpreting-secret-risk-assessment-results' + - href: '/code-security/tutorials/remediate-leaked-secrets/assessing-ghsp-impact' + - href: '/code-security/concepts/secret-security/about-push-protection' + - id: 'assess' + title: 'Phase 1: Assess your current secret risk' + description: 'Run a free secret risk assessment (SRA) to understand your organization exposure and establish baseline metrics. Before purchasing GHSP, identify how many secrets are exposed across your organization and build a data-driven business case for the investment.' + timeCommitment: '10 minutes to run, 30 minutes to analyze results' + guides: + - href: '/code-security/concepts/secret-security/secret-leakage-risks' + - href: '/code-security/how-tos/secure-at-scale/configure-organization-security/configure-specific-tools/assess-your-secret-risk' + - href: '/code-security/tutorials/secure-your-organization/interpreting-secret-risk-assessment-results' + - href: '/code-security/how-tos/secure-at-scale/configure-organization-security/configure-specific-tools/viewing-your-security-risk-assessment-reports' + - id: 'evaluate' + title: 'Phase 2: Evaluate GitHub Secret Protection' + description: 'Determine if GHSP meets your needs and build a business case. Review detection capabilities, push protection features, and validity checking. Use the pricing calculator to estimate costs and calculate potential cost savings from preventing manual remediation.' + timeCommitment: '2-4 hours' + guides: + - href: '/code-security/concepts/secret-security/about-secret-scanning' + - href: '/code-security/concepts/secret-security/about-push-protection' + - href: '/code-security/reference/secret-security/supported-secret-scanning-patterns' + - href: '/code-security/how-tos/secure-at-scale/configure-organization-security/configure-specific-tools/estimating-the-price-of-secret-protection' + - href: '/code-security/tutorials/remediate-leaked-secrets/calculating-the-cost-savings-of-push-protection' + - id: 'pilot' + title: 'Phase 3: Pilot GitHub Secret Protection' + description: 'Run a pilot to validate GHSP with a small set of repositories before organization-wide enablement. Select 5-10 repositories with active development and known secret exposure. If you estimated pricing in Phase 2, you''ll confirm costs as part of the enablement flow. A successful pilot demonstrates security value quickly, identifies workflow adjustments, and gathers feedback to refine your rollout strategy.' + timeCommitment: '2-4 weeks' + guides: + - href: '/code-security/concepts/security-at-scale/best-practices-for-selecting-pilot-repositories' + - href: '/code-security/how-tos/secure-at-scale/configure-organization-security/configure-specific-tools/protect-your-secrets' + - href: '/code-security/how-tos/secure-your-secrets/prevent-future-leaks/enabling-push-protection-for-your-repository' + - href: '/code-security/tutorials/remediate-leaked-secrets/remediating-a-leaked-secret' + - id: 'monitor' + title: 'Phase 4: Monitor and assess value' + description: 'Track metrics to demonstrate ROI and identify areas for improvement. Monitor how many secrets are being detected, how often developers bypass push protection, and how quickly leaked secrets are remediated. Use these insights to refine your rollout strategy, prove value to stakeholders, and justify organization-wide deployment.' + timeCommitment: '1-2 hours per week during pilot' + guides: + - href: '/code-security/tutorials/remediate-leaked-secrets/assessing-ghsp-impact' + - href: '/code-security/concepts/secret-security/push-protection-metrics' + - href: '/code-security/tutorials/secure-your-organization/organizing-remediation-efforts-for-leaked-secrets' + - href: '/code-security/tutorials/remediate-leaked-secrets/evaluating-alerts' + - id: 'scale' + title: 'Phase 5: Scale, customize, and automate' + description: 'Expand GHSP organization-wide and tailor it to your specific workflows. Use validity checks to prioritize remediation, define custom patterns for organization-specific secrets, and apply security configurations at scale. For advanced use cases, enable AI-powered detection and integrate with automated workflows.' + timeCommitment: '1-2 weeks for initial rollout, ongoing for optimization' + guides: + - href: '/code-security/how-tos/secure-at-scale/configure-organization-security/establish-complete-coverage/applying-a-custom-security-configuration' + - href: '/code-security/how-tos/secure-your-secrets/customize-leak-detection/defining-custom-patterns-for-secret-scanning' + - href: '/code-security/how-tos/secure-your-secrets/manage-bypass-requests/enabling-delegated-bypass-for-push-protection' + - href: '/code-security/how-tos/secure-your-secrets/detect-secret-leaks/enabling-secret-scanning-for-non-provider-patterns' + - href: '/code-security/how-tos/secure-your-secrets/detect-secret-leaks/enabling-ai-powered-generic-secret-detection' + - href: '/code-security/how-tos/use-ghas-with-ai-coding-agents/scan-for-secrets-with-github-mcp-server' +--- \ No newline at end of file diff --git a/content/code-security/tutorials/secure-your-organization/preparing-for-security-incidents.md b/content/code-security/tutorials/secure-your-organization/preparing-for-security-incidents.md index f7c6a6e52cfb..b8bc02aaa668 100644 --- a/content/code-security/tutorials/secure-your-organization/preparing-for-security-incidents.md +++ b/content/code-security/tutorials/secure-your-organization/preparing-for-security-incidents.md @@ -8,6 +8,8 @@ versions: ghec: '*' ghes: '*' contentType: tutorials +category: + - Secure at scale --- The guidance in this article is aimed at enterprise owners, organization owners, security managers and security teams. However, you will need to have the enterprise owner role to enable several of the features referenced in this article. From c6af007cfe9f759431f3949a5f2d0df1f479b6a9 Mon Sep 17 00:00:00 2001 From: Ryosuke Nakayama Date: Fri, 15 May 2026 23:14:57 +0900 Subject: [PATCH 2/2] fix: rejoin dangling markers and don't split Liquid tags in headings (#61254) --- .../lib/correct-translation-content.ts | 57 +++++++++++-- .../tests/correct-translation-content.ts | 82 +++++++++++++++++-- 2 files changed, 127 insertions(+), 12 deletions(-) diff --git a/src/languages/lib/correct-translation-content.ts b/src/languages/lib/correct-translation-content.ts index 37e4b6c36ebb..9fc1c05b0bb7 100644 --- a/src/languages/lib/correct-translation-content.ts +++ b/src/languages/lib/correct-translation-content.ts @@ -1914,8 +1914,13 @@ export function correctTranslatedContentStrings( else englishSpaces.add(m[0]) } if (englishLinebreaks.size > 0) { - content = content.replace(/\{%(.+?)%\} /g, (match) => { + content = content.replace(/\{%(.+?)%\} /g, (match, _p1, offset, string) => { if (match.lastIndexOf('{%') > 0) return match + // Don't inject a linebreak when the tag is inside a heading line — doing + // so would split `#### {% data X %} Japanese text` into a heading with + // no content followed by a loose paragraph of Japanese text. + const lineStart = (string as string).lastIndexOf('\n', offset) + 1 + if (/^[ \t]{0,3}#{1,6}/.test((string as string).slice(lineStart, offset))) return match const withLinebreak = `${match.slice(0, -1)}\n` if (englishLinebreaks.has(withLinebreak) && !englishSpaces.has(match)) { return withLinebreak @@ -2058,11 +2063,12 @@ export function correctTranslatedContentStrings( * Rejoin marker lines that the translation pipeline split from their content. * * Translators sometimes leave a heading marker (`#`/`##`/...), blockquote - * marker (`>`), or the opening `**` of a bold span (immediately following a - * list/heading/blockquote/table marker) on its own line, with the rest of - * the content pushed to the next line as deeply indented text. This breaks - * rendering (empty headings, broken blockquotes, unrendered bold, unexpanded - * Liquid and `[AUTOTITLE]` links). + * marker (`>`), ordered-list marker (`1.`, `2.`, ...), or the opening `**` + * of a bold span (immediately following a list/heading/blockquote/table + * marker) on its own line, with the rest of the content pushed to the next + * line as deeply indented text. This breaks rendering (empty headings, broken + * blockquotes, broken ordered lists rendered as code blocks, unrendered bold, + * unexpanded Liquid and `[AUTOTITLE]` links). * * Conservative thresholds: * - Marker line has 0–3 leading spaces (CommonMark heading/blockquote rule). @@ -2081,11 +2087,21 @@ function joinDanglingMarkers(content: string): string { // Marker-only line patterns (run only against non-fenced, non-frontmatter lines). const headingOnly = /^([ \t]{0,3})(#{1,6})[ \t]*$/ const blockquoteOnly = /^([ \t]{0,3}>)[ \t]*$/ + // Ordered-list marker alone on a line: `1. \n content`. + const orderedListOnly = /^([ \t]{0,3}\d+\.)[ \t]*$/ // Bold-open after a list/heading/blockquote/table marker (no other content). const markerThenBoldOnly = /^([ \t]{0,3}(?:[*+-]|\d+\.)[ \t]+|[ \t]{0,3}>[ \t]+|[ \t]{0,3}#{1,6}[ \t]+|\|[ \t]*)\*\*[ \t]*$/ // Continuation: 6+ leading spaces and at least one non-whitespace character. + // Used when checking whether the *next* line is a deeply-indented continuation + // after a recognised marker. const deepIndented = /^[ \t]{6,}(\S.*)$/ + // Standalone deeply-indented paragraph: 9+ leading spaces. Translation + // artifacts consistently use 14 spaces; legitimate list-continuation content + // uses at most 6 spaces (confirmed by corpus analysis). The 9+ threshold + // keeps the two populations well separated and is fence-safe after the + // improved fence detection above. + const veryDeepIndented = /^[ \t]{9,}(\S.*)$/ for (let i = 0; i < lines.length; i++) { const line = lines[i] @@ -2108,7 +2124,12 @@ function joinDanglingMarkers(content: string): string { } // CommonMark fenced code block: 0–3 leading spaces, then 3+ ` or ~. - const fenceMatch = line.match(/^[ \t]{0,3}(`{3,}|~{3,})/) + // CommonMark permits fences to be indented 0–3 spaces at the document + // level, but inside a list item a fence can appear at 4+ spaces of + // leading indentation. Use `^[ \t]*` so that code blocks nested inside + // list items (e.g. ` ```json`) are correctly recognised and their + // content is not inadvertently stripped by the selfStrip pass below. + const fenceMatch = line.match(/^[ \t]*(`{3,}|~{3,})/) if (fenceMatch) { const marker = fenceMatch[1] if (!inFence) { @@ -2129,6 +2150,21 @@ function joinDanglingMarkers(content: string): string { continue } + // A line that itself starts with 9+ spaces and is not inside a code fence + // is a translation-pipeline corruption artifact: the pipeline indented an + // entire paragraph line, causing CommonMark to render it as an indented + // code block (4+ spaces at the document level = code block). Strip the + // leading whitespace so the content renders as a normal paragraph. + // Marker-only lines (headings `# `, blockquotes `> `, list items `1. `) + // always have ≤3 leading spaces, so they are never misidentified here. + // The 9+ threshold (vs the 6+ used for nextDeep) ensures that legitimate + // list-continuation lines (which use ≤6 spaces) are never stripped. + const selfStrip = line.match(veryDeepIndented) + if (selfStrip) { + out.push(selfStrip[1]) + continue + } + const next = i + 1 < lines.length ? lines[i + 1] : undefined const nextDeep = next !== undefined ? next.match(deepIndented) : null if (!nextDeep) { @@ -2151,6 +2187,13 @@ function joinDanglingMarkers(content: string): string { continue } + const ol = line.match(orderedListOnly) + if (ol) { + out.push(`${ol[1]} ${nextContent}`) + i++ + continue + } + const boldOpen = line.match(markerThenBoldOnly) if (boldOpen) { out.push(`${boldOpen[1]}**${nextContent}`) diff --git a/src/languages/tests/correct-translation-content.ts b/src/languages/tests/correct-translation-content.ts index 3680fecce191..a46fc7d30c53 100644 --- a/src/languages/tests/correct-translation-content.ts +++ b/src/languages/tests/correct-translation-content.ts @@ -1556,6 +1556,17 @@ describe('correctTranslatedContentStrings', () => { expect(fix(translated, 'es', en)).toBe('{% endif %}\n| Column |') }) + test('does not inject linebreak after data tag that is mid-heading', () => { + // English: tag is at end of heading line → English has tag+newline. + // Japanese: tag is mid-heading, followed by Japanese text. + // The linebreak recovery must NOT replace the space with a newline here, + // or the heading gets split into `#### TAG` + `Japanese text` paragraph. + const en = '#### Using {% data variables.copilot.subagents_short %}\n\nSome paragraph.' + const translated = + '#### {% data variables.copilot.subagents_short %} の使用\n\nSome paragraph.' + expect(fix(translated, 'ja', en)).toBe(translated) + }) + test('fixes collapsed Markdown table rows', () => { expect(fix('Cell1 | | Cell2', 'es')).toBe('Cell1 |\n| Cell2') }) @@ -1611,8 +1622,9 @@ describe('correctTranslatedContentStrings', () => { expect(fix(' ### \n Title', 'ja')).toBe(' ### Title') // Valid headings are not modified expect(fix('### Already correct', 'ja')).toBe('### Already correct') - // 4-space indented heading-like text is not collapsed (looks like code) - expect(fix(' ###\n code', 'ja')).toBe(' ###\n code') + // 4-space indented heading-like text is not collapsed (no marker join); + // but selfStrip still removes the 14-space indentation from the next line. + expect(fix(' ###\n code', 'ja')).toBe(' ###\ncode') // Shallow next-line indent (<6) is not collapsed expect(fix('### \n Title', 'ja')).toBe('### \n Title') }) @@ -1646,9 +1658,29 @@ describe('correctTranslatedContentStrings', () => { expect(fix('> **\n Quoted bold**', 'ja')).toBe('> **Quoted bold**') // Table cell expect(fix('| **\n Cell bold** | x', 'ja')).toBe('| **Cell bold** | x') - // Bare `**` (no preceding marker) is not collapsed — could be a closing - // bold marker followed by legitimate indented continuation. - expect(fix('**\n text', 'ja')).toBe('**\n text') + // Bare `**` (no preceding marker) is not marker-joined, but selfStrip + // still removes the 14-space indentation from the next line so it does + // not render as an indented code block. + expect(fix('**\n text', 'ja')).toBe('**\ntext') + }) + + test('rejoins dangling ordered-list markers (all languages)', () => { + const broken = + '1. \n {% data variables.product.prodname_vscode %}では、サイドバーの拡張機能アイコンをクリックします。' + const expected = + '1. {% data variables.product.prodname_vscode %}では、サイドバーの拡張機能アイコンをクリックします。' + for (const lang of ['ja', 'de', 'es', 'fr', 'ko', 'pt', 'ru', 'zh']) { + expect(fix(broken, lang)).toBe(expected) + } + // Higher numbered items + expect(fix('2. \n Content', 'ja')).toBe('2. Content') + expect(fix('10. \n Content', 'ja')).toBe('10. Content') + // 0–3 leading spaces are accepted + expect(fix(' 1. \n Indented', 'ja')).toBe(' 1. Indented') + // Valid ordered list items are not modified + expect(fix('1. Already correct', 'ja')).toBe('1. Already correct') + // Shallow next-line indent (<6 spaces) is not collapsed + expect(fix('1. \n Content', 'ja')).toBe('1. \n Content') }) test('does not modify content inside fenced code blocks', () => { @@ -1723,6 +1755,46 @@ intro: | const nested = '1. Run this command:\n\n gh auth login' expect(fix(nested, 'ja')).toBe(nested) }) + + test('strips standalone deeply-indented paragraph lines (all languages)', () => { + // The translation pipeline sometimes indents an entire paragraph line + // with 14 spaces, causing it to render as a code block at the document + // level. Such lines should have their leading whitespace stripped. + const broken = + '### MCP サーバーの手動での構成\n\n {% data variables.product.prodname_vscode %}で MCP サーバーを構成するには、...' + const expected = + '### MCP サーバーの手動での構成\n\n{% data variables.product.prodname_vscode %}で MCP サーバーを構成するには、...' + for (const lang of ['ja', 'de', 'es', 'fr', 'ko', 'pt', 'ru', 'zh']) { + expect(fix(broken, lang)).toBe(expected) + } + // 9 spaces is the minimum threshold + expect(fix(' content', 'ja')).toBe('content') + // 8 spaces is below threshold and should be preserved + expect(fix(' content', 'ja')).toBe(' content') + // Standalone 14-space line mid-document + expect(fix('Para one.\n\n Para two.\n\nPara three.', 'ja')).toBe( + 'Para one.\n\nPara two.\n\nPara three.', + ) + }) + + test('does not strip content inside 4-space-indented fences (list code blocks)', () => { + // A fenced code block that itself lives inside a list item is indented + // by 4 spaces. Its content may have 6–25 spaces of leading whitespace + // but must NOT be stripped. + const fenced = [ + '1. Add this config:', + '', + ' ```json copy', + ' {', + ' "key": "value",', + ' "nested": {', + ' "deep": true', + ' }', + ' }', + ' ```', + ].join('\n') + expect(fix(fenced, 'ja')).toBe(fenced) + }) }) // ─── EDGE CASES ────────────────────────────────────────────────────