Skip to content

fix(agents): never write host git config from repo-action templates#250

Merged
galanko merged 5 commits into
mainfrom
fix/agent-templates-scope-git-config
May 31, 2026
Merged

fix(agents): never write host git config from repo-action templates#250
galanko merged 5 commits into
mainfrom
fix/agent-templates-scope-git-config

Conversation

@galanko
Copy link
Copy Markdown
Collaborator

@galanko galanko commented May 31, 2026

Problem

The repo-action agent templates run git config --global before cloning to (a) set an insteadOf rewrite embedding the GitHub App token for clone/push auth, and (b) set the Cliff Posture Bot commit identity.

When an agent flow runs outside an isolated sandbox (a maintainer's or customer's host), those --global writes land in the operator's real ~/.gitconfig:

  • the insteadOf rule shadows the host's own credential helper; the embedded ghu_… token is short-lived, so once it expires every git push fails with Invalid username or token;
  • the operator's global user.name/user.email get overwritten with the bot identity.

This was hit in practice — a maintainer's pushes to an unrelated repo broke, with the global identity silently flipped to Cliff Posture Bot.

git config --global always succeeds against the host and cannot fail closed. A dropped env var or a reordered/retried step is enough to corrupt a user's machine. That's an unacceptable failure mode for a tool we ship to customers.

Fix: remove every host-scoped git-config write

Need Before (unsafe) After (safe) Why safe
Clone auth git config --global …insteadOf token-embedded clone URL built at runtime from ${GH_TOKEN} token only lands in the throwaway repo/.git/config; origin carries it so push works
Commit identity git config --global user.* git config --local user.* after cd repo/ --local writes the repo's .git/config and errors out when not in a repo — never reaches ~/.gitconfig
Push relied on global insteadOf nothing — origin already authenticated no config write

The worst case is now a contained failure of the agent's own PR, never damage to the operator's git configuration.

Also fixes a latent bug: remediation_executor had no commit identity of its own and only committed by relying on the leaked host identity. It now sets --local identity.

Token hygiene (folded in)

remediation_executor and evidence_collector previously did export GH_TOKEN="{{ gh_token }}", baking the literal token into the LLM prompt. The export was redundant — GH_TOKEN is already injected into the workspace process env (the same env the repo-action templates use via ${GH_TOKEN} without exporting). Dropped the export so the token is never echoed into the prompt; ${GH_TOKEN} still resolves at runtime.

Direction of travel: agents will ultimately run in a full sandbox; this hardens the pre-sandbox path so it carries zero risk to the host in the meantime.

Files

  • backend/cliff/agents/templates/{security_md_generator,dependabot_config_generator,remediation_executor,evidence_collector}.md.j2
  • backend/tests/test_repo_action_templates.py — regression guards

Test plan

  • pytest -m 'not e2e'1522 passed, 1 skipped (33 e2e deselected). One unrelated LLM-eval test (test_plain_description_eval … [snyk-python-setuptools-rce]) is flaky — passes in isolation and on reruns; it touches none of the changed files.
  • ruff check cliff/ tests/ → clean
  • Render checks across all four templates (with/without token): no git config --global/--system; clone auth via token-embedded URL only when a token is present; literal token never appears in any rendered prompt.
  • Regression guards: _assert_never_writes_host_git_config runs against all four templates (incl. remediation_executor, which pushes), plus a literal-token-absence assertion on the two tool agents.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Clone/auth steps no longer modify host/global Git config; when a token is present, cloning uses a runtime token-embedded URL and commit identity is set via repo-local git config after cloning.
  • Documentation

    • Templates include clarifying comments that credentials and git config changes are confined to the run/repo-local scope and add an explicit post-clone check for a valid git directory.
  • Tests

    • Added regression tests to assert no host/global git config writes, verify repo-local identity config, ensure token-embedded clone URLs, and check post-clone git directory existence.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 31, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: cc7f6e87-51e0-4ab0-bd7f-c1f3ea87003f

📥 Commits

Reviewing files that changed from the base of the PR and between 316c298 and 51ae645.

📒 Files selected for processing (2)
  • backend/cliff/agents/templates/evidence_collector.md.j2
  • backend/tests/test_repo_action_templates.py

📝 Walkthrough

Walkthrough

Templates now compute token-embedded clone URLs at runtime when a GitHub token is present and perform git clone with that URL. After cloning, commit identity is written with repo-local git config --local only. Tests assert rendered templates do not contain git config --global or git config --system and include git config --local user.name.

Changes

Constrain git config & clone auth to run/repo-local

Layer / File(s) Summary
Tokenized clone URL and clone flow
backend/cliff/agents/templates/dependabot_config_generator.md.j2, backend/cliff/agents/templates/evidence_collector.md.j2, backend/cliff/agents/templates/remediation_executor.md.j2, backend/cliff/agents/templates/security_md_generator.md.j2
When gh_token/GH_TOKEN is present, templates compute a token-embedded CLONE_URL (using REPO_URL where applicable) and run git clone with that URL instead of configuring global url.*.insteadOf rewrites; add a post-clone test -d repo/.git guard.
Repo-local commit identity
backend/cliff/agents/templates/remediation_executor.md.j2, backend/cliff/agents/templates/security_md_generator.md.j2, backend/cliff/agents/templates/dependabot_config_generator.md.j2
After verifying repo/.git and cding into the checkout, templates set git config --local user.email and git config --local user.name; global/system gitconfig writes were removed.
Tests: assert no host/global gitconfig writes
backend/tests/test_repo_action_templates.py
Add _assert_never_writes_host_git_config(content: str) and update SECURITY.md and dependabot generator tests to assert absence of git config --global/git config --system and presence of git config --local user.name; add parametrized tests for cloning agents to assert token-embedded clone URL usage and test -d repo/.git.
sequenceDiagram
  participant Template
  participant Runner
  participant GitHub
  participant LocalFS
  Template->>Runner: render clone step (compute CLONE_URL / REPO_URL)
  Runner->>GitHub: git clone <CLONE_URL or REPO_URL>
  GitHub-->>Runner: repository data (.git)
  Runner->>LocalFS: test -d repo/.git
  Runner->>LocalFS: cd repo/ and git config --local user.email/name
Loading

🎯 3 (Moderate) | ⏱️ ~20 minutes

"I stitched the clone with care and grace,
A token in the URL finds its place,
No global marks upon the host,
Just local names I proudly post,
A rabbit’s hop keeps configs in space."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: preventing host git config writes from repo-action templates, which is the primary objective of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/agent-templates-scope-git-config

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/tests/test_repo_action_templates.py`:
- Around line 34-43: The test currently searches for any occurrence of
"GIT_CONFIG_GLOBAL" which can match comments; change the first search to look
specifically for the export assignment (e.g. search for "export
GIT_CONFIG_GLOBAL" or "export GIT_CONFIG_GLOBAL=" using the existing export_idx
variable and content.find) so export_idx only matches the actual export line,
keep the assert export_idx != -1, and keep the following check that export_idx
comes before the first "git config --global" occurrence (first_global) to ensure
the export assignment is present and precedes any global writes.
- Around line 24-43: Add render tests that call the existing
_assert_global_config_is_scoped helper for the evidence_collector.md.j2 and
remediation_executor.md.j2 templates so scoping regressions fail the suite;
locate where SECURITY.md and dependabot templates are already rendered in the
tests and mirror that pattern to render evidence_collector and
remediation_executor (referencing the template names evidence_collector.md.j2
and remediation_executor.md.j2 and the helper function
_assert_global_config_is_scoped) and assert the helper is executed against their
rendered content.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3d3d6e67-e406-487f-8d34-7e0f69d40611

📥 Commits

Reviewing files that changed from the base of the PR and between 6e29fd7 and 4b3fa0b.

📒 Files selected for processing (5)
  • backend/cliff/agents/templates/dependabot_config_generator.md.j2
  • backend/cliff/agents/templates/evidence_collector.md.j2
  • backend/cliff/agents/templates/remediation_executor.md.j2
  • backend/cliff/agents/templates/security_md_generator.md.j2
  • backend/tests/test_repo_action_templates.py

Comment thread backend/tests/test_repo_action_templates.py Outdated
Comment thread backend/tests/test_repo_action_templates.py Outdated
The repo-action agent templates ran `git config --global` to set an
`insteadOf` credential rewrite (embedding a GitHub App token) and the
"Cliff Posture Bot" identity before cloning. When an agent flow runs
outside an isolated sandbox (e.g. on a maintainer's or customer's host),
those `--global` writes land in the operator's real `~/.gitconfig`:

  - the injected `insteadOf` rule shadows the host's own credential
    helper, and the short-lived token then expires, breaking the
    operator's `git push` with "Invalid username or token";
  - the operator's global `user.name`/`user.email` get overwritten.

`--global` always succeeds against the host and cannot fail closed, so a
dropped env var or a reordered/retried step is enough to corrupt a user's
machine — unacceptable for a tool we ship to customers.

Fix: remove every host-scoped git-config write. Clone auth is carried by
a token-embedded clone URL built at runtime from `${GH_TOKEN}` (the
literal token never enters the rendered prompt; it only ever lands in the
throwaway `repo/.git/config`). Commit identity is set with
`git config --local`, which writes the repo's config and errors out when
not inside a repo — it can never reach `~/.gitconfig`. This also fixes a
latent bug where `remediation_executor` had no identity of its own and
only committed by relying on the leaked host identity.

The worst case is now a contained failure of the agent's own PR, never
damage to the operator's git configuration.

Regression guard (`_assert_never_writes_host_git_config`) asserts the
templates contain no `git config --global` or `--system`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@galanko galanko force-pushed the fix/agent-templates-scope-git-config branch from 4b3fa0b to 86af911 Compare May 31, 2026 19:27
@galanko galanko changed the title fix(agents): scope git config --global to the workspace, not the host fix(agents): never write host git config from repo-action templates May 31, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/cliff/agents/templates/dependabot_config_generator.md.j2`:
- Around line 49-54: The template builds CLONE_URL using the shell variable
${GH_TOKEN} but never exports GH_TOKEN into the shell; update the Jinja template
so when gh_token is present it emits an export GH_TOKEN="{{ gh_token }}" line
before the CLONE_URL block (i.e., inside the same {% if gh_token %} branch),
ensuring the exported environment variable name matches ${GH_TOKEN} used by
CLONE_URL and enabling authenticated git clone for private repos.

In `@backend/cliff/agents/templates/security_md_generator.md.j2`:
- Around line 49-55: The template references the shell variable GH_TOKEN when
setting CLONE_URL (symbols: GH_TOKEN, CLONE_URL, REPO_URL) but doesn't export it
first; update the template block that runs when gh_token is truthy to export the
token into the environment (e.g., add an export GH_TOKEN=... before computing
CLONE_URL) so subsequent uses like
CLONE_URL="https://x-access-token:${GH_TOKEN}@${REPO_URL#https://}" and the git
clone command see the variable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a7856665-e00b-4ee1-89ed-e839ef021f0d

📥 Commits

Reviewing files that changed from the base of the PR and between 4b3fa0b and 86af911.

📒 Files selected for processing (5)
  • backend/cliff/agents/templates/dependabot_config_generator.md.j2
  • backend/cliff/agents/templates/evidence_collector.md.j2
  • backend/cliff/agents/templates/remediation_executor.md.j2
  • backend/cliff/agents/templates/security_md_generator.md.j2
  • backend/tests/test_repo_action_templates.py

Comment thread backend/cliff/agents/templates/dependabot_config_generator.md.j2
Comment thread backend/cliff/agents/templates/security_md_generator.md.j2
… host git config

Extend the no-host-gitconfig regression guard to the two cloning templates
that go through `render_agent` rather than `render_repo_action`. The
executor pushes commits, so it is the higher-risk path; both are now
asserted to contain no `git config --global`/`--system` and to carry clone
auth via a token-embedded URL.

Addresses CodeRabbit review feedback on PR #250 (coverage gap).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@galanko
Copy link
Copy Markdown
Collaborator Author

galanko commented May 31, 2026

Thanks @coderabbitai — triaged all four comments against the current commit:

1 & 2 — "Missing export GH_TOKEN" in security_md_generator / dependabot_config_generator (flagged Critical): not applied. This is a false positive given the runtime. GH_TOKEN is injected into the workspace process environment by the runner (repo_workspace_runner.py:208env_vars["GH_TOKEN"] = gh_token), and the agent's shell inherits it — so ${GH_TOKEN} resolves without a re-export. This matches the original templates, which intentionally did not export it ("GH_TOKEN is already set in this workspace's environment — do NOT re-assign it"). Adding export GH_TOKEN="{{ gh_token }}" would render the literal token into the LLM prompt and break the existing assert "ghp_fake_token_for_render_test" not in content guard. (The two templates that do export it — remediation_executor, evidence_collector — are the inconsistent ones; tightening that is tracked as a separate token-hygiene follow-up.)

3 — Guard only exercised on 2 of 4 templates: fixed in c68f9b7. Added TestCloningAgentsNeverWriteHostGitConfig, which renders remediation_executor and evidence_collector via render_agent and asserts no git config --global/--system plus token-embedded clone auth. The executor (which pushes) is now covered.

4 — "Anchor GIT_CONFIG_GLOBAL to the export": obsolete. That comment was against the earlier commit. The GIT_CONFIG_GLOBAL approach was dropped entirely in 86af911 in favor of never running git config --global at all, so there's no export line to anchor anymore.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 31, 2026

Tip

For best results, initiate chat on the files or code changes.

@galanko Thanks for the thorough triage — all four points are well-reasoned.

  • 1 & 2 (GH_TOKEN export — false positive): Understood. The runner-injected env var pattern is clear, and the argument against export GH_TOKEN="{{ gh_token }}" (literal token in LLM prompt + breaking the existing anti-leak guard) is sound. Good call not applying it; I'll note the inconsistency in remediation_executor/evidence_collector as tracked.

  • 3 (coverage gap — fixed): Confirmed, the new TestCloningAgentsNeverWriteHostGitConfig covers the missing templates. ✓

  • 4 (GIT_CONFIG_GLOBAL anchor — obsolete): Agreed, the comment was stale against a prior approach that no longer exists.


✏️ Learnings added
Learnt from: galanko
URL: https://github.com/cliff-security/cliff/pull/250

Timestamp: 2026-05-31T19:37:53.848Z
Learning: In the cliff repository (backend/cliff/agents/templates/), `GH_TOKEN` is injected into the agent shell's process environment by the runner (`repo_workspace_runner.py`, line ~208: `env_vars["GH_TOKEN"] = gh_token`). The agent templates intentionally do NOT re-export `GH_TOKEN` because doing so would render the literal token into the LLM prompt and break the existing `assert "ghp_fake_token_for_render_test" not in content` test guard. Flagging a missing `export GH_TOKEN` in those templates is a false positive.

Learnt from: galanko
URL: https://github.com/cliff-security/cliff/pull/250

Timestamp: 2026-05-31T19:37:53.848Z
Learning: In PR `#250` of the cliff repository, the initial approach of using `export GIT_CONFIG_GLOBAL="${PWD}/.cliff-gitconfig"` to scope `git config --global` was dropped entirely (commit 86af911) in favor of removing all `git config --global` calls from agent templates entirely, using token-embedded clone URLs and `git config --local` instead. There is no `GIT_CONFIG_GLOBAL` export to anchor in the final implementation.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

`remediation_executor` and `evidence_collector` rendered
`export GH_TOKEN="{{ gh_token }}"`, baking the literal token into the
prompt sent to the LLM. The export was redundant: GH_TOKEN is already
injected into the workspace process env by the executor / repo-workspace
runner (the same env the repo-action templates rely on via `${GH_TOKEN}`
without exporting). Drop the export so the token is never echoed into the
prompt; the `${GH_TOKEN}` shell reference still resolves from the env.

Extends the template guard to assert the literal token never appears in
the rendered prompt for these two agents.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@galanko
Copy link
Copy Markdown
Collaborator Author

galanko commented May 31, 2026

Folded the token-hygiene follow-up into this PR (316c298): dropped the redundant export GH_TOKEN="{{ gh_token }}" from remediation_executor and evidence_collector so the literal token is never echoed into the agent prompt. GH_TOKEN is already in the workspace process env, so ${GH_TOKEN} still resolves for the clone. Added a literal-token-absence assertion to the template guard.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/cliff/agents/templates/evidence_collector.md.j2 (1)

63-65: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a clone-success guard before cd repo/.

Unlike remediation_executor.md.j2 (which runs test -d repo/.git || { …; exit 1; }), this template cds into repo/ with no verification. There is no set -e, so if the clone fails the cd repo/ also fails but the subsequent grep steps still run from the workspace root. Since the evidence collector's output feeds the remediation planner/executor, silently searching the wrong directory can produce misleading "affected files" that cascade into a bad fix.

🛡️ Mirror the executor's clone verification
    git clone --depth 1 "$CLONE_URL" repo/
+   # Verify the clone succeeded — if repo/.git is missing, STOP here.
+   test -d repo/.git || { echo "clone failed: repo/.git missing"; exit 1; }
    cd repo/
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/cliff/agents/templates/evidence_collector.md.j2` around lines 63 -
65, The template evidence_collector.md.j2 currently runs "git clone --depth 1
\"$CLONE_URL\" repo/" then unconditionally "cd repo/" which can proceed
incorrectly if clone failed; add a clone-success guard like the one used in
remediation_executor.md.j2 (e.g. test -d repo/.git || { echo "clone failed";
exit 1; }) immediately after the clone and before cd repo/ so the script aborts
on clone failure and does not run grep from the wrong directory.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@backend/cliff/agents/templates/evidence_collector.md.j2`:
- Around line 63-65: The template evidence_collector.md.j2 currently runs "git
clone --depth 1 \"$CLONE_URL\" repo/" then unconditionally "cd repo/" which can
proceed incorrectly if clone failed; add a clone-success guard like the one used
in remediation_executor.md.j2 (e.g. test -d repo/.git || { echo "clone failed";
exit 1; }) immediately after the clone and before cd repo/ so the script aborts
on clone failure and does not run grep from the wrong directory.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8369f7f8-b39b-4f7c-b837-4c96deae8709

📥 Commits

Reviewing files that changed from the base of the PR and between c68f9b7 and 316c298.

📒 Files selected for processing (3)
  • backend/cliff/agents/templates/evidence_collector.md.j2
  • backend/cliff/agents/templates/remediation_executor.md.j2
  • backend/tests/test_repo_action_templates.py

The evidence collector cd'd into repo/ straight after clone with no
verification. With no `set -e`, a failed clone left `cd repo/` failing
while the subsequent search steps ran from the workspace root — reporting
misleading "affected files" that cascade into the planner/executor. Mirror
the remediation_executor guard (`test -d repo/.git || exit 1`) so the run
aborts on clone failure. The template guard now asserts both cloning
agents verify the clone.

Addresses CodeRabbit review feedback on PR #250.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@galanko
Copy link
Copy Markdown
Collaborator Author

galanko commented May 31, 2026

Addressed the latest review (and rebased onto the main merge / #221):

Clone-success guard in evidence_collector (valid) — fixed in 51ae645. Mirrored the remediation_executor guard: test -d repo/.git || { echo "clone failed: repo/.git missing"; exit 1; } before cd repo/, so a failed clone aborts instead of letting the search steps run from the workspace root and report misleading "affected files". The template guard now asserts both cloning agents verify the clone.

The two "missing export GH_TOKEN" comments — confirmed resolved; as noted earlier, GH_TOKEN is injected into the workspace process env and re-exporting it would leak the literal token into the prompt. The latest commit also removed the export from remediation_executor/evidence_collector, so all four templates are now consistent (none re-export). Resolving those threads.

Re-verified against the merged code: no git config --global/--system in any template, GH_TOKEN still env-injected (repo_workspace_runner.py:208, executor.py:915), and the template/executor/repo-creation suites pass (108 passed, 2 skipped).

@galanko galanko merged commit cef63fb into main May 31, 2026
7 checks passed
@galanko galanko deleted the fix/agent-templates-scope-git-config branch May 31, 2026 20:24
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.

1 participant