Commit Graph

1864 Commits

  • test(ci): coverage for round-1 fixes (quoted write-all, dedup, lifecycle scope)
    Three test changes in response to the round-1 review:
    
    1. **Add quoted-write-all coverage** (cubic P0 follow-up).
       Two new cases assert the regex now matches the double-quoted and
       single-quoted YAML forms of `permissions: "write-all"`:
         - `rejects double-quoted permissions: "write-all"`
         - `rejects single-quoted permissions: 'write-all'`
       Both fixtures trigger only the persist-credentials gate, so they
       exercise the WRITE_ALL_PATTERN OR-clause in isolation.
    
    2. **Add expression+ref dedup coverage** (greptile P2 follow-up).
       `emits a single violation when both expressionPattern and refPattern
       match the same step` — uses `refs/pull/${{ … head.sha }}/merge` as
       the fixture (which matches both patterns) and counts ERROR lines for
       the `pull_request_target` rule, asserting exactly one. Re-introducing
       the duplicate-push bug would re-fail this test immediately.
    
    3. **Drop the `npm ci without --ignore-scripts under write-all` test**
       (greptile P2). That test happened to pass under the previous
       `--ignore-scripts` regex, but `UNSAFE_INSTALL_PATTERNS` (added in
       `f7035b56`) fires unconditionally for every workflow regardless of
       permissions. So the test was exercising a pre-existing code path
       that has nothing to do with WRITE_ALL_PATTERN. Reviewer flagged this
       could mislead future contributors into thinking lifecycle-script
       enforcement is gated on write permissions.
    
       Replaced by the surrounding `rejects checkout credential persistence
       in workflows with permissions: write-all` test (already present) and
       the new quoted-form tests above, which all exercise the actual
       persist-credentials gate that the WRITE_ALL_PATTERN clause newly
       activates.
    
    Test count: 22 → 24 (added 3 new, dropped 1). All green; `yarn lint`
    clean.
    
    The cohort comment above the write-all block was also tightened to
    explicitly note that "the lifecycle-script gate already fires
    unconditionally for every workflow" so the next reader sees the
    distinction up front.
  • fix(ci): match quoted write-all + dedupe duplicate checkout violations
    Two round-1 review findings, fixed together because they touch the
    same regex/loop region of `findViolations`:
    
    1. **cubic P0 — quoted write-all bypass**.
       `WRITE_ALL_PATTERN` was `/^\s*permissions:\s*write-all\b/m`, which
       does not match the perfectly valid YAML forms
       `permissions: "write-all"` and `permissions: 'write-all'`. A
       workflow that quoted the shorthand slipped right through the
       persist-credentials gate the previous commit was supposed to close.
    
       Reproduced before this commit:
         $ cat /tmp/q.yml
         name: bad
         on: [push]
         permissions: "write-all"
         jobs:
           do:
             runs-on: ubuntu-latest
             steps:
               - uses: actions/checkout@v4
         $ ECC_WORKFLOWS_DIR=/tmp node scripts/ci/validate-workflow-security.js
         Validated workflow security for 1 workflow files
         exit=0
    
       Fix: tighten the regex to
         /^\s*permissions:\s*["']?write-all["']?\s*$/m
       which accepts the bare, double-quoted, and single-quoted YAML forms
       while still anchoring on the `permissions:` key. The trailing `\s*$`
       prevents accidentally matching keys whose value happens to start
       with `write-all` (e.g. some future literal `write-all-something`).
    
    2. **greptile P2 — duplicate violation when both patterns match**.
       A `ref: refs/pull/${{ github.event.pull_request.head.sha }}/merge`
       value matches both the `pull_request_target` rule's
       `expressionPattern` (the `head.sha` interpolation) and its
       `refPattern` (the `refs/pull/` literal). Each push generates an
       ERROR line with the same description and just a different
       `expression:` echo, so the reviewer sees the same violation twice.
    
       Fix: track `stepFlagged` inside the per-step loop and skip the
       `refPattern` fallback once any `expressionPattern` match has already
       produced a violation for this step. The `refPattern` is a fallback
       for ref-only forms (`refs/pull/123/head`, `${{ env.X }}` whose
       resolved value is a PR ref); when the more specific expression
       already fires, the fallback is redundant by definition.
    
    After both fixes, the round-1 reproductions resolve cleanly:
    
      $ # quoted form now blocks
      $ ECC_WORKFLOWS_DIR=/tmp/q1/.github/workflows node scripts/ci/validate-workflow-security.js
      ERROR: quoted.yml:8 - workflows with write permissions must disable checkout credential persistence
      exit=1
    
      $ # combined head.sha + refs/pull now prints one ERROR, not two
      $ ECC_WORKFLOWS_DIR=/tmp/q2/.github/workflows node scripts/ci/validate-workflow-security.js
      ERROR: dup.yml:10 - pull_request_target must not checkout an untrusted pull_request head ref/repository
        Unsafe expression: ${{ github.event.pull_request.head.sha }}
      exit=1
    
    Test additions land in the next commit.
  • fix(ci): flag refs/pull checkouts under pull_request_target
    The `pull_request_target` rule's `expressionPattern` matches only
    the canonical `github.event.pull_request.head.{ref,sha,repo.full_name}`
    interpolations. It does not match the second canonical form of
    the same exploit — fetching `refs/pull/<N>/{head,merge}` directly:
    
      - uses: actions/checkout@v4
        with:
          ref: refs/pull/${{ github.event.pull_request.number }}/merge
    
    The merge-ref variant is what GitHub's own security guidance calls
    out as the highest-severity privilege-escalation pattern under
    `pull_request_target`: it materialises the PR's merge commit
    (attacker code spliced with base), executes inside a workflow that
    has full repo-scoped tokens, and gives the attacker the chance to
    exfiltrate secrets or push to default branches. `refs/pull/N/head`
    is functionally equivalent — same source, same trust boundary.
    
    Reproduced on `main` before this commit:
    
      $ cat /tmp/bad.yml
      name: bad
      on: { pull_request_target: { types: [opened] } }
      permissions: { contents: read }
      jobs:
        do:
          runs-on: ubuntu-latest
          steps:
            - uses: actions/checkout@v4
              with:
                ref: refs/pull/${{ github.event.pull_request.number }}/merge
                persist-credentials: false
            - run: npm ci --ignore-scripts
    
      $ ECC_WORKFLOWS_DIR=/tmp node scripts/ci/validate-workflow-security.js
      Validated workflow security for 1 workflow files
      $ echo $?
      0
    
    Expected: violation flagging the refs/pull checkout under pull_request_target.
    Actual: passes silently.
    
    Fix: add a `refPattern` to the `pull_request_target` rule:
    
        /^\s*ref:\s*['"]?[^'"\n]*refs\/(?:remotes\/)?pull\/[^'"\n\s]+/m
    
    and apply it per checkout step inside the existing
    event-gated loop. The pattern matches the ref VALUE so it catches
    all interpolation shapes — `refs/pull/123/head`,
    `refs/pull/${{ github.event.pull_request.number }}/merge`,
    `${{ env.FOO }}/refs/pull/N/head` — without enumerating the
    possible interpolations themselves.
    
    Scoping: the rule is already gated on the workflow containing
    `pull_request_target:`, so non-privileged `pull_request` workflows
    that legitimately check out a PR ref are not affected.
    
    After this commit the reproduction above exits 1 with:
    
      ERROR: bad.yml:10 - pull_request_target must not checkout an untrusted pull_request head ref/repository
    
    Three new regression tests in `tests/ci/validate-workflow-security.test.js`:
      - rejects pull_request_target + refs/pull/<N>/merge
      - rejects pull_request_target + hardcoded refs/pull/<N>/head
      - allows pull_request_target with no `with.ref:` (base-ref checkout —
        the safe pattern from GitHub's own guidance)
    
    Test count: 17 → 20 in this file; full `yarn test` still green.
    
    Together with the previous commit, this closes the two
    independent `validate-workflow-security.js` bypasses I found.
  • fix(ci): treat 'permissions: write-all' as a write-permission gate
    `WRITE_PERMISSION_PATTERN` in `validate-workflow-security.js`
    enumerates named GitHub Actions scopes (`contents: write`,
    `issues: write`, etc.) to decide whether a workflow needs to:
      - disable `persist-credentials` on `actions/checkout`
      - pass `--ignore-scripts` to `npm ci`
    
    The pattern misses the top-level shorthand `permissions:
    write-all`, which is the strictly broader form — it grants every
    named scope write access in a single line. As a result, a
    workflow that opts into write-all currently slips both gates.
    
    Reproduced on `main` before this commit:
    
      $ cat /tmp/bad.yml
      name: bad
      on: [push]
      permissions: write-all
      jobs:
        do:
          runs-on: ubuntu-latest
          steps:
            - uses: actions/checkout@v4
            - run: npm ci
    
      $ ECC_WORKFLOWS_DIR=/tmp node scripts/ci/validate-workflow-security.js
      Validated workflow security for 1 workflow files
      $ echo $?
      0
    
    Expected: at least two violations (missing `persist-credentials:
    false`, missing `--ignore-scripts`).
    Actual: passes silently.
    
    Fix: add a sibling pattern `WRITE_ALL_PATTERN` that matches
    `^\s*permissions:\s*write-all\b` and OR it with
    `WRITE_PERMISSION_PATTERN` at the single gate. Both top-level
    and job-level `permissions:` blocks satisfy the `^\s*` prefix.
    
    After this commit the reproduction above exits 1 with:
    
      ERROR: bad.yml:8 - workflows with write permissions must disable checkout credential persistence
      ERROR: bad.yml:9 - workflows with write permissions must install npm dependencies with --ignore-scripts
    
    Three new regression tests in `tests/ci/validate-workflow-security.test.js`:
      - rejects write-all + credential-persisting checkout
      - rejects write-all + `npm ci` without `--ignore-scripts`
      - allows write-all when both gates are satisfied (no over-block)
    
    Test count: 14 → 17 in this file; full `yarn test` still green.
    
    A separate `refs/pull/N/merge` bypass under `pull_request_target`
    exists in the same validator and is fixed in the next commit.
  • feat(installer): add --locale flag for translated docs installation
    Adds `--locale <code>` support to the ECC installer so users can install
    localized reference docs (agents, commands, skills, rules) into
    `~/.claude/docs/<locale>/` alongside the existing English installation.
    
    Changes:
    - manifests/install-modules.json: add 8 locale doc modules (docs-ja-JP,
      docs-zh-CN, docs-ko-KR, docs-pt-BR, docs-ru, docs-tr, docs-vi-VN,
      docs-zh-TW), each with kind="docs" and defaultInstall=false
    - manifests/install-components.json: add 8 locale: components mapping to
      the new modules
    - scripts/lib/install-manifests.js: add locale: family prefix,
      SUPPORTED_LOCALES, LOCALE_ALIAS_TO_COMPONENT_ID (with aliases like
      ja=ja-JP, zh=zh-CN, ko=ko-KR), and listSupportedLocales()
    - scripts/lib/install/request.js: add --locale flag to parseInstallArgs(),
      resolve locale alias → component ID in normalizeInstallRequest(), throw
      on unsupported locale codes
    - scripts/lib/install-targets/claude-home.js: map docs/<locale>/ source
      paths to ~/.claude/docs/<locale>/ destination (side-by-side, no overwrite
      of English files)
    - scripts/install-apply.js: import listSupportedLocales, add --locale
      usage line and available locales list to --help output
    
    Usage examples:
      ./install.sh --locale ja                    # Japanese docs only
      ./install.sh --profile core --locale zh-CN  # core profile + zh-CN docs
      ./install.sh typescript --locale ja         # legacy + locale (errors)
  • docs(th): add Thai (th) README translation
    Adds docs/th/README.md with a concise onboarding-style Thai
    translation mirroring the docs/vi-VN format. Updates the language
    switchers in the English, Simplified Chinese, Traditional Chinese,
    Japanese, Korean, Portuguese (BR), Russian, Turkish, Vietnamese,
    and Simplified Chinese docs READMEs to link to the new Thai page.
    
    The English README remains the canonical source of truth; the Thai
    page links back to it for full content.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
  • fix(ja-JP): translate frontmatter description to Japanese in 3 skills
    - skill-scout: translate description field
    - tinystruct-patterns: translate description field
    - ui-to-vue: translate description field
  • fix(ja-JP): address review feedback and add 5 missing skills
    - Fix Chinese term '提炼' → '蒸留' in commands/rules-distill.md
    - Fix '重大な所見' (Critical→重大) in agents/opensource-sanitizer.md
    - Fix non-transactional persistence in swift-actor-persistence/SKILL.md:
      add rollback logic so cache stays consistent if disk write fails
    - Clarify anti-pattern wording: 'configurable file URL' → 'externally
      mutable after init' to remove internal inconsistency (P2)
    - Fix broken relative link in videodb/reference/api-reference.md:
      ../../../../../skills/... → ./editor.md
    - Add 5 previously missing SKILL.md translations:
      skill-scout, tinystruct-patterns, ui-to-vue, vite-patterns,
      windows-desktop-e2e
  • docs: fix zh-CN parity — add 44 missing files to ja-JP
    Add files present in zh-CN but missing from ja-JP:
    - commands: claw, context-budget, devfleet, docs, projects, prompt-optimize, rules-distill (7 files)
    - skills: regex-vs-llm-structured-text, remotion-video-creation, repo-scan, research-ops,
      returns-reverse-logistics, rules-distill, rust-patterns, rust-testing, skill-comply,
      skill-stocktake, social-graph-ranker, swift-actor-persistence, swift-concurrency-6-2,
      swift-protocol-di-testing, swiftui-patterns, team-builder, terminal-ops, token-budget-advisor,
      ui-demo, unified-notifications-ops, video-editing, videodb (+reference/*), visa-doc-translate,
      workspace-surface-audit, x-api (37 files)
    
    Result: ja-JP now has 517 files vs zh-CN 412 files.
    zh-CN parity: 0 missing files (complete parity achieved).
  • docs: add missing Japanese translations to complete zh-CN parity (ja-JP)
    Add remaining files to match zh-CN documentation structure:
    - hooks/README.md — hooks architecture and customization guide
    - examples/ — 8 project CLAUDE.md templates (general, user, django, go, harmonyos, laravel, rust, saas-nextjs)
    - CHANGELOG.md — version history
    - the-openclaw-guide.md — OpenClaw guide (471 lines)
    
    Total: 11 files, 2362 insertions
    ja-JP now has full parity with zh-CN directory structure.
  • docs: add native Japanese translation of ECC documentation (ja-JP)
    Translate everything-claude-code repository to Japanese including:
    - 17 root documentation files
    - 60 agent documentation files
    - 80 command documentation files
    - 99 rule files across 18 language directories (common, angular, arkts, cpp, csharp, dart, fsharp, golang, java, kotlin, perl, php, python, ruby, rust, swift, typescript, web)
    - 199 skill documentation files
    
    Total: 455 files translated to Japanese with:
    - Consistent terminology glossary applied throughout
    - YAML field names preserved in English (name, description, etc.)
    - Code blocks and examples untouched (comments translated)
    - Markdown structure and relative links preserved
    - Professional translation maintaining technical accuracy
    
    This translation expands ECC accessibility to Japanese-speaking developers and teams.
    
    Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>