# Before You Share an AI Chat, Run This Privacy Gate

> Source: <https://dev.to/jamilxt/before-you-share-an-ai-chat-run-this-privacy-gate-2amm>
> Published: 2026-10-23 03:06:39+00:00

An AI conversation can look harmless when you read it one message at a time. The full thread may contain a customer name, a draft contract, a private URL, a partial account number, or an internal decision that was never meant for a wider audience.

That risk became concrete in October when researchers reported that they could discover thousands of Meta Muse conversation-sharing links. [Ars Technica reported](https://arstechnica.com/security/2026/10/meta-muse-exposes-users-personal-and-business-secrets-when-they-share-conversations/) that some pages contained business and personal details, and that Meta said it had paused the sharing feature while it reviewed the issue. This was a report about a particular link-discovery design, not evidence that every AI chat-sharing product works the same way or that every Muse user was exposed.[1]

The useful lesson is broader than one product: a share link is a disclosure boundary. Before making a conversation available to someone else, inspect the actual content and remove details that the recipient does not need.

This tutorial builds a small Python pre-share gate. It catches a few recognizable patterns, replaces them with review markers, writes a separate sanitized copy, and refuses to treat automated redaction as approval. It is a teaching example, not a complete privacy scanner.

**Context accumulates.** A user may paste an error log, then add a customer name, then ask for a summary that repeats those details. The final answer can expose information that was several turns earlier.

**Sensitive data is not just a password.** [OWASP's 2025 guidance](https://genai.owasp.org/llmrisk2023-24/llm06-sensitive-information-disclosure/) identifies personal, financial, health, business, security, and legal information as sensitive categories that can be disclosed through LLM applications.[2] A transcript can include names, contact details, invoice amounts, medical context, source snippets, or credentials. Some of these are easy to recognize with patterns. Others depend on context.

**A link may travel farther than intended.** Depending on the product, anyone with a link may be able to open the shared page, the page may be indexed or discoverable through infrastructure, and recipients may copy or forward it. Those are distinct risks. Do not assume that a hard-to-guess URL is access control, and do not assume that a vendor's pause or fix applies to other services.

[Meta's launch material](https://research.meta.ai/blog/security-and-safety-for-ai-agents-our-approach-with-muse) described Muse as an agent designed to work with personal accounts and data. That makes its share incident a useful reminder to separate two questions: what an assistant can access to perform a task, and what a share feature publishes for another person to see.[3]

The gate below has four deliberate limits:

It does not upload data to a third-party service. It does not prove that the remaining text is safe. It cannot reliably identify confidential business context, every secret format, or every personal detail.

Save this as `share_gate.py`:

``` python
from __future__ import annotations

import re
import sys
from pathlib import Path

# These patterns are intentionally narrow examples, not universal validators.
RULES: list[tuple[str, re.Pattern[str]]] = [
    (
        "email",
        re.compile(r"\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b", re.I),
    ),
    (
        "phone",
        re.compile(r"(?<!\w)(?:\+?\d[\d ().-]{7,}\d)(?!\w)"),
    ),
    (
        "api_key_like",
        re.compile(r"\b(?:sk|pk|api)[-_][A-Za-z0-9_-]{16,}\b", re.I),
    ),
    (
        "private_key_block",
        re.compile(
            r"-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----"
            r".*?-----END (?:RSA |EC |OPENSSH )?PRIVATE KEY-----",
            re.S,
        ),
    ),
]

def redact(text: str) -> tuple[str, dict[str, int]]:
    counts: dict[str, int] = {}
    result = text

    for label, pattern in RULES:
        result, count = pattern.subn(f"[REDACTED:{label}]", result)
        if count:
            counts[label] = count

    return result, counts

def main() -> int:
    if len(sys.argv) != 3:
        print("Usage: python share_gate.py INPUT.txt OUTPUT.txt", file=sys.stderr)
        return 2

    source = Path(sys.argv[1])
    destination = Path(sys.argv[2])

    if not source.is_file():
        print(f"Input does not exist: {source}", file=sys.stderr)
        return 2
    if source.resolve() == destination.resolve():
        print("Refusing to overwrite the input file.", file=sys.stderr)
        return 2
    if destination.exists():
        print(f"Output already exists: {destination}", file=sys.stderr)
        return 2

    cleaned, counts = redact(source.read_text(encoding="utf-8"))
    destination.write_text(cleaned, encoding="utf-8")

    print(f"Wrote review copy: {destination}")
    if counts:
        for label, count in sorted(counts.items()):
            print(f"Review {count} match(es): {label}")
    else:
        print("No configured patterns matched. This is not a clean bill of health.")

    print("Do not share until a person reviews the complete output.")
    return 0

if __name__ == "__main__":
    raise SystemExit(main())
```

Run it on an exported plain-text transcript:

```
python share_gate.py conversation.txt conversation.review.txt
```

The first version intentionally does not read every export format. If a product exports HTML, JSON, PDF, or images, convert or extract the content through a trusted process first, then inspect the result. A failed conversion can omit content without making the original safe to share.

Create a disposable test input, not a real customer transcript:

```
Contact: alex@example.com
Notes: call the sample number +1 (202) 555-0147
```

Run the command and inspect `conversation.review.txt`. The email and phone-shaped sequence should become markers. The test suite below constructs a synthetic token-like value and checks that pattern separately. The scanner reports the number of matches by category, not the original values, so the terminal output does not repeat what it detected.

The phone expression is deliberately broad. It may redact dates, ticket numbers, or other digit sequences. That is safer than pretending a simple pattern can distinguish every phone number from every other number, but it can make a review copy harder to read. Adjust it for your environment and add tests for both the values you want caught and examples you do not want changed.

Add a private-key test in a separate dummy file if needed. Never paste a real credential into a test fixture. For a real key discovered in a transcript, treat it as exposed: revoke or rotate it at the system that issued it. Redaction changes the copy; it does not undo disclosure.

The replacement function is easy to exercise without installing packages. In the test, construct a synthetic key-like value from string fragments so your editor or secret scanner does not mistake the fixture for a real credential. Put this in `test_share_gate.py` next to the script:

``` python
import unittest

from share_gate import redact

class RedactionTests(unittest.TestCase):
    def test_email_is_replaced(self):
        result, counts = redact("Contact alex@example.com for details")
        self.assertIn("[REDACTED:email]", result)
        self.assertNotIn("alex@example.com", result)
        self.assertEqual(counts["email"], 1)

    def test_key_like_value_is_replaced(self):
        value = "sk-demo_" + "x" * 20
        result, counts = redact("token " + value)
        self.assertIn("[REDACTED:api_key_like]", result)
        self.assertEqual(counts["api_key_like"], 1)

    def test_private_key_block_is_removed_as_one_match(self):
        begin = "-----BEGIN " + "PRIVATE KEY-----"
        end = "-----END " + "PRIVATE KEY-----"
        text = "before\n" + begin + "\ndummy-material\n" + end + "\nafter"
        result, counts = redact(text)
        self.assertIn("[REDACTED:private_key_block]", result)
        self.assertNotIn("dummy-material", result)
        self.assertEqual(counts["private_key_block"], 1)

    def test_unmatched_text_is_preserved(self):
        text = "The service returned status 204."
        result, counts = redact(text)
        self.assertEqual(result, text)
        self.assertEqual(counts, {})

if __name__ == "__main__":
    unittest.main()
```

Run the tests with:

```
python -m unittest -v
```

A passing test only means the examples behave as written. It does not measure recall against real-world secrets or privacy leaks. Build a local test corpus with synthetic values that represent your organization's formats, and have a security or privacy owner review both false negatives and false positives.

**Names and confidential facts need context.** A regular expression can spot many email addresses. It cannot know that “Project Cedar” is confidential, that a meeting time identifies an unreleased acquisition, or that a combination of public details points to one person.

**Use a detector as another layer, not the final authority.** [Microsoft Presidio's documentation](https://microsoft.github.io/presidio/text_anonymization/) describes separate detection and anonymization stages and supports recognizers and replacement operators. Detection is configurable, which means results depend on the recognizers and data you provide.[4] A more complete pipeline can send text to a local or approved analyzer, redact detected entities, then show a reviewer what changed. That still needs human review and a threat model.

**Don't ask the model to police itself.** A prompt such as “remove sensitive information” can help with drafting, but it is not a security boundary. OWASP warns that relying on model instructions alone can fail because model behavior is unpredictable and may be circumvented.[2] Treat model-based redaction as an assistive step, never as the only gate.

**Keep the original protected.** The sample writes a second file and refuses to overwrite either input or an existing output. In a production workflow, also restrict file permissions, avoid logging transcript contents, define retention and deletion, and decide whether backups include both files.

If a team is adding conversation sharing to an internal tool, make the privacy gate part of the share action rather than a separate reminder:

These controls address different failure modes. A scanner does not fix public link enumeration. Authentication does not prevent a user from sharing the wrong conversation. Link expiration does not erase screenshots or copies. Defense comes from making the scope clear and limiting access at more than one point.

Before sending an AI conversation link, check:

The important distinction is between “the scanner found nothing” and “a reviewer confirmed the intended content is safe for this audience.” The first is a tool result. The second is a decision.

AI conversation sharing is useful when the shared scope is intentional. But the conversation may contain more than the piece you meant to show, and the URL itself may not enforce the audience you imagined.

A small pre-share gate can catch obvious patterns and create a safer review copy. Its value is not that regular expressions solve privacy. Its value is that it turns “share” from a reflex into a controlled step: inspect, redact, review, confirm, then publish only what the recipient needs.

I write practical guides about AI infrastructure, developer tools, and secure software workflows. Subscribe for future tutorials and comparisons.

Have you added a review step before sharing AI-generated content or conversation links? What would you want the scanner to flag automatically, and what should always stay a human decision?

[1] [Ars Technica: Meta Muse share links](https://arstechnica.com/security/2026/10/meta-muse-exposes-users-personal-and-business-secrets-when-they-share-conversations/)

[2] [OWASP: Sensitive Information Disclosure](https://genai.owasp.org/llmrisk2023-24/llm06-sensitive-information-disclosure/)

[3] [Meta: How We Built Safety Into Muse](https://research.meta.ai/blog/security-and-safety-for-ai-agents-our-approach-with-muse)

[4] [Microsoft Presidio: Text Anonymization](https://microsoft.github.io/presidio/text_anonymization/)
