Not in the top 1,000 by GitHub stars

VenusOne-Lee/popia-compliance

Open-source POPIA privacy and security guardrails for South African builders, for every AI coding tool.

History

Review weekly changes to SKILL.md before updating. Dates mark the weeks in which we observed each version; checks apply to the recorded commits.

View weekly changes

SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

1be23b4e77a1 → 1be23b4e77a1

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 1be23b4e77a1

No earlier version was observed for comparison.

Compare on GitHub

adapters/lovable/skill/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Local scripts → After Local scripts

1be23b4e77a1 → 1be23b4e77a1

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Local scripts

First observation → 1be23b4e77a1

No earlier version was observed for comparison.

Compare on GitHub

View on GitHubAdd to my skills

Verified commit 1be23b4e77a1d68014603ae6bc6e92dcf4232fe7

git clone --filter=blob:none --no-checkout https://github.com/VenusOne-Lee/popia-compliance.git .hotskills/venusone-lee-popia-compliance && git -C .hotskills/venusone-lee-popia-compliance checkout --detach 1be23b4e77a1d68014603ae6bc6e92dcf4232fe7 # Copy each selected skill directory from .hotskills/venusone-lee-popia-compliance into .agents/skills/<skill-name>/
git clone --filter=blob:none --no-checkout https://github.com/VenusOne-Lee/popia-compliance.git .hotskills/venusone-lee-popia-compliance && git -C .hotskills/venusone-lee-popia-compliance checkout --detach 1be23b4e77a1d68014603ae6bc6e92dcf4232fe7 # Copy each selected skill directory from .hotskills/venusone-lee-popia-compliance into .cursor/skills/<skill-name>/
12GitHub stars
0stars in 7 days
2skills in this repository
53commits in 30 days

Heat

1.7
  • Star growth, last 7 daysNot available
  • Star growth, last 24 hoursNot available
  • Rank movement, last 7 daysNot available
  • Commits, last 30 daysNot available
  • Accounts asking about it through MCP, last 7 daysNot available
  • Hot votes this weekNot available

Heat never changes the order by stars, and sponsorship never touches it. How heat works

Stars over time

Oct 25, 2026Oct 25, 202612

30-day change not yet available: not enough history.

Individual skills in this repository

How it works

popia-compliance

When your agent uses it

POPIA (South Africa's Protection of Personal Information Act) compliance and security audit skill for builders. Use whenever building, reviewing, auditing or shipping any website, web app, mobile app, API, CRM setup, AI feature or automation that touches personal information of South Africans - sign-up forms, lead capture, contact forms, checkout, user accounts, SA ID numbers, analytics/cookies, email or WhatsApp marketing, AI/LLM features, Supabase/Lovable/Firebase/Next.js/WordPress/Salesforce builds - and whenever the user mentions POPIA, POPI, PAIA, Information Regulator, Information Officer, data protection, privacy policy, consent, data breach / security compromise, DSAR, data retention, or "is this compliant". Trigger even if POPIA isn't named but the product serves South African users and collects personal data.

What it loads into context

Frontmatter

182 tokens

Always available to the agent

SKILL.md

3,288 tokens

When the skill is activated

Reference documents

115,365 tokens

Loaded on request

Token counts are estimates. Scripts run separately; they are not counted as reference context.

The playbook

  1. Pick the mode
  2. Core mental model (keep this loaded)
  3. Build mode
  4. Audit mode
  5. Non-negotiables (stop and flag, don't silently "fix")
  6. Output conventions
  7. File index
  8. Using the components
  9. Disclaimer

Scripts executed separately

  • bin/popia-kit.js
  • evals/fixtures/chatbot/support_bot.py
  • evals/fixtures/env-service-role/supabaseClient.ts
  • evals/fixtures/loan-decision/decide.ts
  • evals/fixtures/newsletter-form/Newsletter.tsx
  • evals/fixtures/react-preticked/SignupForm.tsx
  • evals/fixtures/repo-audit/src/SignupForm.tsx
  • evals/fixtures/repo-audit/src/logger.ts
  • evals/run_evals.py
  • hosted/site-check/adapters/node-pinned-fetch.ts
  • hosted/site-check/adapters/node-supabase-rate-limit.ts
  • hosted/site-check/adapters/package.json
  • hosted/site-check/adapters/test/node.smoke.ts
  • hosted/site-check/supabase/functions/site-check/core.ts
  • hosted/site-check/supabase/functions/site-check/index.ts
  • hosted/site-check/tests/actions.test.ts
  • hosted/site-check/tests/checks.test.ts
  • hosted/site-check/tests/guard.test.ts
  • hosted/site-check/tests/handler.test.ts
  • hosted/site-check/tests/hardening.test.ts
  • hosted/site-check/tests/helpers.ts
  • hosted/site-check/tests/sql/run_sql_tests.sh
  • mcp/__init__.py
  • mcp/server.py
  • mcp/smoke_test.py
  • package.json
  • registry/build.py
  • registry/popia/account/privacy-centre.tsx
  • registry/popia/consent/consent-banner.tsx
  • registry/popia/consent/consent-gate.tsx
  • registry/popia/consent/consent-provider.tsx
  • registry/popia/forms/collection-notice.tsx
  • registry/popia/forms/marketing-opt-in.tsx
  • registry/popia/forms/sa-id-input.tsx
  • registry/popia/lib/consent-record.ts
  • registry/popia/lib/redact.ts
  • registry/popia/lib/sa-id.ts
  • registry/popia/supabase/functions/_shared/privacy-config.ts
  • registry/popia/supabase/functions/consent/index.ts
  • registry/popia/supabase/functions/dsar-delete/index.ts
  • registry/popia/supabase/functions/dsar-export/index.ts
  • registry/popia/supabase/functions/unsubscribe/index.ts
  • scripts/__init__.py
  • scripts/build_adapters.py
  • scripts/cli.py
  • scripts/popia_badge.py
  • scripts/popia_fleet.py
  • scripts/popia_init.py
  • scripts/popia_operators.py
  • scripts/popia_pack.py
  • scripts/popia_scan.py
  • scripts/popia_vendors.py
  • scripts/regwatch.py
  • scripts/rules/__init__.py
  • scripts/rules/access.py
  • scripts/rules/consent.py
  • scripts/rules/pii.py
  • scripts/rules/repo.py
  • scripts/rules/secrets.py
  • scripts/rules/thirdparty.py
  • scripts/rules/web.py
  • scripts/sa_id.py
  • scripts/site_check.py
  • scripts/style_lint.py
  • scripts/supabase_check.py
  • tests/__init__.py
  • tests/automation/__init__.py
  • tests/automation/fixtures/__init__.py
  • tests/automation/test_badge.py
  • tests/automation/test_cli.py
  • tests/automation/test_fleet.py
  • tests/automation/test_init.py
  • tests/automation/test_mcp.py
  • tests/automation/test_regwatch.py
  • tests/fixtures/demo-app/package.json
  • tests/fixtures/demo-app/src/ai.ts
  • tests/fixtures/demo-app/src/components/SignupForm.tsx
  • tests/fixtures/demo-app/src/newsletter.ts
  • tests/fixtures/demo-app/supabase/functions/lead/index.ts
  • tests/fixtures/operators-app/api/triage.py
  • tests/fixtures/operators-app/package.json
  • tests/fixtures/operators-app/src/checkout.ts
  • tests/fixtures/operators-app/src/sms.js
  • tests/fixtures/operators-app/supabase-edge.ts
  • tests/fixtures/operators-app/tests/checkout.test.ts
  • tests/pack/__init__.py
  • tests/pack/test_pack.py
  • tests/scanner/__init__.py
  • tests/scanner/test_baseline.py
  • tests/scanner/test_operators.py
  • tests/scanner/test_rules.py
  • tests/scanner/test_site_check.py
  • tests/security/__init__.py
  • tests/security/test_hardening.py
  • tests/supabase/__init__.py
  • tests/supabase/run_live.sh
  • tests/supabase/test_supabase_check.py

Files

  • .github/CODEOWNERSasset271 bytes
  • .github/ISSUE_TEMPLATE/bug.ymlasset389 bytes
  • .github/ISSUE_TEMPLATE/config.ymlasset374 bytes
  • .github/ISSUE_TEMPLATE/false-positive.ymlasset1,384 bytes
  • .github/ISSUE_TEMPLATE/law-change.ymlasset1,706 bytes
  • .github/ISSUE_TEMPLATE/legal-reviewer.ymlasset2,312 bytes
  • .github/ISSUE_TEMPLATE/new-rule.ymlasset729 bytes
  • .github/ISSUE_TEMPLATE/translation.ymlasset446 bytes
  • .github/PULL_REQUEST_TEMPLATE.mddoc848 bytes
  • .github/dependabot.ymlasset272 bytes
  • .github/workflows/ci.ymlasset7,311 bytes
  • .github/workflows/regwatch.ymlasset1,972 bytes
  • .github/workflows/release.ymlasset5,197 bytes
  • .github/workflows/security.ymlasset2,682 bytes
  • .gitignoreasset133 bytes
  • .popiaignoreasset140 bytes
  • .pre-commit-hooks.yamlasset491 bytes
  • .semgrepignoreasset46 bytes
  • CHANGELOG.mddoc13,480 bytes
  • CODE_OF_CONDUCT.mddoc693 bytes
  • CONTRIBUTING.mddoc6,704 bytes
  • DISCLAIMER.mddoc2,492 bytes
  • GOVERNANCE.mddoc5,127 bytes
  • LICENSEasset1,116 bytes
  • LICENSE-CONTENTasset991 bytes
  • LICENSING.mddoc827 bytes
  • README.mddoc10,618 bytes
  • SECURITY.mddoc2,146 bytes
  • SKILL.mdcore13,828 bytes
  • SKILL.mdcore13,828 bytes
  • SKILL.mdcore13,828 bytes
  • SKILL.mdcore13,828 bytes
  • TRADEMARKS.mddoc729 bytes
  • action.ymlasset7,621 bytes
  • adapters/README.mddoc1,280 bytes
  • adapters/_core.mddoc5,223 bytes
  • adapters/agents/AGENTS.mddoc5,325 bytes
  • adapters/chatgpt/custom-gpt-instructions.mddoc5,437 bytes
  • adapters/claude/CLAUDE.mddoc5,433 bytes
  • adapters/copilot/.github/copilot-instructions.mddoc5,325 bytes
  • adapters/cursor/.cursor/rules/popia.mdcasset5,470 bytes
  • adapters/gemini/GEMINI.mddoc5,325 bytes
  • adapters/lovable/knowledge.mddoc5,619 bytes
  • adapters/windsurf/.windsurf/rules/popia.mddoc5,403 bytes
  • adapters/windsurf/.windsurfrulesasset5,325 bytes
  • bin/popia-kit.jsruns1,105 bytes
  • docs/automation.mddoc12,593 bytes
  • docs/mcp.mddoc10,093 bytes
  • docs/quickstart.mddoc8,096 bytes
  • docs/releasing.mddoc3,385 bytes
  • docs/site-check-bot.mddoc2,893 bytes
  • evals/README.mddoc11,908 bytes
  • evals/cases/audit-repo.jsonasset1,190 bytes
  • evals/cases/breach-exposed-service-role-key.jsonasset1,263 bytes
  • evals/cases/breach-misdirected-email.jsonasset971 bytes
  • evals/cases/breach-rls-off-week.jsonasset993 bytes
  • evals/cases/build-ai-chatbot-customer-data.jsonasset961 bytes
  • evals/cases/build-airbnb-guest-ids.jsonasset1,458 bytes
  • evals/cases/build-analytics-pixels.jsonasset860 bytes
  • evals/cases/build-automated-loan-decision.jsonasset859 bytes
  • evals/cases/build-delete-account.jsonasset851 bytes
  • evals/cases/build-face-verification.jsonasset926 bytes
  • evals/cases/build-fine-tune-customer-data.jsonasset868 bytes
  • evals/cases/build-kids-app.jsonasset1,064 bytes
  • evals/cases/build-newsletter-cursor.jsonasset1,090 bytes
  • evals/cases/build-newsletter-lovable.jsonasset1,027 bytes
  • evals/cases/build-newsletter-signup.jsonasset1,137 bytes
  • evals/cases/build-sa-id-without-need.jsonasset922 bytes
  • evals/cases/build-supabase-table.jsonasset930 bytes
  • evals/cases/build-whatsapp-broadcast.jsonasset876 bytes
  • evals/cases/docs-access-request.jsonasset824 bytes
  • evals/cases/docs-cookie-banner.jsonasset748 bytes
  • evals/cases/docs-objection.jsonasset819 bytes
  • evals/cases/docs-privacy-policy.jsonasset1,112 bytes
  • evals/cases/explain-cold-b2b-email.jsonasset879 bytes
  • evals/cases/explain-hr-criminal-checks.jsonasset915 bytes
  • evals/cases/explain-popia-fines.jsonasset869 bytes
  • evals/cases/explain-uncertain-answer.jsonasset873 bytes
  • evals/cases/explain-us-cloud-hosting.jsonasset896 bytes
  • evals/cases/refuse-backdate-consent.jsonasset916 bytes
  • evals/cases/refuse-buy-lead-list.jsonasset1,054 bytes
  • evals/cases/refuse-preticked-checkbox.jsonasset980 bytes
  • evals/fixtures/chatbot/support_bot.pyruns776 bytes
  • evals/fixtures/env-service-role/env.local.txtdoc296 bytes
  • evals/fixtures/env-service-role/supabaseClient.tsruns203 bytes
  • evals/fixtures/guest-checkin/guests.csvasset384 bytes
  • evals/fixtures/guest-checkin/whatsapp-template.txtdoc269 bytes
  • evals/fixtures/kids-app/analytics.htmlasset452 bytes
  • evals/fixtures/kids-app/schema.sqlasset255 bytes
  • evals/fixtures/loan-decision/decide.tsruns357 bytes
  • evals/fixtures/misdirected-email/incident.mddoc330 bytes
  • evals/fixtures/newsletter-form/Newsletter.tsxruns305 bytes
  • evals/fixtures/react-preticked/SignupForm.tsxruns661 bytes
  • evals/fixtures/repo-audit/README.mddoc87 bytes
  • evals/fixtures/repo-audit/src/SignupForm.tsxruns661 bytes
  • evals/fixtures/repo-audit/src/index.htmlasset452 bytes
  • evals/fixtures/repo-audit/src/logger.tsruns173 bytes
  • evals/fixtures/repo-audit/supabase/migrations/20261001_create_leads.sqlasset397 bytes
  • evals/fixtures/supabase-no-rls/20261001_create_leads.sqlasset397 bytes
  • evals/published/2026-10-18-claude-opus-5-5-run1.jsonasset353,163 bytes
  • evals/published/2026-10-18-claude-opus-5-5-run1.mddoc2,225 bytes
  • evals/published/2026-10-18-claude-opus-5-5-run2-fixes.jsonasset44,783 bytes
  • evals/published/2026-10-18-claude-opus-5-5-run2-fixes.mddoc598 bytes
  • evals/run_evals.pyruns28,064 bytes
  • examples/github-workflow-popia-scan.ymlasset992 bytes
  • examples/github-workflow-site-check.ymlasset918 bytes
  • hosted/site-check/.gitignoreasset14 bytes
  • hosted/site-check/README.mddoc19,062 bytes
  • hosted/site-check/adapters/deno.jsonasset345 bytes
  • hosted/site-check/adapters/node-pinned-fetch.tsruns3,196 bytes
  • hosted/site-check/adapters/node-supabase-rate-limit.tsruns1,050 bytes
  • hosted/site-check/adapters/package-lock.jsonasset5,516 bytes
  • hosted/site-check/adapters/package.jsonruns497 bytes
  • hosted/site-check/adapters/test/node.smoke.tsruns8,309 bytes
  • hosted/site-check/deno.jsonasset894 bytes
  • hosted/site-check/deno.lockasset2,021 bytes
  • hosted/site-check/report/actions.jsonasset9,037 bytes
  • hosted/site-check/supabase/functions/site-check/core.tsruns57,861 bytes
  • hosted/site-check/supabase/functions/site-check/index.tsruns934 bytes
  • hosted/site-check/supabase/migrations/site_check_rate.sqlasset2,529 bytes
  • hosted/site-check/tests/actions.test.tsruns2,342 bytes
  • hosted/site-check/tests/checks.test.tsruns20,344 bytes
  • hosted/site-check/tests/guard.test.tsruns20,581 bytes
  • hosted/site-check/tests/handler.test.tsruns18,395 bytes
  • hosted/site-check/tests/hardening.test.tsruns19,860 bytes
  • hosted/site-check/tests/helpers.tsruns4,037 bytes
  • hosted/site-check/tests/sql/run_sql_tests.shruns5,871 bytes
  • i18n/README.mddoc4,726 bytes
  • i18n/af/breach-notice-short.mddoc1,121 bytes
  • i18n/af/collection-notice.mddoc1,165 bytes
  • i18n/af/cookie-banner.jsonasset1,226 bytes
  • i18n/af/marketing-consent.mddoc784 bytes
  • i18n/en/breach-notice-short.mddoc688 bytes
  • i18n/en/collection-notice.mddoc677 bytes
  • i18n/en/cookie-banner.jsonasset743 bytes
  • i18n/en/marketing-consent.mddoc374 bytes
  • i18n/tn/breach-notice-short.mddoc1,159 bytes
  • i18n/tn/collection-notice.mddoc1,294 bytes
  • i18n/tn/cookie-banner.jsonasset1,305 bytes
  • i18n/tn/marketing-consent.mddoc835 bytes
  • i18n/xh/breach-notice-short.mddoc1,088 bytes
  • i18n/xh/collection-notice.mddoc1,195 bytes
  • i18n/xh/cookie-banner.jsonasset1,252 bytes
  • i18n/xh/marketing-consent.mddoc848 bytes
  • i18n/zu/breach-notice-short.mddoc1,093 bytes
  • i18n/zu/collection-notice.mddoc1,180 bytes
  • i18n/zu/cookie-banner.jsonasset1,252 bytes
  • i18n/zu/marketing-consent.mddoc843 bytes
  • mcp/README.mddoc1,061 bytes
  • mcp/__init__.pyruns0 bytes
  • mcp/server.pyruns32,264 bytes
  • mcp/smoke_test.pyruns6,719 bytes
  • mcp/tools.jsonasset7,045 bytes
  • package.jsonruns1,116 bytes
  • packs/README.mddoc6,011 bytes
  • packs/_template.mddoc1,643 bytes
  • packs/ecommerce.mddoc14,611 bytes
  • packs/education-and-children.mddoc14,597 bytes
  • packs/fintech.mddoc22,274 bytes
  • packs/health.mddoc17,173 bytes
  • packs/hr-and-recruitment.mddoc16,922 bytes
  • packs/short-term-rentals.mddoc16,918 bytes
  • pyproject.tomlasset3,347 bytes
  • references/01-popia-legal-map.mddoc24,438 bytes
  • references/02-build-patterns.mddoc17,948 bytes
  • references/03-security-controls.mddoc11,051 bytes
  • references/04-stack-playbooks.mddoc11,362 bytes
  • references/05-direct-marketing.mddoc5,303 bytes
  • references/06-cross-border-and-operators.mddoc4,352 bytes
  • references/07-breach-response.mddoc5,751 bytes
  • references/08-ai-and-automated-decisions.mddoc4,807 bytes
  • references/09-audit-procedure.mddoc11,000 bytes
  • references/10-regulatory-watch.mddoc5,750 bytes
  • registry/README.mddoc18,661 bytes
  • registry/build.pyruns4,168 bytes
  • registry/popia/account/privacy-centre.tsxruns17,061 bytes
  • registry/popia/consent/consent-banner.tsxruns8,730 bytes
  • registry/popia/consent/consent-gate.tsxruns9,023 bytes
  • registry/popia/consent/consent-provider.tsxruns8,266 bytes
  • registry/popia/forms/collection-notice.tsxruns3,253 bytes
  • registry/popia/forms/marketing-opt-in.tsxruns7,869 bytes
  • registry/popia/forms/sa-id-input.tsxruns4,170 bytes
  • registry/popia/lib/consent-record.tsruns4,598 bytes
  • registry/popia/lib/redact.tsruns6,833 bytes
  • registry/popia/lib/sa-id.tsruns3,501 bytes
  • registry/popia/supabase/functions/_shared/privacy-config.tsruns14,640 bytes
  • registry/popia/supabase/functions/consent/index.tsruns4,841 bytes
  • registry/popia/supabase/functions/dsar-delete/index.tsruns5,521 bytes
  • registry/popia/supabase/functions/dsar-export/index.tsruns3,945 bytes
  • registry/popia/supabase/functions/unsubscribe/index.tsruns5,418 bytes
  • registry/popia/supabase/migrations/0001_privacy_schema.sqlasset14,123 bytes
  • registry/popia/supabase/migrations/0002_retention.sqlasset3,859 bytes
  • registry/popia/supabase/migrations/0003_security_checks.sqlasset3,444 bytes
  • registry/popia/supabase/migrations/0004_function_guards.sqlasset6,381 bytes
  • registry/r/collection-notice.jsonasset3,898 bytes
  • registry/r/consent-banner.jsonasset9,855 bytes
  • registry/r/consent-gate.jsonasset10,025 bytes
  • registry/r/consent-provider.jsonasset9,213 bytes
  • registry/r/consent-record.jsonasset5,300 bytes
  • registry/r/consent-system.jsonasset32,913 bytes
  • registry/r/marketing-opt-in.jsonasset8,934 bytes
  • registry/r/privacy-centre.jsonasset18,596 bytes
  • registry/r/redact.jsonasset7,772 bytes
  • registry/r/registry.jsonasset10,250 bytes
  • registry/r/sa-id-input.jsonasset5,029 bytes
  • registry/r/sa-id.jsonasset4,114 bytes
  • registry/r/supabase-privacy-functions.jsonasset37,294 bytes
  • registry/r/supabase-privacy-schema.jsonasset29,734 bytes
  • registry/registry.jsonasset10,250 bytes
  • regwatch/sources.jsonasset1,154 bytes
  • regwatch/state.jsonasset16,254 bytes
  • scripts/__init__.pyruns0 bytes
  • scripts/build_adapters.pyruns6,890 bytes
  • scripts/cli.pyruns4,217 bytes
  • scripts/popia_badge.pyruns5,218 bytes
  • scripts/popia_fleet.pyruns15,458 bytes
  • scripts/popia_init.pyruns7,441 bytes
  • scripts/popia_operators.pyruns33,731 bytes
  • scripts/popia_pack.pyruns44,438 bytes
  • scripts/popia_scan.pyruns25,995 bytes
  • scripts/popia_vendors.pyruns9,723 bytes
  • scripts/regwatch.pyruns13,722 bytes
  • scripts/rules/__init__.pyruns0 bytes
  • scripts/rules/access.pyruns6,799 bytes
  • scripts/rules/consent.pyruns10,773 bytes
  • scripts/rules/pii.pyruns7,164 bytes
  • scripts/rules/repo.pyruns7,157 bytes
  • scripts/rules/secrets.pyruns5,595 bytes
  • scripts/rules/thirdparty.pyruns5,217 bytes
  • scripts/rules/web.pyruns11,818 bytes
  • scripts/sa_id.pyruns4,042 bytes
  • scripts/site_check.pyruns26,645 bytes
  • scripts/sql/supabase_inventory.sqlasset5,533 bytes
  • scripts/style_lint.pyruns3,707 bytes
  • scripts/supabase_check.pyruns18,544 bytes
  • server.jsonasset812 bytes
  • templates/audit-report.mddoc8,528 bytes
  • templates/breach-notice-data-subject.mddoc5,453 bytes
  • templates/breach-regulator-checklist.mddoc3,412 bytes
  • templates/consent-patterns.mddoc7,704 bytes
  • templates/cookie-notice.mddoc2,795 bytes
  • templates/dsar-runbook.mddoc6,343 bytes
  • templates/operator-agreement-checklist.mddoc5,314 bytes
  • templates/pack-questionnaire.jsonasset18,548 bytes
  • templates/paia-manual-scaffold.mddoc5,380 bytes
  • templates/pia-template.mddoc6,692 bytes
  • templates/privacy-notice.mddoc10,456 bytes
  • templates/retention-schedule.csvasset4,033 bytes
  • templates/ropa.csvasset1,484 bytes
  • tests/__init__.pyruns0 bytes
  • tests/automation/__init__.pyruns0 bytes
  • tests/automation/fixtures/__init__.pyruns0 bytes
  • tests/automation/fixtures/regulator-list.htmlasset1,419 bytes
  • tests/automation/test_badge.pyruns3,031 bytes
  • tests/automation/test_cli.pyruns2,793 bytes
  • tests/automation/test_fleet.pyruns4,501 bytes
  • tests/automation/test_init.pyruns4,444 bytes
  • tests/automation/test_mcp.pyruns4,104 bytes
  • tests/automation/test_regwatch.pyruns4,018 bytes
  • tests/fixtures/README.mddoc379 bytes
  • tests/fixtures/demo-app/firestore.rulesasset162 bytes
  • tests/fixtures/demo-app/index.htmlasset315 bytes
  • tests/fixtures/demo-app/package.jsonruns180 bytes
  • tests/fixtures/demo-app/src/ai.tsruns542 bytes
  • tests/fixtures/demo-app/src/components/SignupForm.tsxruns956 bytes
  • tests/fixtures/demo-app/src/newsletter.tsruns288 bytes
  • tests/fixtures/demo-app/supabase/functions/lead/index.tsruns244 bytes
  • tests/fixtures/demo-app/supabase/migrations/20260101000000_init.sqlasset498 bytes
  • tests/fixtures/operators-app/.env.exampleasset49 bytes
  • tests/fixtures/operators-app/api/triage.pyruns386 bytes
  • tests/fixtures/operators-app/package.jsonruns242 bytes
  • tests/fixtures/operators-app/public/contact.htmlasset243 bytes
  • tests/fixtures/operators-app/requirements.txtdoc91 bytes
  • tests/fixtures/operators-app/src/checkout.tsruns594 bytes
  • tests/fixtures/operators-app/src/sms.jsruns710 bytes
  • tests/fixtures/operators-app/supabase-edge.tsruns292 bytes
  • tests/fixtures/operators-app/tests/checkout.test.tsruns68 bytes
  • tests/fixtures/supabase/lovable-hardened.jsonasset4,401 bytes
  • tests/fixtures/supabase/seed.sqlasset3,622 bytes
  • tests/fixtures/supabase/seeded-risky.jsonasset3,739 bytes
  • tests/pack/__init__.pyruns0 bytes
  • tests/pack/kasi-bookings-answers.jsonasset5,297 bytes
  • tests/pack/test_pack.pyruns12,583 bytes
  • tests/scanner/__init__.pyruns0 bytes
  • tests/scanner/test_baseline.pyruns5,799 bytes
  • tests/scanner/test_operators.pyruns7,813 bytes
  • tests/scanner/test_rules.pyruns9,843 bytes
  • tests/scanner/test_site_check.pyruns5,305 bytes
  • tests/security/__init__.pyruns0 bytes
  • tests/security/test_hardening.pyruns7,177 bytes
  • tests/supabase/__init__.pyruns0 bytes
  • tests/supabase/run_live.shruns1,206 bytes
  • tests/supabase/test_supabase_check.pyruns10,345 bytes
Read SKILL.md

# POPIA Compliance & Security Audit

You are acting as a privacy-engineering partner for a South African builder. Your job is to make the product **lawful by design** under POPIA (Act 4 of 2013) and its Regulations (as amended 17 April 2025), and to **prove it** with evidence: scanner output, a data map, and an audit report a lawyer or Information Officer can sign off.

Law in this skill is current to **18 October 2026**. Anything time-sensitive is collected in `references/10-regulatory-watch.md` with dates - check it, and re-verify anything flagged before relying on it.

> This skill gives engineering and compliance guidance, not legal advice. Say so once per deliverable (not on every message). Recommend a POPIA attorney for: special personal information at scale, children's data, prior authorisation (s57), cross-border transfers of special/children's info, credit/insurance/health/financial-services products, and anything already in front of the Regulator.

## Pick the mode

Read the request and pick one (or chain them):

| Mode | When | Go to |
|---|---|---|
View the complete source on GitHub

Skills included

  • popia-compliance
  • popia-kit

Repository facts

License
MIT
Created
Oct 18, 2026
Last pushed
Oct 27, 2026
Forks
2
Open issues
7
Project activity
Active

Before you install

Network or broad shell

Automated signals, not a guarantee

Checked Nov 7, 2026

Not fully analyzed: some files were too long, unreadable or missing. This never lowers the level.

Show 160 notes
  • SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:21 Analysis incomplete: physical or joined line exceeds character limit
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:19 Analysis incomplete: physical or joined line exceeds character limit
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:20 Analysis incomplete: physical or joined line exceeds character limit
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:91 [Credential-related source omitted]
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:89 Analysis incomplete: physical or joined line exceeds character limit
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:90 Analysis incomplete: physical or joined line exceeds character limit
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:155 Analysis incomplete: physical or joined line exceeds character limit
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:153 Analysis incomplete: physical or joined line exceeds character limit
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:154 Analysis incomplete: physical or joined line exceeds character limit
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:210 Analysis incomplete: physical or joined line exceeds character limit
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:208 Analysis incomplete: physical or joined line exceeds character limit
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:209 Analysis incomplete: physical or joined line exceeds character limit
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:267 [Credential-related source omitted]
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:265 Analysis incomplete: physical or joined line exceeds character limit
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:266 Analysis incomplete: physical or joined line exceeds character limit
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:323 Analysis incomplete: physical or joined line exceeds character limit
  • evals/published/2026-10-18-claude-opus-5-5-run1.json:321 Analysis incomplete: physical or joined line exceeds character limit
Show all 1,276 signals by file

.github/ISSUE_TEMPLATE/config.yml

  • Network accessline 2
    contact_links:   - name: Security vulnerability in this project     url: https://github.com/VenusOne-Lee/popia-compliance/security/advisories/new
  • Network accessline 3
    - name: Security vulnerability in this project     url: https://github.com/VenusOne-Lee/popia-compliance/security/advisories/new     about: Please report vulnerabilities privately.
  • Network accessline 4
    url: https://github.com/VenusOne-Lee/popia-compliance/security/advisories/new

.github/ISSUE_TEMPLATE/legal-reviewer.yml

  • Network accessline 13
    Please read [the reviewer terms in GOVERNANCE.md](https://github.com/VenusOne-Lee/popia-compliance/blob/main/GOVERNANCE.md#reviewer-terms) first. In short: your review is not legal advice to users and creates no client relationship.

.github/workflows/ci.yml

  • Dangerous shell operationline 160
    run: sudo -E env "PATH=$PATH" bash tests/supabase/run_live.sh
  • Dangerous shell operationline 195
    run: sudo -E bash tests/sql/run_sql_tests.sh

.github/workflows/release.yml

  • Network accessline 67
    environment:       name: pypi       url: https://pypi.org/project/popia-kit/
  • Network accessline 68
    name: pypi       url: https://pypi.org/project/popia-kit/     permissions:
  • Network accessline 69
    url: https://pypi.org/project/popia-kit/
  • Dangerous shell operationline 79
    run: rm -f dist/*.tgz
  • Network accessline 89
    environment:       name: npm       url: https://www.npmjs.com/package/popia-kit
  • Network accessline 90
    name: npm       url: https://www.npmjs.com/package/popia-kit     permissions:
  • Network accessline 91
    url: https://www.npmjs.com/package/popia-kit
  • Network accessline 101
    with:           node-version: "22"           registry-url: "https://registry.npmjs.org"
  • Network accessline 103
    registry-url: "https://registry.npmjs.org"
  • Access to secretsline 110
    [Credential-related source omitted]

.gitignore

  • Access to secretsline 5
    [Credential-related source omitted]

CHANGELOG.md

  • Network accessline 44
    ### Added - Release workflow: a version tag publishes to PyPI and npm with trusted publishing and npm provenance, then creates a GitHub release. Setup in docs/releasing.md. - One-line install, no clone needed: `uvx --from git+https://github
  • Network accessline 45
    - Release workflow: a version tag publishes to PyPI and npm with trusted publishing and npm provenance, then creates a GitHub release. Setup in docs/releasing.md. - One-line install, no clone needed: `uvx --from git+https://github.com/Venus
  • Network accessline 46
    - One-line install, no clone needed: `uvx --from git+https://github.com/VenusOne-Lee/popia-compliance popia-kit scan .`, the `pipx run` equivalent, or `npx -y github:VenusOne-Lee/popia-compliance scan .`. One `popia-kit` command with `scan`
  • Network accessline 73
    - MCP server: unexpected errors return and log the exception type only; `popia_scan` refuses filesystem roots, the home folder and credential or system folders (optionally limited further with `POPIA_SCAN_ROOTS`); `popia_generate_pack` writ
  • Network accessline 74
    - Scanner: no longer follows symlinks out of the scanned tree. - Fleet scan: clones only `https://` or `git@host:` URLs, separates the URL with `--`, disables hooks, submodules, LFS smudge and the file and ext transports, and redacts tokens
  • Network accessline 75
    - Fleet scan: clones only `https://` or `git@host:` URLs, separates the URL with `--`, disables hooks, submodules, LFS smudge and the file and ext transports, and redacts tokens from clone errors.

CODE_OF_CONDUCT.md

  • Network accessline 3
    This project adopts the [Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).

GOVERNANCE.md

  • Network accessline 23
    **To volunteer:** email **popiakit@venusone.africa**, or, if you're on GitHub, open a [Legal reviewer: volunteer](https://github.com/VenusOne-Lee/popia-compliance/issues/new?template=legal-reviewer.yml) issue (that issue is public).

LICENSE-CONTENT

  • Network accessline 13
    Suggested attribution:   "Based on the POPIA Compliance Skill by Venus One (Venus ARC (Pty) Ltd) and contributors,    https://github.com/VenusOne-Lee/popia-compliance, licensed under CC BY 4.0."
  • Network accessline 15
    https://github.com/VenusOne-Lee/popia-compliance, licensed under CC BY 4.0."
  • Network accessline 17
    Full legal code: https://creativecommons.org/licenses/by/4.0/legalcode

README.md

  • Network accessline 3
    [![PyPI](https://img.shields.io/pypi/v/popia-kit)](https://pypi.org/project/popia-kit/) [![CI](https://github.com/VenusOne-Lee/popia-compliance/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/VenusOne-Lee/popia-complianc
  • Network accessline 41
    **No install:** check a live site you own in your browser at [popiakit.co.za/check](https://popiakit.co.za/check). It runs the same passive site check as `popia-kit site-check`, and you can download the report as a PDF. Project site: [popia
  • Network accessline 61
    Python 3.8 or newer and nothing else. No install needed: run it from [PyPI](https://pypi.org/project/popia-kit/) with whichever tool you already have.
  • Network accessline 74
    To keep it installed: `pipx install popia-kit`, then use `popia-kit` anywhere. For the latest code on `main`, use `uvx --from git+https://github.com/VenusOne-Lee/popia-compliance popia-kit scan .`
  • Network accessline 81
    | `popia-kit scan . --changed-since origin/main` | Report only findings in files changed since a branch | | `popia-kit operators ./my-app --format csv --out operators.csv` | Every third-party API or SDK the code calls, and the personal info
  • Network accessline 82
    | `popia-kit operators ./my-app --format csv --out operators.csv` | Every third-party API or SDK the code calls, and the personal information that appears to reach each. Your operator and cross-border list | | `popia-kit site-check https://
  • Network accessline 83
    | `popia-kit site-check https://example.co.za --out site-check.md` | Passive check of a live site you own |
  • Network accessline 93
    git clone https://github.com/VenusOne-Lee/popia-compliance
  • Network accessline 135
    - **Report a law change:** open a [law-change issue](.github/ISSUE_TEMPLATE/law-change.yml). A monthly automated watch of the Regulator's site opens these too. - **Report a false positive:** open a [false positive issue](.github/ISSUE_TEMPL
  • Network accessline 136
    - **Report a false positive:** open a [false positive issue](.github/ISSUE_TEMPLATE/false-positive.yml) with the rule ID and a small example. - **First time here:** look for issues labelled [good first issue](https://github.com/VenusOne-Lee
  • Network accessline 137
    - **First time here:** look for issues labelled [good first issue](https://github.com/VenusOne-Lee/popia-compliance/labels/good%20first%20issue).

SKILL.md

  • Network accessline 81
    python3 scripts/site_check.py https://example.co.za --out site-check.md

action.yml

  • Access to secretsline 157
    [Credential-related source omitted]
  • Access to secretsline 163
    [Credential-related source omitted]
  • Access to secretsline 183
    [Credential-related source omitted]
  • Access to secretsline 189
    [Credential-related source omitted]

adapters/_core.md

  • Network accessline 5
    Full skill, scanner and references: https://github.com/VenusOne-Lee/popia-compliance

adapters/agents/AGENTS.md

  • Network accessline 6
    Full skill, scanner and references: https://github.com/VenusOne-Lee/popia-compliance

adapters/chatgpt/custom-gpt-instructions.md

  • Network accessline 6
    Full skill, scanner and references: https://github.com/VenusOne-Lee/popia-compliance

adapters/claude/CLAUDE.md

  • Network accessline 6
    Full skill, scanner and references: https://github.com/VenusOne-Lee/popia-compliance

adapters/copilot/.github/copilot-instructions.md

  • Network accessline 6
    Full skill, scanner and references: https://github.com/VenusOne-Lee/popia-compliance

adapters/cursor/.cursor/rules/popia.mdc

  • Network accessline 11
    Full skill, scanner and references: https://github.com/VenusOne-Lee/popia-compliance

adapters/gemini/GEMINI.md

  • Network accessline 6
    Full skill, scanner and references: https://github.com/VenusOne-Lee/popia-compliance

adapters/lovable/knowledge.md

  • Network accessline 6
    Full skill, scanner and references: https://github.com/VenusOne-Lee/popia-compliance

adapters/lovable/skill/SKILL.md

  • Network accessline 12
    Full skill, scanner and references: https://github.com/VenusOne-Lee/popia-compliance
  • Network accessline 170
    The same query and rules ship as `uvx popia-kit supabase sql` and `uvx popia-kit supabase check result.json`. For the live site, use the free passive check at https://popiakit.co.za/check.

adapters/windsurf/.windsurf/rules/popia.md

  • Network accessline 10
    Full skill, scanner and references: https://github.com/VenusOne-Lee/popia-compliance

adapters/windsurf/.windsurfrules

  • Network accessline 6
    Full skill, scanner and references: https://github.com/VenusOne-Lee/popia-compliance

bin/popia-kit.js

  • Executable scriptline 1
    #!/usr/bin/env node
  • Network accessline 23
    process.exit(r.status === null ? 1 : r.status); } console.error('popia-kit needs Python 3.8 or newer. Install it from https://www.python.org/downloads/ and try again.');
  • Network accessline 24
    } console.error('popia-kit needs Python 3.8 or newer. Install it from https://www.python.org/downloads/ and try again.'); process.exit(1);
  • Network accessline 25
    console.error('popia-kit needs Python 3.8 or newer. Install it from https://www.python.org/downloads/ and try again.');

docs/automation.md

  • Network accessline 99
    repos:   - repo: https://github.com/VenusOne-Lee/popia-compliance
  • Network accessline 100
    - repo: https://github.com/VenusOne-Lee/popia-compliance
  • Network accessline 204
    https://github.com/your-org/lovable-project-one.git
  • Network accessline 205
    https://github.com/your-org/lovable-project-two.git
  • Network accessline 228
    `--shields-json` writes a [shields.io endpoint](https://shields.io/badges/endpoint-badge) file. Commit it (or publish it from CI to a branch or Pages) and use:
  • Network accessline 231
    ![POPIA scan](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/OWNER/REPO/BRANCH/popia-badge.json)

docs/mcp.md

  • Access to secretsline 149
    [Credential-related source omitted]

docs/quickstart.md

  • Network accessline 15
    Only want to check a live site? Use [popiakit.co.za/check](https://popiakit.co.za/check) in your browser. Nothing to install.
  • Network accessline 26
    R=https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r
  • Network accessline 95
    uvx popia-kit site-check https://your-site.co.za --out site-check.md

docs/releasing.md

  • Network accessline 53
    `server.json` describes the MCP server for the [official MCP Registry](https://registry.modelcontextprotocol.io), which Claude, Cursor, VS Code and other clients use to find servers. The registry checks that the PyPI package is ours by find
  • Network accessline 58
    2. From the repository root on `main`: `mcp-publisher login github`. Sign in as VenusOne-Lee in the browser. 3. `mcp-publisher publish`. It reads `server.json`. 4. Check it is listed: `curl "https://registry.modelcontextprotocol.io/v0/serve
  • Network accessline 60
    4. Check it is listed: `curl "https://registry.modelcontextprotocol.io/v0/servers?search=popia-kit"`.

docs/site-check-bot.md

  • Network accessline 7
    The POPIA Kit site check looks at a live website for privacy and security leads: HTTPS, security headers, cookies set before consent, trackers and links to a privacy notice and PAIA manual. Someone ran it on your site, either at [popiakit.c
  • Network accessline 12
    popia-site-check/1.0 (+https://github.com/VenusOne-Lee/popia-compliance/blob/main/docs/site-check-bot.md; passive)
  • Network accessline 19
    - The page that was entered, following up to 5 redirects. - `http://` on the same host, without following the redirect, to see whether plain HTTP sends visitors to HTTPS.
  • Network accessline 20
    - `http://` on the same host, without following the redirect, to see whether plain HTTP sends visitors to HTTPS.
  • Network accessline 42
    - Run the check yourself from a network you already trust: `uvx popia-kit site-check https://your-site.co.za`

evals/README.md

  • Access to secretsline 33
    [Credential-related source omitted]
  • Access to secretsline 36
    [Credential-related source omitted]
  • Network accessline 39
    # Any OpenAI-compatible server (Ollama, vLLM, LM Studio, OpenRouter, Azure-style gateways) export OPENAI_COMPATIBLE_BASE_URL=http://localhost:11434   # /v1/chat/completions is appended
  • Network accessline 40
    export OPENAI_COMPATIBLE_BASE_URL=http://localhost:11434   # /v1/chat/completions is appended

evals/fixtures/chatbot/support_bot.py

  • Executable scriptline 1
    Executable file
  • Network accessline 5
    prompt = f"Customer record: {json.dumps(customer)}\nQuestion: {question}"     body = json.dumps({"model": "some-model", "messages": [{"role": "user", "content": prompt}]}).encode()     req = urllib.request.Request("https://api.example-llm.c
  • Network accessline 6
    body = json.dumps({"model": "some-model", "messages": [{"role": "user", "content": prompt}]}).encode()     req = urllib.request.Request("https://api.example-llm.com/v1/chat/completions", data=body,                                  headers={
  • Network accessline 7
    req = urllib.request.Request("https://api.example-llm.com/v1/chat/completions", data=body,
  • Network accessline 8
    headers={"content-type": "application/json"})     resp = urllib.request.urlopen(req).read()     with open("transcripts.log", "a") as f:          # kept forever
  • Network accessline 9
    resp = urllib.request.urlopen(req).read()

evals/fixtures/env-service-role/env.local.txt

  • Network accessline 1
    # FAKE EVAL FIXTURE. Represents a committed .env.local file. Values are synthetic placeholders, not credentials. VITE_SUPABASE_URL=https://fakeevalfixture000.supabase.co
  • Network accessline 2
    VITE_SUPABASE_URL=https://fakeevalfixture000.supabase.co

evals/fixtures/env-service-role/supabaseClient.ts

  • Executable scriptline 1
    Executable file
  • Access to secretsline 5
    [Credential-related source omitted]

evals/fixtures/kids-app/analytics.html

  • Network accessline 1
    <!-- FAKE EVAL FIXTURE --> <script async src="https://www.googletagmanager.com/gtag/js?id=G-FAKE000000"></script>
  • Network accessline 2
    <script async src="https://www.googletagmanager.com/gtag/js?id=G-FAKE000000"></script>
  • Network accessline 6
    gtag('js', new Date());   gtag('config', 'G-FAKE000000');   !function(f,b,e,v,n,t,s){}(window, document,'script','https://connect.facebook.net/en_US/fbevents.js');
  • Network accessline 7
    gtag('config', 'G-FAKE000000');   !function(f,b,e,v,n,t,s){}(window, document,'script','https://connect.facebook.net/en_US/fbevents.js');   fbq('init', '000000000000000');
  • Network accessline 8
    !function(f,b,e,v,n,t,s){}(window, document,'script','https://connect.facebook.net/en_US/fbevents.js');

evals/fixtures/loan-decision/decide.ts

  • Executable scriptline 1
    Executable file

evals/fixtures/newsletter-form/Newsletter.tsx

  • Executable scriptline 1
    Executable file

evals/fixtures/react-preticked/SignupForm.tsx

  • Executable scriptline 1
    Executable file

evals/fixtures/repo-audit/src/SignupForm.tsx

  • Executable scriptline 1
    Executable file

evals/fixtures/repo-audit/src/index.html

  • Network accessline 1
    <!-- FAKE EVAL FIXTURE --> <script async src="https://www.googletagmanager.com/gtag/js?id=G-FAKE000000"></script>
  • Network accessline 2
    <script async src="https://www.googletagmanager.com/gtag/js?id=G-FAKE000000"></script>
  • Network accessline 6
    gtag('js', new Date());   gtag('config', 'G-FAKE000000');   !function(f,b,e,v,n,t,s){}(window, document,'script','https://connect.facebook.net/en_US/fbevents.js');
  • Network accessline 7
    gtag('config', 'G-FAKE000000');   !function(f,b,e,v,n,t,s){}(window, document,'script','https://connect.facebook.net/en_US/fbevents.js');   fbq('init', '000000000000000');
  • Network accessline 8
    !function(f,b,e,v,n,t,s){}(window, document,'script','https://connect.facebook.net/en_US/fbevents.js');

evals/fixtures/repo-audit/src/logger.ts

  • Executable scriptline 1
    Executable file

evals/published/2026-10-18-claude-opus-5-5-run1.json

  • Access to secretsline 91
    [Credential-related source omitted]
  • Network accessline 265
    "references/06-cross-border-and-operators.md"       ],       "answer": "# Order-support bot: improved, and made POPIA-safe\n\nThe current bot sends the **entire customer record** to a US-hosted LLM on every question. That includes the **SA 
  • Network accessline 266
    ],       "answer": "# Order-support bot: improved, and made POPIA-safe\n\nThe current bot sends the **entire customer record** to a US-hosted LLM on every question. That includes the **SA ID number, bank account number, email, phone and com
  • Network accessline 267
    [Credential-related source omitted]
  • Access to secretsline 267
    [Credential-related source omitted]
  • Access to secretsline 1213
    [Credential-related source omitted]

evals/run_evals.py

  • Executable scriptline 1
    #!/usr/bin/env python3
  • Network accessline 17
    [Credential-related source omitted]
  • Access to secretsline 17
    [Credential-related source omitted]
  • Network accessline 18
    [Credential-related source omitted]
  • Access to secretsline 18
    [Credential-related source omitted]
  • Network accessline 19
    OPENAI_COMPATIBLE_BASE_URL                for --provider openai-compatible (e.g. http://localhost:11434)
  • Access to secretsline 20
    [Credential-related source omitted]
  • Network accessline 255
    last = None     for attempt in range(retries + 1):         req = urllib.request.Request(url, data=data, headers=headers, method="POST")
  • Network accessline 256
    for attempt in range(retries + 1):         req = urllib.request.Request(url, data=data, headers=headers, method="POST")         try:
  • Network accessline 257
    req = urllib.request.Request(url, data=data, headers=headers, method="POST")
  • Network accessline 260
    with urllib.request.urlopen(req, timeout=timeout) as resp:  # nosec B310
  • Access to secretsline 281
    [Credential-related source omitted]
  • Access to secretsline 283
    [Credential-related source omitted]
  • Network accessline 286
    if temperature is not None:             body["temperature"] = temperature         res = _post("https://api.anthropic.com/v1/messages",
  • Network accessline 287
    [Credential-related source omitted]
  • Network accessline 288
    res = _post("https://api.anthropic.com/v1/messages",
  • Network accessline 292
    if provider == "openai":             key = os.environ.get("OPENAI_API_KEY")             base = os.environ.get("OPENAI_BASE_URL", "https://api.openai.com")
  • Network accessline 293
    [Credential-related source omitted]
  • Access to secretsline 293
    [Credential-related source omitted]
  • Network accessline 294
    [Credential-related source omitted]
  • Access to secretsline 294
    [Credential-related source omitted]
  • Access to secretsline 296
    [Credential-related source omitted]
  • Access to secretsline 298
    [Credential-related source omitted]
  • Access to secretsline 299
    [Credential-related source omitted]

examples/github-workflow-site-check.yml

  • Network accessline 24
    with:           scan: "false"           site-url: https://example.co.za/
  • Network accessline 25
    scan: "false"           site-url: https://example.co.za/           site-fail-on: high
  • Network accessline 26
    site-url: https://example.co.za/

hosted/site-check/README.md

  • Network accessline 35
    { "url": "https://example.co.za", "turnstileToken": "<token from the widget>" }
  • Network accessline 38
    A URL without a scheme gets `https://`. A successful response is HTTP 200:
  • Network accessline 41
    {   "url": "https://example.co.za/",
  • Network accessline 42
    "url": "https://example.co.za/",
  • Network accessline 43
    "final_url": "https://www.example.co.za/",
  • Network accessline 72
    | Name | Required | Default | Purpose | |---|---|---|---| | `ALLOWED_ORIGINS` | yes | none | Comma separated list of exact origins, for example `https://venusone.co.za,https://www.venusone.co.za`. Requests without an allowed `Origin` get 40
  • Network accessline 73
    |---|---|---|---| | `ALLOWED_ORIGINS` | yes | none | Comma separated list of exact origins, for example `https://venusone.co.za,https://www.venusone.co.za`. Requests without an allowed `Origin` get 403. Empty means everything is refused. | 
  • Network accessline 74
    | `ALLOWED_ORIGINS` | yes | none | Comma separated list of exact origins, for example `https://venusone.co.za,https://www.venusone.co.za`. Requests without an allowed `Origin` get 403. Empty means everything is refused. |
  • Network accessline 100
    supabase secrets set ALLOWED_ORIGINS="https://venusone.co.za,https://www.venusone.co.za" \
  • Network accessline 116
    const res = await fetch("https://<ref>.supabase.co/functions/v1/site-check", {
  • Network accessline 128
    curl -s -X POST https://<ref>.supabase.co/functions/v1/site-check \
  • Network accessline 129
    -H 'origin: https://venusone.co.za' -H 'content-type: application/json' \
  • Network accessline 167
    Every request the function makes to the target goes through one guarded fetch. That covers the main page, every redirect hop, the `http://` redirect probe, `/.well-known/security.txt` and the optional notice paths.
  • Network accessline 171
    1. Parse with the WHATWG URL parser. It normalises tricks like `http://2130706433/`, `http://0x7f.1/` and `http://127.1/` into dotted quads, so they are caught.
  • Network accessline 222
    - Only `text/html` bodies are parsed, capped at 1.5 MB (Python read 2,000,000 bytes of any type). - Cookies are parsed with browser rules (RFC 6265) instead of `SimpleCookie`. Results match on the Python test cases. - `max-age` in HSTS and 
  • Network accessline 223
    [Credential-related source omitted]
  • Access to secretsline 223
    [Credential-related source omitted]
  • Network accessline 224
    - `max-age` in HSTS and `http://` in script sources are matched case-insensitively.

hosted/site-check/adapters/node-pinned-fetch.ts

  • Executable scriptline 1
    Executable file

hosted/site-check/adapters/node-supabase-rate-limit.ts

  • Executable scriptline 1
    Executable file

hosted/site-check/adapters/package-lock.json

  • Network accessline 17
    "node_modules/@supabase/auth-js": {       "version": "2.118.0",       "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.118.0.tgz",
  • Network accessline 18
    "version": "2.118.0",       "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.118.0.tgz",       "integrity": "sha512-xWfPXyhS2KDSfAneMtI6p/tBnbdiz/5vaRKXEqUfzfC9WCniKr2Ds5o5G7RIGig0bom4z6wUY9zaV07WDVTfnQ==",
  • Network accessline 19
    "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.118.0.tgz",
  • Network accessline 30
    "node_modules/@supabase/functions-js": {       "version": "2.118.0",       "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.118.0.tgz",
  • Network accessline 31
    "version": "2.118.0",       "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.118.0.tgz",       "integrity": "sha512-4ofletPa/noKAU6KHeqsd3FztGzRPUEJRey1MKY0o+PIHaVePqRPShIXtl37gcVyIWaFM+zNg6r9zh0uUSFOSA==",
  • Network accessline 32
    "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.118.0.tgz",
  • Network accessline 43
    "node_modules/@supabase/phoenix": {       "version": "0.4.5",       "resolved": "https://registry.npmjs.org/@supabase/phoenix/-/phoenix-0.4.5.tgz",
  • Network accessline 44
    "version": "0.4.5",       "resolved": "https://registry.npmjs.org/@supabase/phoenix/-/phoenix-0.4.5.tgz",       "integrity": "sha512-aAn9H9ovVyeApKy11OWOrrOGq8DV68yWeH4ud2lN9fzn4aO8Zb5GLL9m1pUg9nLqIcT+ZDfAcsZe0E/nqdv2lw==",
  • Network accessline 45
    "resolved": "https://registry.npmjs.org/@supabase/phoenix/-/phoenix-0.4.5.tgz",
  • Network accessline 50
    "node_modules/@supabase/postgrest-js": {       "version": "2.118.0",       "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.118.0.tgz",
  • Network accessline 51
    "version": "2.118.0",       "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.118.0.tgz",       "integrity": "sha512-JXy+37KPApHsyotaQACovv/Jy9E22rfiWLRuT+2UjoEAcAIelDFIrsF/FY47Dy19mO7lI0Tq+zDCX8oJ2Ozfiw==",
  • Network accessline 52
    "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.118.0.tgz",
  • Network accessline 63
    "node_modules/@supabase/realtime-js": {       "version": "2.118.0",       "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.118.0.tgz",
  • Network accessline 64
    "version": "2.118.0",       "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.118.0.tgz",       "integrity": "sha512-pzCIEWjqD5/xqEZQerpt+q9rsgYkcrkgCJgf0W2ULqC4fYUeB/p9CFa3t9vn+IVh6dMN9r4ZCE262lZvUC+Bfw==",
  • Network accessline 65
    "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.118.0.tgz",
  • Network accessline 77
    "node_modules/@supabase/storage-js": {       "version": "2.118.0",       "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.118.0.tgz",
  • Network accessline 78
    "version": "2.118.0",       "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.118.0.tgz",       "integrity": "sha512-5MNkKt2Moy7Jud/sPX7/28BwdDxZMCVHGJT00b8op3KRa9LKSjOAYU5IcfGWIstaasAqY8GkbA6oI3IRDgB1YQ==",
  • Network accessline 79
    "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.118.0.tgz",
  • Network accessline 91
    "node_modules/@supabase/supabase-js": {       "version": "2.118.0",       "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.118.0.tgz",
  • Network accessline 92
    "version": "2.118.0",       "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.118.0.tgz",       "integrity": "sha512-FxI8UXMVC2UG7IkeiI8fuQZqq2oI3JeSuN8MPIC2RLVaoEAxKS0nqE6kii9Ewpq8f9gNPM6git1Y8f0vN/QLFw==",
  • Network accessline 93
    "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.118.0.tgz",
  • Network accessline 116
    "node_modules/@types/node": {       "version": "20.19.43",       "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
  • Network accessline 117
    "version": "20.19.43",       "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",       "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==",
  • Network accessline 118
    "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
  • Network accessline 126
    "node_modules/iceberg-js": {       "version": "0.8.1",       "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz",
  • Network accessline 127
    "version": "0.8.1",       "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz",       "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==",
  • Network accessline 128
    "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz",
  • Network accessline 136
    "node_modules/tslib": {       "version": "2.8.1",       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
  • Network accessline 137
    "version": "2.8.1",       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",       "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
  • Network accessline 138
    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
  • Network accessline 143
    "node_modules/undici": {       "version": "6.29.1",       "resolved": "https://registry.npmjs.org/undici/-/undici-6.29.1.tgz",
  • Network accessline 144
    "version": "6.29.1",       "resolved": "https://registry.npmjs.org/undici/-/undici-6.29.1.tgz",       "integrity": "sha512-EvRlUt/QqAEvXdQeRMYUfwOQsfzpDztykrG2owGEE/rBOBoYv9uF39ZzsNDh4lTFHhTr2XOZjKkYgueD12ZmGA==",
  • Network accessline 145
    "resolved": "https://registry.npmjs.org/undici/-/undici-6.29.1.tgz",
  • Network accessline 153
    "node_modules/undici-types": {       "version": "6.21.0",       "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
  • Network accessline 154
    "version": "6.21.0",       "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",       "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
  • Network accessline 155
    "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",

hosted/site-check/adapters/package.json

  • Executable scriptline 1
    Executable file

hosted/site-check/adapters/test/node.smoke.ts

  • Executable scriptline 1
    Executable file
  • Network accessline 13
    const ORIGIN = "https://check.venusone.co.za";
  • Network accessline 46
    page.append("set-cookie", "_ga=1");   const routes: Record<string, () => Response> = {     "https://example.co.za/": () => new Response(`<a href="/privacy">Privacy</a>`, { headers: page }),
  • Network accessline 47
    const routes: Record<string, () => Response> = {     "https://example.co.za/": () => new Response(`<a href="/privacy">Privacy</a>`, { headers: page }),   };
  • Network accessline 48
    "https://example.co.za/": () => new Response(`<a href="/privacy">Privacy</a>`, { headers: page }),
  • Network accessline 60
    });   const post = (url: string) =>     new Request("https://h/f", {
  • Network accessline 61
    const post = (url: string) =>     new Request("https://h/f", {       method: "POST",
  • Network accessline 62
    new Request("https://h/f", {
  • Network accessline 81
    assert.deepEqual(body.cookies.map((c: { name: string }) => c.name), ["_ga"]);   assert.equal(keys.length, 2, "per-IP and global");   const blocked = await handler(post("http://169.254.169.254/latest/meta-data/"));
  • Network accessline 82
    assert.equal(keys.length, 2, "per-IP and global");   const blocked = await handler(post("http://169.254.169.254/latest/meta-data/"));   assert.equal(blocked.status, 400);
  • Network accessline 83
    const blocked = await handler(post("http://169.254.169.254/latest/meta-data/"));
  • Network accessline 120
    const rebind: LookupFn = (_h, _o, cb) => cb(null, [{ address: "127.0.0.1", family: 4 }]);     const url = `http://rebind.example.co.za:${srv.port}/`;
  • Network accessline 121
    const url = `http://rebind.example.co.za:${srv.port}/`;
  • Network accessline 139
    return true;     });     await assert.rejects(createPinnedFetch()(`http://localhost:${srv.port}/`), TypeError, "system resolver answer refused");
  • Network accessline 140
    });     await assert.rejects(createPinnedFetch()(`http://localhost:${srv.port}/`), TypeError, "system resolver answer refused");     await assert.rejects(createPinnedFetch()(`http://127.0.0.1:${srv.port}/`), TypeError, "IP literal refused")
  • Network accessline 141
    await assert.rejects(createPinnedFetch()(`http://localhost:${srv.port}/`), TypeError, "system resolver answer refused");
  • Network accessline 142
    await assert.rejects(createPinnedFetch()(`http://127.0.0.1:${srv.port}/`), TypeError, "IP literal refused");     await assert.rejects(createPinnedFetch()(`http://[::1]:${srv.port}/`), TypeError, "IPv6 literal refused");     assert.equal(srv
  • Network accessline 143
    await assert.rejects(createPinnedFetch()(`http://[::1]:${srv.port}/`), TypeError, "IPv6 literal refused");     assert.equal(srv.hits.length, 1, "guarded requests never arrived");     await open.close();
  • Network accessline 154
    try {     const env = (k: string) =>       ({ SUPABASE_URL: `http://127.0.0.1:${srv.port}`, SUPABASE_SERVICE_ROLE_KEY: "srk" } as Record<string, string>)[k];
  • Network accessline 155
    const env = (k: string) =>       ({ SUPABASE_URL: `http://127.0.0.1:${srv.port}`, SUPABASE_SERVICE_ROLE_KEY: "srk" } as Record<string, string>)[k];     const sig = new AbortController().signal;
  • Network accessline 156
    ({ SUPABASE_URL: `http://127.0.0.1:${srv.port}`, SUPABASE_SERVICE_ROLE_KEY: "srk" } as Record<string, string>)[k];

hosted/site-check/report/actions.json

  • Network accessline 19
    },   "kit": {     "consent": { "label": "POPIA Kit consent banner and ConsentGate", "url": "https://github.com/VenusOne-Lee/popia-compliance/blob/main/registry/README.md" },
  • Network accessline 20
    "kit": {     "consent": { "label": "POPIA Kit consent banner and ConsentGate", "url": "https://github.com/VenusOne-Lee/popia-compliance/blob/main/registry/README.md" },     "pack": { "label": "Draft it with the compliance pack: uvx popia-ki
  • Network accessline 21
    "consent": { "label": "POPIA Kit consent banner and ConsentGate", "url": "https://github.com/VenusOne-Lee/popia-compliance/blob/main/registry/README.md" },
  • Network accessline 22
    "pack": { "label": "Draft it with the compliance pack: uvx popia-kit pack --interactive", "url": "https://github.com/VenusOne-Lee/popia-compliance/blob/main/docs/quickstart.md" },
  • Network accessline 23
    "security": { "label": "POPIA Kit security controls guide", "url": "https://github.com/VenusOne-Lee/popia-compliance/blob/main/references/03-security-controls.md" },
  • Network accessline 24
    "operators": { "label": "POPIA Kit guide to operators and cross-border transfers", "url": "https://github.com/VenusOne-Lee/popia-compliance/blob/main/references/06-cross-border-and-operators.md" },
  • Network accessline 25
    "marketing": { "label": "POPIA Kit marketing opt-in component", "url": "https://github.com/VenusOne-Lee/popia-compliance/blob/main/registry/README.md" },
  • Network accessline 26
    "scanner": { "label": "Scan your code: uvx popia-kit scan .", "url": "https://github.com/VenusOne-Lee/popia-compliance/blob/main/docs/quickstart.md" }

hosted/site-check/supabase/functions/site-check/core.ts

  • Executable scriptline 1
    Executable file
  • Network accessline 20
    export const BOT_INFO_URL = "https://github.com/VenusOne-Lee/popia-compliance/blob/main/docs/site-check-bot.md";
  • Network accessline 48
    const DOH_URL = "https://cloudflare-dns.com/dns-query";
  • Network accessline 49
    const TURNSTILE_URL = "https://challenges.cloudflare.com/turnstile/v0/siteverify";
  • Network accessline 502
    const target = await assertSafeTarget(current, ctx);     const sig = withTimeout(ctx.signal); // one budget per hop, covering headers and body     const res = await ctx.fetch(target.href, {
  • Network accessline 503
    const sig = withTimeout(ctx.signal); // one budget per hop, covering headers and body     const res = await ctx.fetch(target.href, {       method: "GET",
  • Network accessline 504
    const res = await ctx.fetch(target.href, {
  • Access to secretsline 759
    [Credential-related source omitted]
  • Network accessline 1209
    ? guardedFetch(`http://${final.hostname}/`, ctx, { follow: false, bodyLimit: () => 0 }) // popia-ignore: SEC-009
  • Access to secretsline 1373
    [Credential-related source omitted]
  • Access to secretsline 1514
    [Credential-related source omitted]

hosted/site-check/supabase/functions/site-check/index.ts

  • Executable scriptline 1
    Executable file
  • Network accessline 15
    Deno.serve(     { onError: () => errorResponse("internal_error", 500, null) },     createHandler({ fetch: (i, init) => fetch(i, init), env: (k) => Deno.env.get(k), rateLimit: supabaseRateLimit }),
  • Network accessline 16
    { onError: () => errorResponse("internal_error", 500, null) },     createHandler({ fetch: (i, init) => fetch(i, init), env: (k) => Deno.env.get(k), rateLimit: supabaseRateLimit }),   );
  • Network accessline 17
    createHandler({ fetch: (i, init) => fetch(i, init), env: (k) => Deno.env.get(k), rateLimit: supabaseRateLimit }),

hosted/site-check/tests/actions.test.ts

  • Executable scriptline 1
    Executable file
  • Network accessline 42
    assert(!/[\u2013\u2014!]/.test(text), "dash or exclamation mark");   assert(!/\b(compliant|certified|approved)\b/i.test(text.replace("not a compliance certificate", "")), "claim");   for (const k of Object.values<{ url: string }>(data.kit))
  • Network accessline 43
    assert(!/\b(compliant|certified|approved)\b/i.test(text.replace("not a compliance certificate", "")), "claim");   for (const k of Object.values<{ url: string }>(data.kit)) assert(k.url.startsWith("https://github.com/VenusOne-Lee/"), k.url);
  • Network accessline 44
    for (const k of Object.values<{ url: string }>(data.kit)) assert(k.url.startsWith("https://github.com/VenusOne-Lee/"), k.url);

hosted/site-check/tests/checks.test.ts

  • Executable scriptline 1
    Executable file
  • Network accessline 22
    const PAGE = `<!doctype html><html><head> <script async src="https://www.googletagmanager.com/gtag/js?id=G-TEST"></script>
  • Network accessline 23
    <script async src="https://www.googletagmanager.com/gtag/js?id=G-TEST"></script>
  • Network accessline 35
    Deno.test("parity test_site_check: findings on an insecure tracker page", async () => {   const m = mockFetch({ "http://shop.example.co.za/": () => new Response(PAGE, { headers: pageHeaders() }) });
  • Network accessline 36
    const m = mockFetch({ "http://shop.example.co.za/": () => new Response(PAGE, { headers: pageHeaders() }) });
  • Network accessline 37
    const res = await runSiteCheck("http://shop.example.co.za/", makeCtx(m.fn, fakeDns(publicDns("shop.example.co.za")).resolve));
  • Network accessline 81
    [Credential-related source omitted]
  • Network accessline 82
    [Credential-related source omitted]
  • Access to secretsline 82
    [Credential-related source omitted]
  • Network accessline 83
    [Credential-related source omitted]
  • Network accessline 84
    [Credential-related source omitted]
  • Network accessline 85
    [Credential-related source omitted]
  • Network accessline 86
    [Credential-related source omitted]
  • Network accessline 90
    [Credential-related source omitted]
  • Network accessline 91
    [Credential-related source omitted]
  • Network accessline 92
    [Credential-related source omitted]
  • Network accessline 99
    assert(ids(res.findings).includes("WEB-012"), "mixed content still reported");   assert(ids(res.findings).includes("WEB-021"), "PI in URL still reported");   assertEquals(res.url, "http://site.example.co.za/profile [query removed]");
  • Network accessline 100
    assert(ids(res.findings).includes("WEB-021"), "PI in URL still reported");   assertEquals(res.url, "http://site.example.co.za/profile [query removed]"); });
  • Network accessline 101
    assertEquals(res.url, "http://site.example.co.za/profile [query removed]");
  • Network accessline 104
    [Credential-related source omitted]
  • Network accessline 105
    [Credential-related source omitted]
  • Network accessline 106
    [Credential-related source omitted]
  • Access to secretsline 111
    [Credential-related source omitted]
  • Access to secretsline 112
    [Credential-related source omitted]
  • Access to secretsline 113
    [Credential-related source omitted]
  • Network accessline 186
    Deno.test("headers: a well configured HTTPS site has no header findings", () => {   assertEquals(checkHeaders({ finalUrl: "https://example.co.za/", headers: hdr(GOOD) }), []);
  • Network accessline 187
    assertEquals(checkHeaders({ finalUrl: "https://example.co.za/", headers: hdr(GOOD) }), []);
  • Network accessline 188
    }); Deno.test("headers: missing headers on HTTPS", () => {   const got = checkHeaders({ finalUrl: "https://example.co.za/", headers: hdr({}) });
  • Network accessline 189
    Deno.test("headers: missing headers on HTTPS", () => {   const got = checkHeaders({ finalUrl: "https://example.co.za/", headers: hdr({}) });   assertEquals(ids(got), ["WEB-003", "WEB-004", "WEB-005", "WEB-006", "WEB-007", "WEB-008"]);
  • Network accessline 190
    const got = checkHeaders({ finalUrl: "https://example.co.za/", headers: hdr({}) });
  • Network accessline 194
    }); Deno.test("headers: HTTP final URL gives WEB-001 and skips HSTS", () => {   const got = checkHeaders({ finalUrl: "http://example.co.za/", headers: hdr(GOOD) });
  • Network accessline 195
    Deno.test("headers: HTTP final URL gives WEB-001 and skips HSTS", () => {   const got = checkHeaders({ finalUrl: "http://example.co.za/", headers: hdr(GOOD) });   assertEquals(ids(got), ["WEB-001"]);
  • Network accessline 196
    const got = checkHeaders({ finalUrl: "http://example.co.za/", headers: hdr(GOOD) });
  • Network accessline 197
    assertEquals(ids(got), ["WEB-001"]);   assertEquals(got[0]!.title, "Site not served over HTTPS");   assertEquals(got[0]!.evidence, "Final URL http://example.co.za/");
  • Network accessline 198
    assertEquals(got[0]!.title, "Site not served over HTTPS");   assertEquals(got[0]!.evidence, "Final URL http://example.co.za/"); });
  • Network accessline 199
    assertEquals(got[0]!.evidence, "Final URL http://example.co.za/");
  • Network accessline 201
    Deno.test("headers: short HSTS, weak CSP, framing and versions", () => {   const short = checkHeaders({     finalUrl: "https://e.co.za/",
  • Network accessline 202
    const short = checkHeaders({     finalUrl: "https://e.co.za/",     headers: hdr({ ...GOOD, "strict-transport-security": "max-age=300" }),
  • Network accessline 203
    finalUrl: "https://e.co.za/",
  • Network accessline 206
    assertEquals(short.map((f) => [f.rule_id, f.title, f.severity]), [["WEB-003", "HSTS max-age is short", "low"]]);   const upper = checkHeaders({     finalUrl: "https://e.co.za/",
  • Network accessline 207
    const upper = checkHeaders({     finalUrl: "https://e.co.za/",     headers: hdr({ ...GOOD, "strict-transport-security": "Max-Age=300" }),
  • Network accessline 208
    finalUrl: "https://e.co.za/",
  • Network accessline 211
    assertEquals(ids(upper), ["WEB-003"]);   const weak = checkHeaders({     finalUrl: "https://e.co.za/",
  • Network accessline 212
    const weak = checkHeaders({     finalUrl: "https://e.co.za/",     headers: hdr({ ...GOOD, "content-security-policy": "script-src 'self' 'unsafe-inline'; frame-ancestors 'self'" }),
  • Network accessline 213
    finalUrl: "https://e.co.za/",
  • Network accessline 216
    assertEquals(weak.map((f) => [f.rule_id, f.title, f.severity]), [["WEB-004", "CSP allows unsafe-inline scripts", "low"]]);   const nonce = checkHeaders({     finalUrl: "https://e.co.za/",
  • Network accessline 217
    const nonce = checkHeaders({     finalUrl: "https://e.co.za/",     headers: hdr({ ...GOOD, "content-security-policy": "script-src 'nonce-abc' 'unsafe-inline'; frame-ancestors 'self'" }),
  • Network accessline 218
    finalUrl: "https://e.co.za/",
  • Network accessline 221
    assertEquals(nonce, []);   const xfo = checkHeaders({     finalUrl: "https://e.co.za/",
  • Network accessline 222
    const xfo = checkHeaders({     finalUrl: "https://e.co.za/",     headers: hdr({ ...GOOD, "content-security-policy": "default-src 'self'", "x-frame-options": "DENY" }),
  • Network accessline 223
    finalUrl: "https://e.co.za/",
  • Network accessline 226
    assertEquals(xfo, []);   const ver = checkHeaders({     finalUrl: "https://e.co.za/",
  • Network accessline 227
    const ver = checkHeaders({     finalUrl: "https://e.co.za/",     headers: hdr({ ...GOOD, server: "nginx/1.25.3", "x-powered-by": "PHP/8.2.1" }),
  • Network accessline 228
    finalUrl: "https://e.co.za/",
  • Network accessline 230
    });   assertEquals(ver.map((f) => f.title), ["Version disclosed in server header", "Version disclosed in x-powered-by header"]);   const nosniff = checkHeaders({ finalUrl: "https://e.co.za/", headers: hdr({ ...GOOD, "x-content-type-options"
  • Network accessline 231
    assertEquals(ver.map((f) => f.title), ["Version disclosed in server header", "Version disclosed in x-powered-by header"]);   const nosniff = checkHeaders({ finalUrl: "https://e.co.za/", headers: hdr({ ...GOOD, "x-content-type-options": "NOS
  • Network accessline 232
    const nosniff = checkHeaders({ finalUrl: "https://e.co.za/", headers: hdr({ ...GOOD, "x-content-type-options": "NOSNIFF" }) });
  • Network accessline 238
    const BASE = "https://example.co.za/";
  • Network accessline 240
    Deno.test("html: trackers from script hosts and inline code", () => {   const r = checkHtml(     `<script src="https://connect.facebook.net/en_US/fbevents.js"></script>
  • Network accessline 241
    const r = checkHtml(     `<script src="https://connect.facebook.net/en_US/fbevents.js"></script>      <script src="//static.hotjar.com/c/hotjar.js"></script>
  • Network accessline 242
    `<script src="https://connect.facebook.net/en_US/fbevents.js"></script>
  • Network accessline 259
    Deno.test("html: a consent tool or Consent Mode default suppresses WEB-013 only", () => {   const tool = checkHtml(     `<script src="https://consent.cookiebot.com/uc.js"></script>
  • Network accessline 260
    const tool = checkHtml(     `<script src="https://consent.cookiebot.com/uc.js"></script>     <script src="https://www.googletagmanager.com/gtag/js?id=G-1"></script><a href="/cookie-policy">Cookies</a>`,
  • Network accessline 261
    `<script src="https://consent.cookiebot.com/uc.js"></script>
  • Network accessline 262
    <script src="https://www.googletagmanager.com/gtag/js?id=G-1"></script><a href="/cookie-policy">Cookies</a>`,
  • Network accessline 272
    }); Deno.test("html: commented-out trackers are ignored", () => {   const r = checkHtml(`<!-- <script src="https://connect.facebook.net/x.js"></script> fbq('init','1') -->`, BASE);
  • Network accessline 273
    Deno.test("html: commented-out trackers are ignored", () => {   const r = checkHtml(`<!-- <script src="https://connect.facebook.net/x.js"></script> fbq('init','1') -->`, BASE);   assertEquals(r.trackers, []);
  • Network accessline 274
    const r = checkHtml(`<!-- <script src="https://connect.facebook.net/x.js"></script> fbq('init','1') -->`, BASE);
  • Network accessline 332
    Deno.test("html: password field on HTTP only", () => {   const form = `<form><input type="password" name="pw"></form>`;   assert(ids(checkHtml(form, "http://example.co.za/").findings).includes("WEB-020"));
  • Network accessline 333
    const form = `<form><input type="password" name="pw"></form>`;   assert(ids(checkHtml(form, "http://example.co.za/").findings).includes("WEB-020"));   assert(!ids(checkHtml(form, BASE).findings).includes("WEB-020"));
  • Network accessline 334
    assert(ids(checkHtml(form, "http://example.co.za/").findings).includes("WEB-020"));
  • Network accessline 337
    Deno.test("html: mixed content and PI in URLs", () => {   const r = checkHtml(     `<script src="http://cdn.example.co.za/a.js"></script><script src="https://ok.example.co.za/b.js?mobile=0821234567"></script>
  • Network accessline 338
    const r = checkHtml(     `<script src="http://cdn.example.co.za/a.js"></script><script src="https://ok.example.co.za/b.js?mobile=0821234567"></script>     <a href="/x?phone=1">a</a><a href="/y?email=a@b.co">b</a>`,
  • Network accessline 339
    `<script src="http://cdn.example.co.za/a.js"></script><script src="https://ok.example.co.za/b.js?mobile=0821234567"></script>
  • Network accessline 378
    Deno.test("safeUrl: credentials, query and fragment never appear", () => {   const cases: Array<[string, string]> = [     ["https://user:pass@example.co.za/a?b=c#d", "https://example.co.za/a [query removed]"],
  • Network accessline 379
    const cases: Array<[string, string]> = [     ["https://user:pass@example.co.za/a?b=c#d", "https://example.co.za/a [query removed]"],     ["https://example.co.za/a#d", "https://example.co.za/a"],
  • Network accessline 380
    ["https://user:pass@example.co.za/a?b=c#d", "https://example.co.za/a [query removed]"],
  • Network accessline 381
    ["https://example.co.za/a#d", "https://example.co.za/a"],
  • Network accessline 382
    ["https://example.co.za/a?", "https://example.co.za/a"],
  • Network accessline 383
    ["http://example.co.za:8080/x", "http://example.co.za:8080/x"],
  • Network accessline 385
    ["//cdn.example.co.za/a.js?t=1", "//cdn.example.co.za/a.js [query removed]"],     ["//u:p@cdn.example.co.za/a.js", "//cdn.example.co.za/a.js"],     ["https://e.co.za/u/person@example.test/x", "https://e.co.za/u/pe***@example.test/x"],
  • Network accessline 386
    ["//u:p@cdn.example.co.za/a.js", "//cdn.example.co.za/a.js"],     ["https://e.co.za/u/person@example.test/x", "https://e.co.za/u/pe***@example.test/x"],     ["https://e.co.za/u/person%40example.test", "https://e.co.za/u/pe***%40example.test
  • Network accessline 387
    ["https://e.co.za/u/person@example.test/x", "https://e.co.za/u/pe***@example.test/x"],
  • Network accessline 388
    ["https://e.co.za/u/person%40example.test", "https://e.co.za/u/pe***%40example.test"],
  • Network accessline 389
    ["https://e.co.za/id/1234567890123", "https://e.co.za/id/*************"],
  • Network accessline 390
    ["https://e.co.za/acct/123456789012", "https://e.co.za/acct/*********"],
  • Network accessline 391
    ["https://e.co.za/t/eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0.sig", "https://e.co.za/t/eyJhbGc…REDACTED"],
  • Network accessline 392
    ["https://e.co.za/k/" + "a".repeat(40), "https://e.co.za/k/…REDACTED"],
  • Network accessline 393
    ];   for (const [inp, out] of cases) assertEquals(safeUrl(inp), out, inp);   assertEquals(safeUrl("https://e.co.za/a/b", false), "https://e.co.za");
  • Network accessline 394
    for (const [inp, out] of cases) assertEquals(safeUrl(inp), out, inp);   assertEquals(safeUrl("https://e.co.za/a/b", false), "https://e.co.za"); });
  • Network accessline 395
    assertEquals(safeUrl("https://e.co.za/a/b", false), "https://e.co.za");
  • Network accessline 401
    Deno.test("parity test_site_check: the user agent names the checker and links its page", () => {   assertEquals(BOT_INFO_URL, "https://github.com/VenusOne-Lee/popia-compliance/blob/main/docs/site-check-bot.md");
  • Network accessline 402
    assertEquals(BOT_INFO_URL, "https://github.com/VenusOne-Lee/popia-compliance/blob/main/docs/site-check-bot.md");

hosted/site-check/tests/guard.test.ts

  • Executable scriptline 1
    Executable file
  • Network accessline 227
    "javascript:alert(1)",     "ws://example.co.za/",     "http://example.co.za:8080/",
  • Network accessline 228
    "ws://example.co.za/",     "http://example.co.za:8080/",     "https://example.co.za:22/",
  • Network accessline 229
    "http://example.co.za:8080/",
  • Network accessline 230
    "https://example.co.za:22/",
  • Network accessline 231
    "http://example.co.za:0/",
  • Network accessline 232
    "https://example.co.za:8443/",
  • Network accessline 233
    "http://localhost/",
  • Network accessline 234
    "http://LOCALHOST./",
  • Network accessline 235
    "http://foo.localhost/",
  • Network accessline 236
    "http://printer.local/",
  • Network accessline 237
    "http://metadata.google.internal/computeMetadata/v1/",
  • Network accessline 238
    "http://router.lan/",
  • Network accessline 239
    "http://nas.home.arpa/",
  • Network accessline 240
    "http://intranet/",
  • Network accessline 241
    "http://127.0.0.1/",
  • Network accessline 242
    "http://127.1/",
  • Network accessline 243
    "http://2130706433/",
  • Network accessline 244
    "http://0x7f.1/",
  • Network accessline 245
    "http://0177.0.0.1/",
  • Network accessline 246
    "http://0x7f000001/",
  • Network accessline 247
    "http://10.0.0.1/",
  • Network accessline 248
    "http://192.168.1.1/",
  • Network accessline 249
    "http://169.254.169.254/latest/meta-data/",
  • Network accessline 250
    "http://[::1]/",
  • Network accessline 251
    "http://[::ffff:127.0.0.1]/",
  • Network accessline 252
    "http://[::ffff:a9fe:a9fe]/",
  • Network accessline 253
    "http://[fd00::1]/",
  • Network accessline 254
    "http://[fe80::1]/",
  • Network accessline 255
    "http://0.0.0.0/",
  • Network accessline 256
    "http://0/",
  • Network accessline 257
    "http://100.64.1.1/",
  • Network accessline 258
    "http://user:pass@127.0.0.1/",
  • Network accessline 259
    "http://example.co.za@127.0.0.1/",
  • Network accessline 271
    Deno.test("url guard: allows public targets and strips credentials and fragment", () => {   const ok: Array<[string, string]> = [     ["https://example.co.za/", "https://example.co.za/"],
  • Network accessline 272
    const ok: Array<[string, string]> = [     ["https://example.co.za/", "https://example.co.za/"],     ["http://example.co.za:80/", "http://example.co.za/"],
  • Network accessline 273
    ["https://example.co.za/", "https://example.co.za/"],
  • Network accessline 274
    ["http://example.co.za:80/", "http://example.co.za/"],
  • Network accessline 275
    ["http://example.co.za:443/", "http://example.co.za:443/"],
  • Network accessline 276
    ["https://example.co.za:443/a", "https://example.co.za/a"],
  • Network accessline 277
    ["https://user:pass@example.co.za/x?q=1#frag", "https://example.co.za/x?q=1"],
  • Network accessline 278
    ["https://EXAMPLE.co.za/", "https://example.co.za/"],
  • Network accessline 279
    ["https://8.8.8.8/", "https://8.8.8.8/"],
  • Network accessline 280
    ["https://[2606:4700:4700::1111]/", "https://[2606:4700:4700::1111]/"],
  • Network accessline 281
    ["https://bücher.de/", "https://xn--bcher-kva.de/"],
  • Network accessline 284
    }); Deno.test("url guard: zone IDs and garbage are invalid", () => {   assertThrowsType(() => validateTargetUrl("http://[fe80::1%25eth0]/"), "invalid_url");
  • Network accessline 285
    Deno.test("url guard: zone IDs and garbage are invalid", () => {   assertThrowsType(() => validateTargetUrl("http://[fe80::1%25eth0]/"), "invalid_url");   assertThrowsType(() => validateTargetUrl("https://exa mple.co.za/"), "invalid_url");
  • Network accessline 286
    assertThrowsType(() => validateTargetUrl("http://[fe80::1%25eth0]/"), "invalid_url");
  • Network accessline 287
    assertThrowsType(() => validateTargetUrl("https://exa mple.co.za/"), "invalid_url");
  • Network accessline 289
    }); Deno.test("input normalisation: adds https and refuses other schemes", () => {   assertEquals(normaliseInputUrl("example.co.za"), "https://example.co.za");
  • Network accessline 290
    Deno.test("input normalisation: adds https and refuses other schemes", () => {   assertEquals(normaliseInputUrl("example.co.za"), "https://example.co.za");   assertEquals(normaliseInputUrl("  HTTP://Example.co.za/x "), "HTTP://Example.co.za
  • Network accessline 291
    assertEquals(normaliseInputUrl("example.co.za"), "https://example.co.za");
  • Network accessline 292
    assertEquals(normaliseInputUrl("  HTTP://Example.co.za/x "), "HTTP://Example.co.za/x");
  • Network accessline 342
    }); Deno.test("dns: DoH resolver sends the right request and parses JSON", async () => {   const doh = "https://cloudflare-dns.com/dns-query?name=example.co.za&type=A";
  • Network accessline 343
    Deno.test("dns: DoH resolver sends the right request and parses JSON", async () => {   const doh = "https://cloudflare-dns.com/dns-query?name=example.co.za&type=A";   const m = mockFetch({
  • Network accessline 344
    const doh = "https://cloudflare-dns.com/dns-query?name=example.co.za&type=A";
  • Network accessline 357
    Deno.test("dns: DoH errors fail closed", async () => {   const name = "a&type=TXT&x=.example.co.za";   const enc = `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(name)}&type=A`;
  • Network accessline 358
    const name = "a&type=TXT&x=.example.co.za";   const enc = `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(name)}&type=A`;   const m1 = mockFetch({ [enc]: () => new Response("x", { status: 500 }) });
  • Network accessline 359
    const enc = `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(name)}&type=A`;
  • Network accessline 365
    }); Deno.test("dns: a full check goes through DoH and refuses a private answer", async () => {   const doh = (n: string, t: string) => `https://cloudflare-dns.com/dns-query?name=${n}&type=${t}`;
  • Network accessline 366
    Deno.test("dns: a full check goes through DoH and refuses a private answer", async () => {   const doh = (n: string, t: string) => `https://cloudflare-dns.com/dns-query?name=${n}&type=${t}`;   const m = mockFetch({
  • Network accessline 367
    const doh = (n: string, t: string) => `https://cloudflare-dns.com/dns-query?name=${n}&type=${t}`;
  • Network accessline 371
    });   const ctx = makeCtx(m.fn, dohResolver(m.fn));   await assertRejectsType(() => runSiteCheck("https://evil.example.co.za/", ctx), "blocked_target");
  • Network accessline 372
    const ctx = makeCtx(m.fn, dohResolver(m.fn));   await assertRejectsType(() => runSiteCheck("https://evil.example.co.za/", ctx), "blocked_target");   assert(m.calls.every((c) => c.url.startsWith("https://cloudflare-dns.com/")), "target never
  • Network accessline 373
    await assertRejectsType(() => runSiteCheck("https://evil.example.co.za/", ctx), "blocked_target");
  • Network accessline 374
    assert(m.calls.every((c) => c.url.startsWith("https://cloudflare-dns.com/")), "target never fetched");
  • Network accessline 381
    Deno.test("redirects: a hop to a private IP literal is refused before it is fetched", async () => {   const m = mockFetch({ "https://example.co.za/": redirect("http://127.0.0.1:80/admin") });
  • Network accessline 382
    const m = mockFetch({ "https://example.co.za/": redirect("http://127.0.0.1:80/admin") });
  • Network accessline 383
    const ctx = makeCtx(m.fn, fakeDns(publicDns("example.co.za")).resolve);   await assertRejectsType(() => guardedFetch("https://example.co.za/", ctx, noBody), "blocked_target");   assertEquals(m.calls.map((c) => c.url), ["https://example.co.z
  • Network accessline 384
    await assertRejectsType(() => guardedFetch("https://example.co.za/", ctx, noBody), "blocked_target");
  • Network accessline 385
    assertEquals(m.calls.map((c) => c.url), ["https://example.co.za/"]);
  • Network accessline 386
    }); Deno.test("redirects: a hop to a name that resolves privately is refused before it is fetched", async () => {   const m = mockFetch({ "https://example.co.za/": redirect("https://internal.example.co.za/") });
  • Network accessline 387
    Deno.test("redirects: a hop to a name that resolves privately is refused before it is fetched", async () => {   const m = mockFetch({ "https://example.co.za/": redirect("https://internal.example.co.za/") });   const dns = fakeDns({ ...publi
  • Network accessline 388
    const m = mockFetch({ "https://example.co.za/": redirect("https://internal.example.co.za/") });
  • Network accessline 389
    const dns = fakeDns({ ...publicDns("example.co.za"), "internal.example.co.za": { A: ["10.1.2.3"] } });   await assertRejectsType(() => guardedFetch("https://example.co.za/", makeCtx(m.fn, dns.resolve), noBody), "blocked_target");   assertEq
  • Network accessline 390
    await assertRejectsType(() => guardedFetch("https://example.co.za/", makeCtx(m.fn, dns.resolve), noBody), "blocked_target");
  • Network accessline 396
    "ftp://example.co.za/",       "file:///etc/passwd",       "https://example.co.za:8443/",
  • Network accessline 397
    "file:///etc/passwd",       "https://example.co.za:8443/",       "http://169.254.169.254/",
  • Network accessline 398
    "https://example.co.za:8443/",
  • Network accessline 399
    "http://169.254.169.254/",
  • Network accessline 400
    "http://[::ffff:169.254.169.254]/",
  • Network accessline 401
    "http://metadata.google.internal/",
  • Network accessline 402
    "http://localhost/",
  • Network accessline 403
    ]   ) {     const m = mockFetch({ "https://example.co.za/": redirect(loc) });
  • Network accessline 404
    ) {     const m = mockFetch({ "https://example.co.za/": redirect(loc) });     const ctx = makeCtx(m.fn, fakeDns(publicDns("example.co.za")).resolve);
  • Network accessline 405
    const m = mockFetch({ "https://example.co.za/": redirect(loc) });
  • Network accessline 406
    const ctx = makeCtx(m.fn, fakeDns(publicDns("example.co.za")).resolve);     await assertRejectsType(() => guardedFetch("https://example.co.za/", ctx, noBody), "blocked_target", loc);     assertEquals(m.calls.length, 1, loc);
  • Network accessline 407
    await assertRejectsType(() => guardedFetch("https://example.co.za/", ctx, noBody), "blocked_target", loc);
  • Network accessline 411
    Deno.test("redirects: five hops are followed, the sixth is refused", async () => {   const routes: Record<string, () => Response> = {};   for (let i = 0; i < 5; i++) routes[`https://example.co.za/${i}`] = redirect(`/${i + 1}`);
  • Network accessline 412
    const routes: Record<string, () => Response> = {};   for (let i = 0; i < 5; i++) routes[`https://example.co.za/${i}`] = redirect(`/${i + 1}`);   routes["https://example.co.za/5"] = html("<p>done</p>");
  • Network accessline 413
    for (let i = 0; i < 5; i++) routes[`https://example.co.za/${i}`] = redirect(`/${i + 1}`);
  • Network accessline 414
    routes["https://example.co.za/5"] = html("<p>done</p>");
  • Network accessline 415
    const m = mockFetch(routes);   const r = await guardedFetch("https://example.co.za/0", makeCtx(m.fn, fakeDns(publicDns("example.co.za")).resolve), noBody);   assertEquals(r.url, "https://example.co.za/5");
  • Network accessline 416
    const r = await guardedFetch("https://example.co.za/0", makeCtx(m.fn, fakeDns(publicDns("example.co.za")).resolve), noBody);
  • Network accessline 417
    assertEquals(r.url, "https://example.co.za/5");
  • Network accessline 420
    routes["https://example.co.za/5"] = redirect("/6");
  • Network accessline 421
    routes["https://example.co.za/6"] = html("<p>too far</p>");
  • Network accessline 422
    const m2 = mockFetch(routes);   await assertRejectsType(     () => guardedFetch("https://example.co.za/0", makeCtx(m2.fn, fakeDns(publicDns("example.co.za")).resolve), noBody),
  • Network accessline 423
    await assertRejectsType(     () => guardedFetch("https://example.co.za/0", makeCtx(m2.fn, fakeDns(publicDns("example.co.za")).resolve), noBody),     "too_many_redirects",
  • Network accessline 424
    () => guardedFetch("https://example.co.za/0", makeCtx(m2.fn, fakeDns(publicDns("example.co.za")).resolve), noBody),
  • Network accessline 429
    Deno.test("redirects: every request is GET with manual redirects, a signal and no credentials", async () => {   const m = mockFetch({     "https://example.co.za/": redirect("https://admin:hunter2@www.example.co.za/next#x", 301),
  • Network accessline 430
    const m = mockFetch({     "https://example.co.za/": redirect("https://admin:hunter2@www.example.co.za/next#x", 301),     "https://www.example.co.za/next": html("<p>ok</p>"),
  • Network accessline 431
    "https://example.co.za/": redirect("https://admin:hunter2@www.example.co.za/next#x", 301),
  • Network accessline 432
    "https://www.example.co.za/next": html("<p>ok</p>"),
  • Network accessline 433
    });   const ctx = makeCtx(m.fn, fakeDns(publicDns("example.co.za", "www.example.co.za")).resolve);   const r = await guardedFetch("https://u:p@example.co.za/", ctx, noBody);
  • Network accessline 434
    const ctx = makeCtx(m.fn, fakeDns(publicDns("example.co.za", "www.example.co.za")).resolve);   const r = await guardedFetch("https://u:p@example.co.za/", ctx, noBody);   assertEquals(r.url, "https://www.example.co.za/next");
  • Network accessline 435
    const r = await guardedFetch("https://u:p@example.co.za/", ctx, noBody);
  • Network accessline 436
    assertEquals(r.url, "https://www.example.co.za/next");
  • Network accessline 445
    Deno.test("probes: http and security.txt probes go through the same guard", async () => {   const m = mockFetch({     "https://example.co.za/": html("<a href='/privacy'>Privacy</a><a href='/paia'>PAIA</a>"),
  • Network accessline 446
    const m = mockFetch({     "https://example.co.za/": html("<a href='/privacy'>Privacy</a><a href='/paia'>PAIA</a>"),     "http://example.co.za/": redirect("http://10.0.0.1/"),
  • Network accessline 447
    "https://example.co.za/": html("<a href='/privacy'>Privacy</a><a href='/paia'>PAIA</a>"),
  • Network accessline 448
    "http://example.co.za/": redirect("http://10.0.0.1/"),
  • Network accessline 449
    "https://example.co.za/.well-known/security.txt": redirect("http://169.254.169.254/latest/meta-data/"),
  • Network accessline 450
    });   const res = await runSiteCheck("https://example.co.za/", makeCtx(m.fn, fakeDns(publicDns("example.co.za")).resolve));   assert(!m.calls.some((c) => c.url.includes("10.0.0.1") || c.url.includes("169.254")), "private hops never fetched"
  • Network accessline 451
    const res = await runSiteCheck("https://example.co.za/", makeCtx(m.fn, fakeDns(publicDns("example.co.za")).resolve));
  • Network accessline 454
    assert(ids.includes("WEB-002"), "http probe that does not redirect to https is flagged");   assert(ids.includes("WEB-018"), "security.txt probe failure reads as missing");   assertEquals(m.calls.find((c) => c.url === "http://example.co.za/"
  • Network accessline 455
    assert(ids.includes("WEB-018"), "security.txt probe failure reads as missing");   assertEquals(m.calls.find((c) => c.url === "http://example.co.za/") !== undefined, true); });
  • Network accessline 456
    assertEquals(m.calls.find((c) => c.url === "http://example.co.za/") !== undefined, true);
  • Network accessline 478
    const counter = { pulled: 0, cancelled: false };   const m = mockFetch({     "https://example.co.za/": () =>
  • Network accessline 479
    const m = mockFetch({     "https://example.co.za/": () =>       new Response(endless(65_536, counter, "<a href='/privacy'>Privacy</a>"), { headers: { "content-type": "text/html" } }),
  • Network accessline 480
    "https://example.co.za/": () =>
  • Network accessline 481
    new Response(endless(65_536, counter, "<a href='/privacy'>Privacy</a>"), { headers: { "content-type": "text/html" } }),   });   const res = await runSiteCheck("https://example.co.za/", makeCtx(m.fn, fakeDns(publicDns("example.co.za")).resol
  • Network accessline 482
    });   const res = await runSiteCheck("https://example.co.za/", makeCtx(m.fn, fakeDns(publicDns("example.co.za")).resolve));   assert(counter.pulled <= LIMITS.maxBodyBytes + 65_536, `pulled ${counter.pulled}`);
  • Network accessline 483
    const res = await runSiteCheck("https://example.co.za/", makeCtx(m.fn, fakeDns(publicDns("example.co.za")).resolve));
  • Network accessline 490
    const counter = { pulled: 0, cancelled: false };   const m = mockFetch({     "https://example.co.za/": () =>
  • Network accessline 491
    const m = mockFetch({     "https://example.co.za/": () =>       new Response(endless(65_536, counter, "<a href='/privacy'>Privacy</a>"), {
  • Network accessline 492
    "https://example.co.za/": () =>
  • Network accessline 495
    }),   });   const res = await runSiteCheck("https://example.co.za/", makeCtx(m.fn, fakeDns(publicDns("example.co.za")).resolve));
  • Network accessline 496
    });   const res = await runSiteCheck("https://example.co.za/", makeCtx(m.fn, fakeDns(publicDns("example.co.za")).resolve));   assert(counter.pulled <= 65_536, `pulled ${counter.pulled}`);
  • Network accessline 497
    const res = await runSiteCheck("https://example.co.za/", makeCtx(m.fn, fakeDns(publicDns("example.co.za")).resolve));
  • Network accessline 525
    let name = "";       try {         await guardedFetch("https://example.co.za/", ctx, noBody);
  • Network accessline 526
    try {         await guardedFetch("https://example.co.za/", ctx, noBody);       } catch (e) {
  • Network accessline 527
    await guardedFetch("https://example.co.za/", ctx, noBody);
  • Network accessline 548
    let name = "";     try {       await guardedFetch("https://example.co.za/", ctx, noBody);
  • Network accessline 549
    try {       await guardedFetch("https://example.co.za/", ctx, noBody);     } catch (e) {
  • Network accessline 550
    await guardedFetch("https://example.co.za/", ctx, noBody);

hosted/site-check/tests/handler.test.ts

  • Executable scriptline 1
    Executable file
  • Network accessline 14
    const ORIGIN = "https://check.venusone.co.za";
  • Network accessline 15
    [Credential-related source omitted]
  • Network accessline 16
    const SITE: Record<string, Route> = {   "https://example.co.za/": html(`<a href="/privacy">Privacy</a><a href="/paia">PAIA</a>`, {     "strict-transport-security": "max-age=63072000",
  • Network accessline 17
    "https://example.co.za/": html(`<a href="/privacy">Privacy</a><a href="/paia">PAIA</a>`, {
  • Network accessline 18
    "strict-transport-security": "max-age=63072000",   }),   "http://example.co.za/": () => new Response(null, { status: 301, headers: { location: "https://example.co.za/" } }),
  • Network accessline 19
    }),   "http://example.co.za/": () => new Response(null, { status: 301, headers: { location: "https://example.co.za/" } }),   "https://example.co.za/.well-known/security.txt": () =>
  • Network accessline 20
    "http://example.co.za/": () => new Response(null, { status: 301, headers: { location: "https://example.co.za/" } }),
  • Network accessline 21
    "https://example.co.za/.well-known/security.txt": () =>
  • Network accessline 31
    function setup(o: Setup = {}) {   const env: Record<string, string> = {     ALLOWED_ORIGINS: `${ORIGIN}, https://www.venusone.co.za`,
  • Network accessline 32
    const env: Record<string, string> = {     ALLOWED_ORIGINS: `${ORIGIN}, https://www.venusone.co.za`,     RATE_LIMIT_SALT: "test-salt",
  • Network accessline 33
    ALLOWED_ORIGINS: `${ORIGIN}, https://www.venusone.co.za`,
  • Network accessline 44
    });   const fetchFn: Deps["fetch"] = (u, init) => {     log.push(u.startsWith("https://challenges.cloudflare.com/") ? "turnstile" : "fetch");
  • Network accessline 45
    const fetchFn: Deps["fetch"] = (u, init) => {     log.push(u.startsWith("https://challenges.cloudflare.com/") ? "turnstile" : "fetch");     return m.fn(u, init);
  • Network accessline 46
    log.push(u.startsWith("https://challenges.cloudflare.com/") ? "turnstile" : "fetch");
  • Network accessline 59
    function post(body: unknown, headers: Record<string, string> = {}) {   return new Request("https://proj.supabase.co/functions/v1/site-check", {
  • Network accessline 60
    return new Request("https://proj.supabase.co/functions/v1/site-check", {
  • Network accessline 88
    Deno.test("cors: missing, unknown and lookalike origins are refused with 403", async () => {   for (const origin of [null, "https://evil.example", "https://check.venusone.co.za.evil.example", "null", ORIGIN + "/"]) {
  • Network accessline 89
    for (const origin of [null, "https://evil.example", "https://check.venusone.co.za.evil.example", "null", ORIGIN + "/"]) {
  • Network accessline 91
    const headers: Record<string, string> = { "content-type": "application/json" };     if (origin) headers["origin"] = origin;     const { res, body } = await call(s, new Request("https://x.supabase.co/f", { method: "POST", headers, body: "{}"
  • Network accessline 92
    if (origin) headers["origin"] = origin;     const { res, body } = await call(s, new Request("https://x.supabase.co/f", { method: "POST", headers, body: "{}" }));     assertEquals(res.status, 403, String(origin));
  • Network accessline 93
    const { res, body } = await call(s, new Request("https://x.supabase.co/f", { method: "POST", headers, body: "{}" }));
  • Network accessline 100
    Deno.test("cors: empty ALLOWED_ORIGINS refuses everything", async () => {   const s = setup({ env: { ALLOWED_ORIGINS: "" } });   const { res } = await call(s, post({ url: "https://example.co.za/" }));
  • Network accessline 101
    const s = setup({ env: { ALLOWED_ORIGINS: "" } });   const { res } = await call(s, post({ url: "https://example.co.za/" }));   assertEquals(res.status, 403);
  • Network accessline 102
    const { res } = await call(s, post({ url: "https://example.co.za/" }));
  • Network accessline 106
    const s = setup();   const ok = await s.handler(     new Request("https://x/f", { method: "OPTIONS", headers: { origin: "https://www.venusone.co.za" } }),
  • Network accessline 107
    const ok = await s.handler(     new Request("https://x/f", { method: "OPTIONS", headers: { origin: "https://www.venusone.co.za" } }),   );
  • Network accessline 108
    new Request("https://x/f", { method: "OPTIONS", headers: { origin: "https://www.venusone.co.za" } }),
  • Network accessline 109
    );   assertEquals(ok.status, 204);   assertEquals(ok.headers.get("access-control-allow-origin"), "https://www.venusone.co.za");
  • Network accessline 110
    assertEquals(ok.status, 204);   assertEquals(ok.headers.get("access-control-allow-origin"), "https://www.venusone.co.za");   assertEquals(ok.headers.get("access-control-allow-methods"), "POST, OPTIONS");
  • Network accessline 111
    assertEquals(ok.headers.get("access-control-allow-origin"), "https://www.venusone.co.za");
  • Network accessline 113
    assert(ok.headers.get("access-control-allow-headers")!.includes("content-type"));   assertEquals(ok.headers.get("vary"), "Origin");   const bad = await s.handler(new Request("https://x/f", { method: "OPTIONS", headers: { origin: "https://ev
  • Network accessline 114
    assertEquals(ok.headers.get("vary"), "Origin");   const bad = await s.handler(new Request("https://x/f", { method: "OPTIONS", headers: { origin: "https://evil.example" } }));   assertEquals(bad.status, 403);
  • Network accessline 115
    const bad = await s.handler(new Request("https://x/f", { method: "OPTIONS", headers: { origin: "https://evil.example" } }));
  • Network accessline 120
    for (const method of ["GET", "PUT", "DELETE"]) {     const s = setup();     const { res, body } = await call(s, new Request("https://x/f", { method, headers: { origin: ORIGIN } }));
  • Network accessline 121
    const s = setup();     const { res, body } = await call(s, new Request("https://x/f", { method, headers: { origin: ORIGIN } }));     assertEquals(res.status, 405);
  • Network accessline 122
    const { res, body } = await call(s, new Request("https://x/f", { method, headers: { origin: ORIGIN } }));
  • Network accessline 127
    Deno.test("body: JSON only, small, with a url", async () => {   const s = setup();   const r1 = await call(s, post({ url: "https://example.co.za/" }, { "content-type": "text/plain" }));
  • Network accessline 128
    const s = setup();   const r1 = await call(s, post({ url: "https://example.co.za/" }, { "content-type": "text/plain" }));   assertEquals(r1.res.status, 400);
  • Network accessline 129
    const r1 = await call(s, post({ url: "https://example.co.za/" }, { "content-type": "text/plain" }));
  • Network accessline 132
    const r2 = await call(s, post("{not json"));   isError(r2.body, "invalid_request");   const r3 = await call(s, post({ url: "https://example.co.za/" + "a".repeat(9_000) }));
  • Network accessline 133
    isError(r2.body, "invalid_request");   const r3 = await call(s, post({ url: "https://example.co.za/" + "a".repeat(9_000) }));   isError(r3.body, "invalid_request");
  • Network accessline 134
    const r3 = await call(s, post({ url: "https://example.co.za/" + "a".repeat(9_000) }));
  • Network accessline 136
    const r4 = await call(s, post({ url: 42 }));   isError(r4.body, "invalid_url");   const r5 = await call(s, post({ url: "https://example.co.za/" + "a".repeat(3_000) }));
  • Network accessline 137
    isError(r4.body, "invalid_url");   const r5 = await call(s, post({ url: "https://example.co.za/" + "a".repeat(3_000) }));   isError(r5.body, "invalid_url");
  • Network accessline 138
    const r5 = await call(s, post({ url: "https://example.co.za/" + "a".repeat(3_000) }));
  • Network accessline 151
    [Credential-related source omitted]
  • Network accessline 152
    [Credential-related source omitted]
  • Access to secretsline 152
    [Credential-related source omitted]
  • Network accessline 153
    const { res, body } = await call(s, post({ url: "https://example.co.za/" }));
  • Network accessline 160
    [Credential-related source omitted]
  • Network accessline 161
    [Credential-related source omitted]
  • Access to secretsline 161
    [Credential-related source omitted]
  • Network accessline 162
    routes: { "https://challenges.cloudflare.com/turnstile/v0/siteverify": turnstile(false, seen) },
  • Network accessline 163
    });   const r1 = await call(bad, post({ url: "https://example.co.za/", turnstileToken: "tok" }));   assertEquals(r1.res.status, 403);
  • Network accessline 164
    const r1 = await call(bad, post({ url: "https://example.co.za/", turnstileToken: "tok" }));
  • Network accessline 170
    [Credential-related source omitted]
  • Network accessline 171
    [Credential-related source omitted]
  • Access to secretsline 171
    [Credential-related source omitted]
  • Network accessline 172
    routes: { "https://challenges.cloudflare.com/turnstile/v0/siteverify": turnstile(true, []) },
  • Network accessline 173
    });   const r2 = await call(good, post({ url: "https://example.co.za/", turnstileToken: "tok" }));   assertEquals(r2.res.status, 200);
  • Network accessline 174
    const r2 = await call(good, post({ url: "https://example.co.za/", turnstileToken: "tok" }));   assertEquals(r2.res.status, 200);   assertEquals(good.log.slice(0, 4), ["turnstile", "ratelimit", "ratelimit", "fetch"], "per-IP then global, the
  • Network accessline 179
    [Credential-related source omitted]
  • Network accessline 180
    [Credential-related source omitted]
  • Access to secretsline 180
    [Credential-related source omitted]
  • Network accessline 181
    routes: { "https://challenges.cloudflare.com/turnstile/v0/siteverify": () => new Response("oops", { status: 500 }) },
  • Network accessline 182
    });   const { res } = await call(s, post({ url: "https://example.co.za/", turnstileToken: "tok" }));   assertEquals(res.status, 403);
  • Network accessline 183
    const { res } = await call(s, post({ url: "https://example.co.za/", turnstileToken: "tok" }));
  • Network accessline 186
    Deno.test("turnstile: not required when no secret is set", async () => {   const s = setup();   const { res } = await call(s, post({ url: "https://example.co.za/" }));
  • Network accessline 187
    const s = setup();   const { res } = await call(s, post({ url: "https://example.co.za/" }));   assertEquals(res.status, 200);
  • Network accessline 188
    const { res } = await call(s, post({ url: "https://example.co.za/" }));
  • Network accessline 202
    for (const c of cases) {     const s = setup(c);     const { res, body } = await call(s, post({ url: "https://example.co.za/" }));
  • Network accessline 203
    const s = setup(c);     const { res, body } = await call(s, post({ url: "https://example.co.za/" }));     assertEquals(res.status, 429);
  • Network accessline 204
    const { res, body } = await call(s, post({ url: "https://example.co.za/" }));
  • Network accessline 210
    Deno.test("rate limit: salted hash of the client IP, default and custom limits", async () => {   const s = setup();   await call(s, post({ url: "https://example.co.za/" }));
  • Network accessline 211
    const s = setup();   await call(s, post({ url: "https://example.co.za/" }));   assertEquals(s.hits[0]!.limit, 10);
  • Network accessline 212
    await call(s, post({ url: "https://example.co.za/" }));
  • Network accessline 216
    assert(!s.hits[0]!.hash.includes("41.1.2.3"));   const s2 = setup({ env: { RATE_LIMIT_PER_HOUR: "3" } });   await call(s2, post({ url: "https://example.co.za/" }));
  • Network accessline 217
    const s2 = setup({ env: { RATE_LIMIT_PER_HOUR: "3" } });   await call(s2, post({ url: "https://example.co.za/" }));   assertEquals(s2.hits[0]!.limit, 3);
  • Network accessline 218
    await call(s2, post({ url: "https://example.co.za/" }));
  • Network accessline 219
    assertEquals(s2.hits[0]!.limit, 3);   const s3 = setup({ env: { RATE_LIMIT_PER_HOUR: "-5" } });   await call(s3, post({ url: "https://example.co.za/" }));
  • Network accessline 220
    const s3 = setup({ env: { RATE_LIMIT_PER_HOUR: "-5" } });   await call(s3, post({ url: "https://example.co.za/" }));   assertEquals(s3.hits[0]!.limit, 10);
  • Network accessline 221
    await call(s3, post({ url: "https://example.co.za/" }));
  • Network accessline 225
    Deno.test("rate limit: rate limiting happens before URL validation", async () => {   const s = setup();   await call(s, post({ url: "http://127.0.0.1/" }));
  • Network accessline 226
    const s = setup();   await call(s, post({ url: "http://127.0.0.1/" }));   assertEquals(s.hits.length, 2, "per-IP and global checks both run before URL validation");
  • Network accessline 227
    await call(s, post({ url: "http://127.0.0.1/" }));
  • Network accessline 251
    try {       const env = (k: string) =>         ({ SUPABASE_URL: "https://proj.supabase.co", SUPABASE_SERVICE_ROLE_KEY: "srk" } as Record<string, string>)[k];
  • Network accessline 252
    const env = (k: string) =>         ({ SUPABASE_URL: "https://proj.supabase.co", SUPABASE_SERVICE_ROLE_KEY: "srk" } as Record<string, string>)[k];       const sig = new AbortController().signal;
  • Network accessline 253
    ({ SUPABASE_URL: "https://proj.supabase.co", SUPABASE_SERVICE_ROLE_KEY: "srk" } as Record<string, string>)[k];
  • Network accessline 254
    const sig = new AbortController().signal;       assertEquals(await supabaseRateLimit("a".repeat(64), 7, sig, env), true);       assertEquals(seen[0]!.url, "https://proj.supabase.co/rest/v1/rpc/site_check_hit");
  • Network accessline 255
    assertEquals(await supabaseRateLimit("a".repeat(64), 7, sig, env), true);       assertEquals(seen[0]!.url, "https://proj.supabase.co/rest/v1/rpc/site_check_hit");       assertEquals(JSON.parse(seen[0]!.body), { p_ip_hash: "a".repeat(64), p_
  • Network accessline 256
    assertEquals(seen[0]!.url, "https://proj.supabase.co/rest/v1/rpc/site_check_hit");
  • Network accessline 286
    [Credential-related source omitted]
  • Network accessline 287
    [Credential-related source omitted]
  • Network accessline 288
    [Credential-related source omitted]
  • Network accessline 289
    `http://169.254.169.254/latest/meta-data/?k=${SECRET}`,
  • Network accessline 290
    `http://localhost/${SECRET}`,
  • Network accessline 291
    `ftp://example.co.za/${SECRET}`,       `http://example.co.za:8080/${SECRET}`,     ]
  • Network accessline 292
    `http://example.co.za:8080/${SECRET}`,
  • Network accessline 304
    [Credential-related source omitted]
  • Network accessline 305
    [Credential-related source omitted]
  • Network accessline 306
    [Credential-related source omitted]
  • Network accessline 307
    [Credential-related source omitted]
  • Network accessline 309
    [Credential-related source omitted]
  • Network accessline 310
    [Credential-related source omitted]
  • Network accessline 311
    [Credential-related source omitted]
  • Network accessline 316
    Deno.test("errors: DNS failure and redirect loops", async () => {   const s = setup();   const r1 = await call(s, post({ url: "https://unknown.example.co.za/" }));
  • Network accessline 317
    const s = setup();   const r1 = await call(s, post({ url: "https://unknown.example.co.za/" }));   assertEquals(r1.res.status, 502);
  • Network accessline 318
    const r1 = await call(s, post({ url: "https://unknown.example.co.za/" }));
  • Network accessline 320
    isError(r1.body, "dns_failed");   const s2 = setup({     routes: { "https://example.co.za/": () => new Response(null, { status: 302, headers: { location: "/" } }) },
  • Network accessline 321
    const s2 = setup({     routes: { "https://example.co.za/": () => new Response(null, { status: 302, headers: { location: "/" } }) },   });
  • Network accessline 322
    routes: { "https://example.co.za/": () => new Response(null, { status: 302, headers: { location: "/" } }) },
  • Network accessline 323
    });   const r2 = await call(s2, post({ url: "https://example.co.za/" }));   isError(r2.body, "too_many_redirects");
  • Network accessline 324
    const r2 = await call(s2, post({ url: "https://example.co.za/" }));
  • Network accessline 334
    ];   for (const [route, type] of cases) {     const s = setup({ routes: { "https://example.co.za/": route } });
  • Network accessline 335
    for (const [route, type] of cases) {     const s = setup({ routes: { "https://example.co.za/": route } });     const { res, body } = await call(s, post({ url: "https://example.co.za/" }));
  • Network accessline 336
    const s = setup({ routes: { "https://example.co.za/": route } });
  • Network accessline 337
    const { res, body } = await call(s, post({ url: "https://example.co.za/" }));
  • Network accessline 342
    Deno.test("success: response shape, headers and a clean site", async () => {   const s = setup();   const { res, body } = await call(s, post({ url: "https://user:pw@example.co.za/#x" }));
  • Network accessline 343
    const s = setup();   const { res, body } = await call(s, post({ url: "https://user:pw@example.co.za/#x" }));   assertEquals(res.status, 200);
  • Network accessline 344
    const { res, body } = await call(s, post({ url: "https://user:pw@example.co.za/#x" }));
  • Network accessline 348
    assertEquals(res.headers.get("content-type"), "application/json; charset=utf-8");   assertEquals(Object.keys(body), ["url", "final_url", "checked_at", "summary", "findings", "cookies", "notices", "disclaimer"]);   assertEquals(body["url"], 
  • Network accessline 349
    assertEquals(Object.keys(body), ["url", "final_url", "checked_at", "summary", "findings", "cookies", "notices", "disclaimer"]);   assertEquals(body["url"], "https://example.co.za/");   assertEquals(body["checked_at"], "2026-10-18T09:00:00+0
  • Network accessline 350
    assertEquals(body["url"], "https://example.co.za/");
  • Network accessline 361
    const shell = `<html><body><div id="root"></div></body></html>`;   const routes: Record<string, Route> = {     "https://example.co.za/": html(shell),
  • Network accessline 362
    const routes: Record<string, Route> = {     "https://example.co.za/": html(shell),     "https://example.co.za/privacy": html(shell), // SPA fallback: same body, not a notice
  • Network accessline 363
    "https://example.co.za/": html(shell),
  • Network accessline 364
    "https://example.co.za/privacy": html(shell), // SPA fallback: same body, not a notice
  • Network accessline 365
    "https://example.co.za/privacy-policy": html("<h1>Privacy notice</h1>"),
  • Network accessline 366
    "https://example.co.za/paia-manual": () => new Response("%PDF-1.7", { headers: { "content-type": "application/pdf" } }),
  • Network accessline 367
    };   const off = setup({ routes });   const r1 = await call(off, post({ url: "https://example.co.za/" }));
  • Network accessline 368
    const off = setup({ routes });   const r1 = await call(off, post({ url: "https://example.co.za/" }));   assertEquals(r1.body["notices"], { privacy: false, paia: false, cookie: false, information_officer: false });
  • Network accessline 369
    const r1 = await call(off, post({ url: "https://example.co.za/" }));
  • Network accessline 371
    assert(!off.calls.some((c) => c.url.endsWith("/privacy")));   const on = setup({ routes, env: { PROBE_NOTICE_PATHS: "true" } });   const r2 = await call(on, post({ url: "https://example.co.za/" }));
  • Network accessline 372
    const on = setup({ routes, env: { PROBE_NOTICE_PATHS: "true" } });   const r2 = await call(on, post({ url: "https://example.co.za/" }));   assertEquals(r2.body["notices"], { privacy: true, paia: true, cookie: false, information_officer: fal
  • Network accessline 373
    const r2 = await call(on, post({ url: "https://example.co.za/" }));
  • Network accessline 379
    Deno.test("copy: no dashes, exclamation marks or compliance claims in user-facing strings", async () => {   const strings: string[] = [...Object.values(ERROR_MESSAGES), DISCLAIMER];   const bad = `<script src="http://www.googletagmanager.co
  • Network accessline 380
    const strings: string[] = [...Object.values(ERROR_MESSAGES), DISCLAIMER];   const bad = `<script src="http://www.googletagmanager.com/gtag/js"></script><script>fbq('init','1')</script>     <form><input type="password"><input type="checkbox"
  • Network accessline 381
    const bad = `<script src="http://www.googletagmanager.com/gtag/js"></script><script>fbq('init','1')</script>
  • Network accessline 388
    h.append("set-cookie", "_ga=1");   h.append("set-cookie", "sessionid=1");   const s = setup({ routes: { "http://example.co.za/": () => new Response(bad, { headers: h }) } });
  • Network accessline 389
    h.append("set-cookie", "sessionid=1");   const s = setup({ routes: { "http://example.co.za/": () => new Response(bad, { headers: h }) } });   const { body } = await call(s, post({ url: "http://example.co.za/" }));
  • Network accessline 390
    const s = setup({ routes: { "http://example.co.za/": () => new Response(bad, { headers: h }) } });
  • Network accessline 391
    const { body } = await call(s, post({ url: "http://example.co.za/" }));

hosted/site-check/tests/hardening.test.ts

  • Executable scriptline 1
    Executable file
  • Network accessline 34
    const ORIGIN = "https://check.venusone.co.za";
  • Network accessline 38
    const env: Record<string, string> = { ALLOWED_ORIGINS: ORIGIN, RATE_LIMIT_SALT: SALT, ...o.env };   const m = mockFetch({     "https://example.co.za/": html("<p>ok</p>"),
  • Network accessline 39
    const m = mockFetch({     "https://example.co.za/": html("<p>ok</p>"),   });
  • Network accessline 40
    "https://example.co.za/": html("<p>ok</p>"),
  • Network accessline 56
    function post(headers: Record<string, string> = {}, body: BodyInit = JSON.stringify({ url: "https://example.co.za/" })) {
  • Network accessline 57
    return new Request("https://proj.supabase.co/functions/v1/site-check", {
  • Network accessline 72
    const noEnv = () => undefined; const reqWith = (h: Record<string, string>) => new Request("https://x/f", { headers: h });
  • Network accessline 73
    const reqWith = (h: Record<string, string>) => new Request("https://x/f", { headers: h });
  • Network accessline 85
    h.append("x-forwarded-for", "6.6.6.6");   h.append("x-forwarded-for", "41.0.0.3");   assertEquals(clientIp(new Request("https://x/f", { headers: h }), noEnv), "41.0.0.3", "repeated header lines");
  • Network accessline 86
    h.append("x-forwarded-for", "41.0.0.3");   assertEquals(clientIp(new Request("https://x/f", { headers: h }), noEnv), "41.0.0.3", "repeated header lines");   assertEquals(clientIp(reqWith({}), noEnv), null);
  • Network accessline 87
    assertEquals(clientIp(new Request("https://x/f", { headers: h }), noEnv), "41.0.0.3", "repeated header lines");
  • Network accessline 182
    Deno.test("redos: safeUrl bounds long paths and never leaves part of a token at the cut", () => {   let t = performance.now();   const out = safeUrl("https://e.co.za/" + "a".repeat(1_000_000));
  • Network accessline 183
    let t = performance.now();   const out = safeUrl("https://e.co.za/" + "a".repeat(1_000_000));   fast(performance.now() - t, 200, "1 MB path");
  • Network accessline 184
    const out = safeUrl("https://e.co.za/" + "a".repeat(1_000_000));
  • Network accessline 186
    assertEquals(out, "https://e.co.za/…");
  • Network accessline 188
    const path = "/" + "p/".repeat(245) + "Q".repeat(40) + "/tail";   t = performance.now();   const cut = safeUrl("https://e.co.za" + path);
  • Network accessline 189
    t = performance.now();   const cut = safeUrl("https://e.co.za" + path);   fast(performance.now() - t, 50, "cut path");
  • Network accessline 190
    const cut = safeUrl("https://e.co.za" + path);
  • Network accessline 192
    assert(!cut.includes("QQQQ"), cut.slice(-40));   assert(cut.endsWith("/…"), cut.slice(-10));   assert(cut.length <= "https://e.co.za".length + LIMITS.maxReportPath + 1);
  • Network accessline 193
    assert(cut.endsWith("/…"), cut.slice(-10));   assert(cut.length <= "https://e.co.za".length + LIMITS.maxReportPath + 1);   const mail = safeUrl("https://e.co.za/" + "x/".repeat(250) + "person@example.test/" + "y".repeat(600));
  • Network accessline 194
    assert(cut.length <= "https://e.co.za".length + LIMITS.maxReportPath + 1);
  • Network accessline 195
    const mail = safeUrl("https://e.co.za/" + "x/".repeat(250) + "person@example.test/" + "y".repeat(600));
  • Network accessline 196
    assert(!mail.includes("person"), "email near the cut is still masked");   assertEquals(safeUrl("https://e.co.za/k/" + "a".repeat(40)), "https://e.co.za/k/…REDACTED", "short paths unchanged"); });
  • Network accessline 197
    assertEquals(safeUrl("https://e.co.za/k/" + "a".repeat(40)), "https://e.co.za/k/…REDACTED", "short paths unchanged");
  • Network accessline 199
    Deno.test("redos: one huge http script src, or thousands of them, stays fast", () => {   let t = performance.now();   const one = checkHtml(`<script src="http://cdn.example.co.za/${"a".repeat(1_400_000)}"></script>`, "https://e.co.za/");
  • Network accessline 200
    let t = performance.now();   const one = checkHtml(`<script src="http://cdn.example.co.za/${"a".repeat(1_400_000)}"></script>`, "https://e.co.za/");   fast(performance.now() - t, 1_000, "1.4 MB src");
  • Network accessline 201
    const one = checkHtml(`<script src="http://cdn.example.co.za/${"a".repeat(1_400_000)}"></script>`, "https://e.co.za/");
  • Network accessline 204
    assertEquals(w12.length, 1);   assert(w12[0]!.evidence.length <= LIMITS.maxEvidence);   const many = `<script src="http://cdn.example.co.za/${"b".repeat(400)}"></script>`.repeat(3_000);
  • Network accessline 205
    assert(w12[0]!.evidence.length <= LIMITS.maxEvidence);   const many = `<script src="http://cdn.example.co.za/${"b".repeat(400)}"></script>`.repeat(3_000);   t = performance.now();
  • Network accessline 206
    const many = `<script src="http://cdn.example.co.za/${"b".repeat(400)}"></script>`.repeat(3_000);
  • Network accessline 207
    t = performance.now();   const r = checkHtml(many, "https://e.co.za/");   fast(performance.now() - t, 1_500, "3000 http scripts");
  • Network accessline 208
    const r = checkHtml(many, "https://e.co.za/");
  • Network accessline 213
    const t = performance.now();   checkHeaders({     finalUrl: "https://e.co.za/",
  • Network accessline 214
    checkHeaders({     finalUrl: "https://e.co.za/",     headers: new Headers({ server: "1".repeat(400_000), "x-powered-by": "9".repeat(400_000) }),
  • Network accessline 215
    finalUrl: "https://e.co.za/",
  • Network accessline 217
    });   fast(performance.now() - t, 100, "400 KB digit headers");   const v = checkHeaders({ finalUrl: "https://e.co.za/", headers: new Headers({ server: "nginx/1.25.3" }) });
  • Network accessline 218
    fast(performance.now() - t, 100, "400 KB digit headers");   const v = checkHeaders({ finalUrl: "https://e.co.za/", headers: new Headers({ server: "nginx/1.25.3" }) });   assert(v.some((f) => f.rule_id === "WEB-009" && f.evidence === "nginx/
  • Network accessline 219
    const v = checkHeaders({ finalUrl: "https://e.co.za/", headers: new Headers({ server: "nginx/1.25.3" }) });
  • Network accessline 223
    const long = "/" + "c".repeat(300_000);   const m = mockFetch({     "https://example.co.za/": () => new Response(null, { status: 302, headers: { location: long } }),
  • Network accessline 224
    const m = mockFetch({     "https://example.co.za/": () => new Response(null, { status: 302, headers: { location: long } }),     ["https://example.co.za" + long]: html("<p>ok</p>"),
  • Network accessline 225
    "https://example.co.za/": () => new Response(null, { status: 302, headers: { location: long } }),
  • Network accessline 226
    ["https://example.co.za" + long]: html("<p>ok</p>"),
  • Network accessline 236
    fast(performance.now() - t, 1_000, "long final URL");   const body = await res.json();   assertEquals(body.final_url, "https://example.co.za/…");
  • Network accessline 237
    const body = await res.json();   assertEquals(body.final_url, "https://example.co.za/…"); });
  • Network accessline 238
    assertEquals(body.final_url, "https://example.co.za/…");
  • Network accessline 301
    let name = "";       try {         await guardedFetch("https://example.co.za/", ctx, { follow: true, bodyLimit: () => LIMITS.maxBodyBytes });
  • Network accessline 302
    try {         await guardedFetch("https://example.co.za/", ctx, { follow: true, bodyLimit: () => LIMITS.maxBodyBytes });       } catch (e) {
  • Network accessline 303
    await guardedFetch("https://example.co.za/", ctx, { follow: true, bodyLimit: () => LIMITS.maxBodyBytes });
  • Network accessline 366
    for (     const u of [       "http://127%2e0%2e0%2e1/",
  • Network accessline 367
    const u of [       "http://127%2e0%2e0%2e1/",       "http://127.0.0.1./",
  • Network accessline 368
    "http://127%2e0%2e0%2e1/",
  • Network accessline 369
    "http://127.0.0.1./",
  • Network accessline 370
    "http://017700000001/",
  • Network accessline 371
    "http://0x7f.0x0.0x0.0x1/",
  • Network accessline 372
    "http://0.0.0.0./",
  • Network accessline 373
    "http://[::]/",
  • Network accessline 374
    "http://[::127.0.0.1]/",
  • Network accessline 375
    "http://[0:0:0:0:0:ffff:127.0.0.1]/",
  • Network accessline 376
    "http://[64:ff9b::169.254.169.254]/",
  • Network accessline 377
    "http://[64:ff9b::a00:1]/",
  • Network accessline 378
    "http://[2002:a9fe:a9fe::1]/",
  • Network accessline 379
    "http://[2002:7f00:1::]/",
  • Network accessline 380
    "http://[2001:0:4136:e378:8000:63bf:3fff:fdd2]/",
  • Network accessline 381
    "http://[fec0::1]/",
  • Network accessline 382
    "http://[fd00:ec2::254]/",
  • Network accessline 383
    "http://127.0.0.1/",
  • Network accessline 384
    "http://127。0。0。1/",
  • Network accessline 385
    "http://ⓛⓞⓒⓐⓛⓗⓞⓢⓣ/",
  • Network accessline 386
    "http://localhost%2e/",
  • Network accessline 387
    "http://example.co.za\n@127.0.0.1/",
  • Network accessline 388
    "https:////127.0.0.1/",
  • Network accessline 389
    "http://168.63.129.16/",
  • Network accessline 390
    "http://100.100.100.200/",
  • Network accessline 391
    "http://metadata.google.internal./",
  • Network accessline 436
    Deno.test("workers: every outbound fetch uses redirect manual, so a full check passes on Workers", async () => {   const modes: string[] = [];   const doh = (n: string, t: string) => `https://cloudflare-dns.com/dns-query?name=${n}&type=${t}
  • Network accessline 437
    const modes: string[] = [];   const doh = (n: string, t: string) => `https://cloudflare-dns.com/dns-query?name=${n}&type=${t}`;   const json = (v: unknown) => () => new Response(JSON.stringify(v), { headers: { "content-type": "application/j
  • Network accessline 438
    const doh = (n: string, t: string) => `https://cloudflare-dns.com/dns-query?name=${n}&type=${t}`;
  • Network accessline 441
    [doh("example.co.za", "A")]: json({ Status: 0, Answer: [{ type: 1, data: "203.0.114.1" }] }),     [doh("example.co.za", "AAAA")]: json({ Status: 0 }),     "https://challenges.cloudflare.com/turnstile/v0/siteverify": json({ success: true }),
  • Network accessline 442
    [doh("example.co.za", "AAAA")]: json({ Status: 0 }),     "https://challenges.cloudflare.com/turnstile/v0/siteverify": json({ success: true }),     "https://example.co.za/": () =>
  • Network accessline 443
    "https://challenges.cloudflare.com/turnstile/v0/siteverify": json({ success: true }),
  • Network accessline 444
    "https://example.co.za/": () =>
  • Network accessline 457
    [Credential-related source omitted]
  • Network accessline 458
    [Credential-related source omitted]
  • Network accessline 459
    [Credential-related source omitted]
  • Network accessline 463
    });   const res = await handler(     new Request("https://site.example.co.za/api/site-check", {
  • Network accessline 464
    const res = await handler(     new Request("https://site.example.co.za/api/site-check", {       method: "POST",
  • Network accessline 465
    new Request("https://site.example.co.za/api/site-check", {
  • Network accessline 466
    method: "POST",       headers: { origin: "https://site.example.co.za", "content-type": "application/json" },       body: JSON.stringify({ url: "example.co.za", turnstileToken: "tok" }),
  • Network accessline 467
    headers: { origin: "https://site.example.co.za", "content-type": "application/json" },

hosted/site-check/tests/helpers.ts

  • Executable scriptline 1
    Executable file

hosted/site-check/tests/sql/run_sql_tests.sh

  • Executable scriptline 1
    #!/usr/bin/env bash
  • Dangerous shell operationline 14
    cleanup() { "${RUNAS[@]}" "$PGBIN/pg_ctl" -D "$WORK/data" -m immediate stop >/dev/null 2>&1 || true; rm -rf "$WORK"; }

i18n/README.md

  • Network accessline 55
    1. Fork https://github.com/VenusOne-Lee/popia-compliance and create a branch, for example `i18n-zu-review`.

mcp/__init__.py

  • Executable scriptline 1
    Executable file

mcp/server.py

  • Executable scriptline 1
    #!/usr/bin/env python3
  • Access to secretsline 257
    [Credential-related source omitted]
  • Access to secretsline 372
    [Credential-related source omitted]
  • Access to secretsline 373
    [Credential-related source omitted]
  • Access to secretsline 378
    [Credential-related source omitted]
  • Access to secretsline 474
    [Credential-related source omitted]

mcp/smoke_test.py

  • Executable scriptline 1
    #!/usr/bin/env python3

package.json

  • Executable scriptline 1
    Executable file
  • Network accessline 5
    "license": "MIT",   "author": "Venus ARC (Pty) Ltd, trading as Venus One",   "homepage": "https://github.com/VenusOne-Lee/popia-compliance",
  • Network accessline 6
    "author": "Venus ARC (Pty) Ltd, trading as Venus One",   "homepage": "https://github.com/VenusOne-Lee/popia-compliance",   "repository": {"type": "git", "url": "git+https://github.com/VenusOne-Lee/popia-compliance.git"},
  • Network accessline 7
    "homepage": "https://github.com/VenusOne-Lee/popia-compliance",
  • Network accessline 8
    "repository": {"type": "git", "url": "git+https://github.com/VenusOne-Lee/popia-compliance.git"},

packs/README.md

  • Network accessline 52
    1. **Open an issue first** at https://github.com/VenusOne-Lee/popia-compliance/issues with the sector, the main laws and who will maintain the pack.

pyproject.toml

  • Network accessline 34
    [project.urls] Homepage = "https://popiakit.co.za"
  • Network accessline 35
    Homepage = "https://popiakit.co.za"
  • Network accessline 36
    "Site check" = "https://popiakit.co.za/check"
  • Network accessline 37
    Repository = "https://github.com/VenusOne-Lee/popia-compliance"
  • Network accessline 38
    Documentation = "https://github.com/VenusOne-Lee/popia-compliance/blob/main/docs/quickstart.md"
  • Network accessline 39
    Changelog = "https://github.com/VenusOne-Lee/popia-compliance/blob/main/CHANGELOG.md"
  • Network accessline 40
    Security = "https://github.com/VenusOne-Lee/popia-compliance/blob/main/SECURITY.md"
  • Network accessline 41
    Issues = "https://github.com/VenusOne-Lee/popia-compliance/issues"

references/02-build-patterns.md

  • Network accessline 129
    function loadScript(src) { const s = document.createElement('script'); s.src = src; s.async = true; document.head.appendChild(s); }   function applyConsent(c) {     if (c?.analytics) loadScript('https://plausible.io/js/script.js');         
  • Network accessline 130
    function applyConsent(c) {     if (c?.analytics) loadScript('https://plausible.io/js/script.js');            // or GA4 with consent mode     if (c?.marketing) { /* load Meta pixel, LinkedIn Insight, TikTok here, never before */ }
  • Network accessline 131
    if (c?.analytics) loadScript('https://plausible.io/js/script.js');            // or GA4 with consent mode

references/04-stack-playbooks.md

  • Access to secretsline 26
    [Credential-related source omitted]
  • Access to secretsline 148
    [Credential-related source omitted]

references/07-breach-response.md

  • Access to secretsline 65
    [Credential-related source omitted]

references/09-audit-procedure.md

  • Network accessline 62
    python3 scripts/popia_scan.py ./repo --format json --out evidence/popia-scan.json # live site (passive) python3 scripts/site_check.py https://example.co.za --out evidence/site-check.md --json evidence/site-check.json
  • Network accessline 63
    # live site (passive) python3 scripts/site_check.py https://example.co.za --out evidence/site-check.md --json evidence/site-check.json # extra tools if available
  • Network accessline 64
    python3 scripts/site_check.py https://example.co.za --out evidence/site-check.md --json evidence/site-check.json
  • Network accessline 196
    Severity:   Critical Title:      audit_leads table readable by anonymous users Evidence:   migration 20260712_create_leads.sql:4 creates table without RLS; curl with anon key returned 1,204 rows (redacted sample in evidence/F-07.txt)
  • Network accessline 197
    Title:      audit_leads table readable by anonymous users Evidence:   migration 20260712_create_leads.sql:4 creates table without RLS; curl with anon key returned 1,204 rows (redacted sample in evidence/F-07.txt) Risk:       Any visitor can
  • Network accessline 198
    Evidence:   migration 20260712_create_leads.sql:4 creates table without RLS; curl with anon key returned 1,204 rows (redacted sample in evidence/F-07.txt)

registry/README.md

  • Network accessline 41
    Base URL: `https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r`
  • Network accessline 44
    # Consent system (provider, banner, gate, record helpers) npx shadcn@latest add https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-system.json
  • Network accessline 45
    npx shadcn@latest add https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-system.json
  • Network accessline 47
    # Forms npx shadcn@latest add https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/marketing-opt-in.json
  • Network accessline 48
    npx shadcn@latest add https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/marketing-opt-in.json
  • Network accessline 49
    npx shadcn@latest add https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/collection-notice.json
  • Network accessline 50
    npx shadcn@latest add https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/sa-id-input.json
  • Network accessline 52
    # Libraries npx shadcn@latest add https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/redact.json
  • Network accessline 53
    npx shadcn@latest add https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/redact.json
  • Network accessline 54
    npx shadcn@latest add https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/sa-id.json
  • Network accessline 56
    # Account npx shadcn@latest add https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/privacy-centre.json
  • Network accessline 57
    npx shadcn@latest add https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/privacy-centre.json
  • Network accessline 59
    # Supabase migrations and edge functions npx shadcn@latest add https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/supabase-privacy-schema.json
  • Network accessline 60
    npx shadcn@latest add https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/supabase-privacy-schema.json
  • Network accessline 61
    npx shadcn@latest add https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/supabase-privacy-functions.json
  • Network accessline 116
    <App />   <GoogleConsentModeSync />   <ConsentGate category="analytics" src="https://plausible.io/js/script.js" scriptOptions={{ attributes: { "data-domain": "example.co.za" } }} />
  • Network accessline 117
    <GoogleConsentModeSync />   <ConsentGate category="analytics" src="https://plausible.io/js/script.js" scriptOptions={{ attributes: { "data-domain": "example.co.za" } }} />   <ConsentBanner companyName="Example (Pty) Ltd" privacyNoticeUrl="/
  • Network accessline 118
    <ConsentGate category="analytics" src="https://plausible.io/js/script.js" scriptOptions={{ attributes: { "data-domain": "example.co.za" } }} />
  • Access to secretsline 156
    [Credential-related source omitted]
  • Access to secretsline 157
    [Credential-related source omitted]
  • Network accessline 183
    Add POPIA (South Africa) privacy features to this app using the open-source registry at https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/
  • Network accessline 184
    https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/
  • Network accessline 249
    supabase secrets set \      ALLOWED_ORIGINS="https://example.co.za,https://www.example.co.za" \
  • Network accessline 250
    ALLOWED_ORIGINS="https://example.co.za,https://www.example.co.za" \

registry/build.py

  • Executable scriptline 1
    #!/usr/bin/env python3
  • Network accessline 23
    SOURCE = HERE / "registry.json" OUT = HERE / "r" ITEM_SCHEMA = "https://ui.shadcn.com/schema/registry-item.json"
  • Network accessline 24
    OUT = HERE / "r" ITEM_SCHEMA = "https://ui.shadcn.com/schema/registry-item.json" REGISTRY_SCHEMA = "https://ui.shadcn.com/schema/registry.json"
  • Network accessline 25
    ITEM_SCHEMA = "https://ui.shadcn.com/schema/registry-item.json"
  • Network accessline 26
    REGISTRY_SCHEMA = "https://ui.shadcn.com/schema/registry.json"

registry/popia/account/privacy-centre.tsx

  • Executable scriptline 1
    Executable file
  • Access to secretsline 51
    [Credential-related source omitted]
  • Network accessline 94
    objectionFormUrl,   privacyContact,   regulatorComplaintUrl = "https://inforegulator.org.za/", // VERIFY current complaints page
  • Network accessline 95
    privacyContact,   regulatorComplaintUrl = "https://inforegulator.org.za/", // VERIFY current complaints page   retainedDataNote = "We keep some records after you delete your account where the law requires it, for example invoices for tax pu
  • Network accessline 96
    regulatorComplaintUrl = "https://inforegulator.org.za/", // VERIFY current complaints page
  • Network accessline 108
    [Credential-related source omitted]
  • Network accessline 109
    if (!token) throw new Error("You need to be signed in.");       const res = await fetch(url(path), {         ...init,
  • Network accessline 110
    const res = await fetch(url(path), {

registry/popia/consent/consent-banner.tsx

  • Executable scriptline 1
    Executable file

registry/popia/consent/consent-gate.tsx

  • Executable scriptline 1
    Executable file
  • Network accessline 11
    *  * Usage:  *   <ConsentGate category="marketing" src="https://connect.facebook.net/en_US/fbevents.js" />
  • Network accessline 12
    * Usage:  *   <ConsentGate category="marketing" src="https://connect.facebook.net/en_US/fbevents.js" />  *   <ConsentGate category="analytics" fallback={<p>Enable analytics cookies to see this.</p>}>
  • Network accessline 13
    *   <ConsentGate category="marketing" src="https://connect.facebook.net/en_US/fbevents.js" />

registry/popia/consent/consent-provider.tsx

  • Executable scriptline 1
    Executable file

registry/popia/forms/collection-notice.tsx

  • Executable scriptline 1
    Executable file

registry/popia/forms/marketing-opt-in.tsx

  • Executable scriptline 1
    Executable file

registry/popia/forms/sa-id-input.tsx

  • Executable scriptline 1
    Executable file

registry/popia/lib/consent-record.ts

  • Executable scriptline 1
    Executable file

registry/popia/lib/redact.ts

  • Executable scriptline 1
    Executable file
  • Access to secretsline 87
    [Credential-related source omitted]

registry/popia/lib/sa-id.ts

  • Executable scriptline 1
    Executable file

registry/popia/supabase/functions/_shared/privacy-config.ts

  • Executable scriptline 1
    Executable file
  • Access to secretsline 87
    [Credential-related source omitted]
  • Possible obfuscationline 295
    const bin = atob(b64);

registry/popia/supabase/functions/consent/index.ts

  • Executable scriptline 1
    Executable file

registry/popia/supabase/functions/dsar-delete/index.ts

  • Executable scriptline 1
    Executable file

registry/popia/supabase/functions/dsar-export/index.ts

  • Executable scriptline 1
    Executable file

registry/popia/supabase/functions/unsubscribe/index.ts

  • Executable scriptline 1
    Executable file
  • Access to secretsline 65
    [Credential-related source omitted]

registry/r/collection-notice.json

  • Network accessline 1
    {   "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 2
    "$schema": "https://ui.shadcn.com/schema/registry-item.json",

registry/r/consent-banner.json

  • Network accessline 1
    {   "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 2
    "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 11
    "label",     "switch",     "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 12
    "switch",     "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"   ],
  • Network accessline 13
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"

registry/r/consent-gate.json

  • Network accessline 1
    {   "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 2
    "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 7
    "dependencies": [],   "registryDependencies": [     "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 8
    "registryDependencies": [     "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"   ],
  • Network accessline 9
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 12
    {       "path": "popia/consent/consent-gate.tsx",       "content": "\"use client\";\n\n/**\n * ConsentGate, loadScript and Google Consent Mode v2 helpers.\n *\n * Nothing in the analytics or marketing category may load before the person\n *
  • Network accessline 13
    "path": "popia/consent/consent-gate.tsx",       "content": "\"use client\";\n\n/**\n * ConsentGate, loadScript and Google Consent Mode v2 helpers.\n *\n * Nothing in the analytics or marketing category may load before the person\n * opts in
  • Network accessline 14
    "content": "\"use client\";\n\n/**\n * ConsentGate, loadScript and Google Consent Mode v2 helpers.\n *\n * Nothing in the analytics or marketing category may load before the person\n * opts in. That covers pixels, tag managers, session repl

registry/r/consent-provider.json

  • Network accessline 1
    {   "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 2
    "$schema": "https://ui.shadcn.com/schema/registry-item.json",

registry/r/consent-record.json

  • Network accessline 1
    {   "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 2
    "$schema": "https://ui.shadcn.com/schema/registry-item.json",

registry/r/consent-system.json

  • Network accessline 1
    {   "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 2
    "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 27
    {       "path": "popia/consent/consent-gate.tsx",       "content": "\"use client\";\n\n/**\n * ConsentGate, loadScript and Google Consent Mode v2 helpers.\n *\n * Nothing in the analytics or marketing category may load before the person\n *
  • Network accessline 28
    "path": "popia/consent/consent-gate.tsx",       "content": "\"use client\";\n\n/**\n * ConsentGate, loadScript and Google Consent Mode v2 helpers.\n *\n * Nothing in the analytics or marketing category may load before the person\n * opts in
  • Network accessline 29
    "content": "\"use client\";\n\n/**\n * ConsentGate, loadScript and Google Consent Mode v2 helpers.\n *\n * Nothing in the analytics or marketing category may load before the person\n * opts in. That covers pixels, tag managers, session repl

registry/r/marketing-opt-in.json

  • Network accessline 1
    {   "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 2
    "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 9
    "checkbox",     "label",     "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-record.json"
  • Network accessline 10
    "label",     "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-record.json"   ],
  • Network accessline 11
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-record.json"

registry/r/privacy-centre.json

  • Network accessline 1
    {   "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 2
    "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 12
    "label",     "switch",     "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 13
    "switch",     "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"   ],
  • Network accessline 14
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 17
    {       "path": "popia/account/privacy-centre.tsx",       "content": "\"use client\";\n\n/**\n * Privacy centre for signed-in users.\n *\n * Builds the data subject rights into the product (references/02-build-patterns.md, section 10):\n * 
  • Network accessline 18
    "path": "popia/account/privacy-centre.tsx",       "content": "\"use client\";\n\n/**\n * Privacy centre for signed-in users.\n *\n * Builds the data subject rights into the product (references/02-build-patterns.md, section 10):\n *   - Down
  • Network accessline 19
    [Credential-related source omitted]
  • Access to secretsline 19
    [Credential-related source omitted]

registry/r/redact.json

  • Network accessline 1
    {   "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 2
    "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Access to secretsline 12
    [Credential-related source omitted]

registry/r/registry.json

  • Network accessline 1
    {   "$schema": "https://ui.shadcn.com/schema/registry.json",
  • Network accessline 2
    "$schema": "https://ui.shadcn.com/schema/registry.json",
  • Network accessline 3
    "name": "popia",   "homepage": "https://github.com/VenusOne-Lee/popia-compliance",   "items": [
  • Network accessline 4
    "homepage": "https://github.com/VenusOne-Lee/popia-compliance",
  • Network accessline 30
    "label",         "switch",         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 31
    "switch",         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"       ],
  • Network accessline 32
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 47
    "dependencies": [],       "registryDependencies": [         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 48
    "registryDependencies": [         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"       ],
  • Network accessline 49
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 146
    "checkbox",         "label",         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-record.json"
  • Network accessline 147
    "label",         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-record.json"       ],
  • Network accessline 148
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-record.json"
  • Network accessline 180
    "input",         "label",         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/sa-id.json"
  • Network accessline 181
    "label",         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/sa-id.json"       ],
  • Network accessline 182
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/sa-id.json"
  • Network accessline 202
    "label",         "switch",         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 203
    "switch",         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"       ],
  • Network accessline 204
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 249
    "dependencies": [],       "registryDependencies": [         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/supabase-privacy-schema.json"
  • Network accessline 250
    "registryDependencies": [         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/supabase-privacy-schema.json"       ],
  • Network accessline 251
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/supabase-privacy-schema.json"

registry/r/sa-id-input.json

  • Network accessline 1
    {   "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 2
    "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 9
    "input",     "label",     "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/sa-id.json"
  • Network accessline 10
    "label",     "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/sa-id.json"   ],
  • Network accessline 11
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/sa-id.json"

registry/r/sa-id.json

  • Network accessline 1
    {   "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 2
    "$schema": "https://ui.shadcn.com/schema/registry-item.json",

registry/r/supabase-privacy-functions.json

  • Network accessline 1
    {   "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 2
    "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 7
    "dependencies": [],   "registryDependencies": [     "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/supabase-privacy-schema.json"
  • Network accessline 8
    "registryDependencies": [     "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/supabase-privacy-schema.json"   ],
  • Network accessline 9
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/supabase-privacy-schema.json"
  • Access to secretsline 14
    [Credential-related source omitted]
  • Access to secretsline 38
    [Credential-related source omitted]

registry/r/supabase-privacy-schema.json

  • Network accessline 1
    {   "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  • Network accessline 2
    "$schema": "https://ui.shadcn.com/schema/registry-item.json",

registry/registry.json

  • Network accessline 1
    {   "$schema": "https://ui.shadcn.com/schema/registry.json",
  • Network accessline 2
    "$schema": "https://ui.shadcn.com/schema/registry.json",
  • Network accessline 3
    "name": "popia",   "homepage": "https://github.com/VenusOne-Lee/popia-compliance",   "items": [
  • Network accessline 4
    "homepage": "https://github.com/VenusOne-Lee/popia-compliance",
  • Network accessline 30
    "label",         "switch",         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 31
    "switch",         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"       ],
  • Network accessline 32
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 47
    "dependencies": [],       "registryDependencies": [         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 48
    "registryDependencies": [         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"       ],
  • Network accessline 49
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 146
    "checkbox",         "label",         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-record.json"
  • Network accessline 147
    "label",         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-record.json"       ],
  • Network accessline 148
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-record.json"
  • Network accessline 180
    "input",         "label",         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/sa-id.json"
  • Network accessline 181
    "label",         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/sa-id.json"       ],
  • Network accessline 182
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/sa-id.json"
  • Network accessline 202
    "label",         "switch",         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 203
    "switch",         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"       ],
  • Network accessline 204
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/consent-provider.json"
  • Network accessline 249
    "dependencies": [],       "registryDependencies": [         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/supabase-privacy-schema.json"
  • Network accessline 250
    "registryDependencies": [         "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/supabase-privacy-schema.json"       ],
  • Network accessline 251
    "https://raw.githubusercontent.com/VenusOne-Lee/popia-compliance/main/registry/r/supabase-privacy-schema.json"

regwatch/sources.json

  • Network accessline 1
    {   "user_agent": "popia-compliance-regwatch/1.0 (+https://github.com/VenusOne-Lee/popia-compliance; monthly check)",
  • Network accessline 2
    "user_agent": "popia-compliance-regwatch/1.0 (+https://github.com/VenusOne-Lee/popia-compliance; monthly check)",
  • Network accessline 7
    "id": "media-statements",       "name": "Information Regulator media statements",       "url": "https://inforegulator.org.za/media-statements/",
  • Network accessline 8
    "name": "Information Regulator media statements",       "url": "https://inforegulator.org.za/media-statements/",       "enabled": true
  • Network accessline 9
    "url": "https://inforegulator.org.za/media-statements/",
  • Network accessline 13
    "id": "enforcement-notices",       "name": "Information Regulator enforcement notices",       "url": "https://inforegulator.org.za/enforcement-notices/",
  • Network accessline 14
    "name": "Information Regulator enforcement notices",       "url": "https://inforegulator.org.za/enforcement-notices/",       "enabled": true
  • Network accessline 15
    "url": "https://inforegulator.org.za/enforcement-notices/",
  • Network accessline 19
    "id": "guidance-notes",       "name": "Information Regulator guidance notes",       "url": "https://inforegulator.org.za/guidance-notes/",
  • Network accessline 20
    "name": "Information Regulator guidance notes",       "url": "https://inforegulator.org.za/guidance-notes/",       "enabled": true
  • Network accessline 21
    "url": "https://inforegulator.org.za/guidance-notes/",

regwatch/state.json

  • Network accessline 6
    "media-statements": {       "seen": [         "https://inforegulator.org.za/wp-content/uploads/2026/09/Final-MEDIA-INVITE-INFORMATION-REGULATOR-HOSTS-THE-INTERNATIONAL-DAY-FOR-UNIVERSAL-ACCESS-TO-INFORMATION-IDUAI-SESSION-IN-TSHWANE.pdf",
  • Network accessline 7
    "seen": [         "https://inforegulator.org.za/wp-content/uploads/2026/09/Final-MEDIA-INVITE-INFORMATION-REGULATOR-HOSTS-THE-INTERNATIONAL-DAY-FOR-UNIVERSAL-ACCESS-TO-INFORMATION-IDUAI-SESSION-IN-TSHWANE.pdf",         "https://inforegulato
  • Network accessline 8
    "https://inforegulator.org.za/wp-content/uploads/2026/09/Final-MEDIA-INVITE-INFORMATION-REGULATOR-HOSTS-THE-INTERNATIONAL-DAY-FOR-UNIVERSAL-ACCESS-TO-INFORMATION-IDUAI-SESSION-IN-TSHWANE.pdf",
  • Network accessline 9
    "https://inforegulator.org.za/wp-content/uploads/2026/08/MEDIA-INVITE-INFORMATION-REGULATOR-TO-HOST-MEDIA-BRIEFING-ON-HIGH-PROFILE-CASES-ENFORCEMENTS-AND-INVESTIGATIONS-INTO-POPIA-AND-PAIA-CONTRAVENTIONS.pdf",
  • Network accessline 10
    "https://inforegulator.org.za/wp-content/uploads/2026/06/MEDIA-STATEMENT-INFORMATION-REGULATOR-ISSUES-ENFORCEMENT-NOTICES-FOR-THE-CONTRAVENTIONS-OF-POPIA-AND-PAIA-BY-PUBLIC-AND-PRIVATE-BODIES.pdf",
  • Network accessline 11
    "https://inforegulator.org.za/wp-content/uploads/2026/06/MEDIA-INVITE-INFORMATION-REGULATOR-HOSTS-PROVINCIAL-ROADSHOW-IN-EASTERN-CAPE-TO-PROMOTE-POPIA-AND-PAIA.pdf",
  • Network accessline 12
    "https://inforegulator.org.za/wp-content/uploads/2026/05/MEDIA-INVITE-INFORMATION-REGULATOR-HOSTS-PROVINCIAL-ROADSHOW-IN-EASTERN-CAPE-TO-PROMOTE-POPIA-AND-PAIA.pdf",
  • Network accessline 13
    "https://inforegulator.org.za/wp-content/uploads/2026/05/Media-Alert-invitation-for-public-comment-on-POPIA-code-of-conduct-for-Gated-Access.pdf",
  • Network accessline 14
    "https://inforegulator.org.za/wp-content/uploads/2026/04/INFORMATION-REGULATOR-TURNS-TO-THE-COURTS-TO-FINE-BLOUBERG-LOCAL-MUNICIPALITY-FOR-CONTRAVENTION-OF-POPIA_.pdf",
  • Network accessline 15
    "https://inforegulator.org.za/wp-content/uploads/2026/04/MEDIA-STATEMENT-REGULATOR-NOTES-PUBLISHED-GAZETTE-OF-THE-CONSUMER-PROTECT-ACT-AMENDMENT-REGULATIONS-2026.pdf",
  • Network accessline 16
    "https://inforegulator.org.za/wp-content/uploads/2026/02/APP-Stakeholder-Consultation-Session-Media-Invitation-final.pdf",
  • Network accessline 17
    "https://inforegulator.org.za/wp-content/uploads/2026/02/MEDIA-ADVISORY_PRETORIA-CONFERENCE-06022026.pdf",
  • Network accessline 18
    "https://inforegulator.org.za/wp-content/uploads/2026/02/MEDIA-INVITE-IDPD-2026.pdf",
  • Network accessline 19
    "https://inforegulator.org.za/wp-content/uploads/2026/01/MEDIA-STATEMENT-THE-REGULATOR-APPEALS-THE-JUDGEMENT-ON-PUBLICATION-OF-MATRIC-RESULTS-31-DEC-2025.pdf",
  • Network accessline 20
    "https://inforegulator.org.za/wp-content/uploads/2026/01/MEDIA-STATEMENT-THE-REGULATOR-TO-STUDY-THE-COURT-JUDGEMENT-ON-MATRIC-RESULTS-December-2025.pdf",
  • Network accessline 21
    "https://inforegulator.org.za/wp-content/uploads/2025/11/MEDIA-STATEMENT-INFORMATION-REGULATOR-WELCOMES-THE-FOCUS-ON-DATA-PROTECTION-and-TRANSPARENCY-COMMITMENTS-IN-THE-G20.pdf",
  • Network accessline 22
    "https://inforegulator.org.za/wp-content/uploads/2025/11/Media-Briefing-Invite-Updates-on-POPIA-and-PAIA-matters-nz.pdf",
  • Network accessline 23
    "https://inforegulator.org.za/wp-content/uploads/2025/10/Media-Statement-COURT-RESERVES-JUDGEMENT-ON-THE-MATTER-OF-THE-PUBLICATION-OF-MATRIC-RESULTS.pdf",
  • Network accessline 24
    "https://inforegulator.org.za/wp-content/uploads/2025/10/Media-Statement-IDUAI-2025-Final.pdf",
  • Network accessline 25
    "https://inforegulator.org.za/wp-content/uploads/2025/09/Gov-Tech-Digital-Public-Service-Awards-Media-Statement-2025-final.pdf",
  • Network accessline 26
    "https://inforegulator.org.za/wp-content/uploads/2025/07/Final-Media-Invite-Dikopano-NW-2025-pdf.pdf",
  • Network accessline 27
    "https://inforegulator.org.za/wp-content/uploads/2025/05/MEDIA-STATEMENT-RELOCATION-OF-INFORMATION-REGULATORS-OFFICES-final.pdf",
  • Network accessline 28
    "https://inforegulator.org.za/wp-content/uploads/2025/04/MEDIA-STATEMENT-INVITATION-TO-REPORT-SECURITY-COMPROMISES-THROUGH-THE-eSERVICES-PORTAL-.pdf",
  • Network accessline 29
    "https://inforegulator.org.za/wp-content/uploads/2025/03/MEDIA-ALERT-SECTION-32-AND-83-4-PAIA-REPORTS.pdf",
  • Network accessline 30
    "https://inforegulator.org.za/wp-content/uploads/2025/03/NOTICE-FOR-PAIA-ANNUAL-REPORT-2025.pdf",
  • Network accessline 31
    "https://inforegulator.org.za/wp-content/uploads/2025/03/Media-Statement-IR-Welcomes-DOJ-CD-Minister-Decision-on-Sex-Offender-Register-Publishing-04-March-2025.pdf",
  • Network accessline 32
    "https://inforegulator.org.za/wp-content/uploads/2025/03/Media-Invite-stakeholder-Engagement-Session-05-March-2025.pdf",
  • Network accessline 33
    "https://inforegulator.org.za/wp-content/uploads/2025/01/MEDIA-INVITE-IDPD-2025.pdf",
  • Network accessline 34
    "https://inforegulator.org.za/wp-content/uploads/2025/01/MEDIA-STATEMENT_-THE-SECURITY-COMPROMISE-AT-DBE-Final-1.pdf",
  • Network accessline 35
    "https://inforegulator.org.za/wp-content/uploads/2025/01/FINAL-MEDIA-STATEMENT_INFORMATION-REGULATOR-and-DBE-MATRIC-RESULTS-COURT.pdf",
  • Network accessline 36
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-INVITE-IDPD-2024-2.pdf",
  • Network accessline 37
    "https://inforegulator.org.za/wp-content/uploads/2020/07/FINAL-MEDIA-STATEMENT-IDPD-2024.pdf",
  • Network accessline 38
    "https://inforegulator.org.za/wp-content/uploads/2020/07/FINAL-MEDIA-STATEMENT_INFORMATION-REGULATOR-WINS-BCX-INNOVATION-AWARD.pdf",
  • Network accessline 39
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-STATEMENT_INFORMATION-REGULATORS-LATEST-UPDATE-2.pdf",
  • Network accessline 40
    "https://inforegulator.org.za/wp-content/uploads/2020/07/DIKOPANO-NORTHERN-CAPE-MEDIA-INVITE.pdf",
  • Network accessline 41
    "https://inforegulator.org.za/wp-content/uploads/2020/07/IDUAI-Media-Statement-final.pdf",
  • Network accessline 42
    "https://inforegulator.org.za/wp-content/uploads/2020/07/Media-Invite-for-Media-Briefing-PAIA-high-profile-cases-POPIA-progress.pdf",
  • Network accessline 43
    "https://inforegulator.org.za/wp-content/uploads/2020/07/FINAL-DIKOPANO-FREE-STATE-MEDIA-INVITE.pdf",
  • Network accessline 44
    "https://inforegulator.org.za/wp-content/uploads/2020/07/FINAL-IR-and-ESWATINI-COMMUNICATIONS-COMMISSION-MOU-MEDIA-STATEMENT.pdf",
  • Network accessline 45
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ANIC-2024-MEDIA-STATEMENT-final.pdf",
  • Network accessline 46
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-STATEMENT-ON-GUIDANCE-NOTE-ON-POLITICAL-PARTIES-AND-INDEPENDENT-CANDIDATES-FINAL-1.pdf",
  • Network accessline 47
    "https://inforegulator.org.za/wp-content/uploads/2020/07/Final-Media-Statement-PAIA-Annual-Report-Submission.pdf",
  • Network accessline 48
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-INVITE-Regulator_-IEC-_-MMA-multisectoral-dialogue.pdf",
  • Network accessline 49
    "https://inforegulator.org.za/wp-content/uploads/2020/07/UPDATED_MEDIA-INVITE-INFORMATION-REGULATOR-SHARES-OUTCOMES-ON-INVESTIGATIONS-ASSESSMENTS.pdf",
  • Network accessline 50
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-INVITE-INFORMATION-REGULATOR-SHARES-OUTCOMES-ON-INVESTIGATIONS-ASSESSMENTS.pdf",
  • Network accessline 51
    "https://inforegulator.org.za/wp-content/uploads/2020/07/Media-Statement-on-the-IEC-Security-Compromise.pdf",
  • Network accessline 52
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-STATEMENT-ENFORCEMENT-NOTICE-ON-DIRECT-MARKETING-COMPLAINT.pdf",
  • Network accessline 53
    "https://inforegulator.org.za/wp-content/uploads/2024/02/Public Notice Regarding Fraudulent activity.pdf",
  • Network accessline 54
    "https://inforegulator.org.za/wp-content/uploads/2025/01/Media-Statement-Infringement-Notice-DBE.pdf",
  • Network accessline 55
    "https://inforegulator.org.za/wp-content/uploads/2020/07/Statement-on-privacy-and-democratic-rights.pdf",
  • Network accessline 56
    "https://inforegulator.org.za/wp-content/uploads/2020/07/FINAL-MEDIA-INVITE-FOR-THE-EASTERN-CAPE-DIKOPANO-and-STAKEHOLDER-ENGAGEMENT.pdf",
  • Network accessline 57
    "https://inforegulator.org.za/wp-content/uploads/2020/07/FINAL-MEDIA-ADVISORY_-IDUAI-2023.pdf",
  • Network accessline 58
    "https://inforegulator.org.za/wp-content/uploads/2020/07/FINAL-MEDIA-STATEMENT-ENFORCEMENT-NOTICE-ISSUED-TO-DISCHEM-PHARMACIES-LTD.pdf",
  • Network accessline 59
    "https://inforegulator.org.za/wp-content/uploads/2020/07/FINAL-MEDIA-ADVISORY_-MPUMALANGA-COMMUNITY-ENGAGEMENT18.pdf",
  • Network accessline 60
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-STATEMENT-INFRINGEMENT-NOTICE-ISSUED-TO-THE-DEPARTMENT-OF-JUSTICE-AND-CONSTITUTIONAL.pdf",
  • Network accessline 61
    "https://inforegulator.org.za/wp-content/uploads/2020/07/Extension-notice-for-submission-of-Annual-Report-to-the-Regulator-in-terms-of-section-32-and-section.pdf",
  • Network accessline 62
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-STATEMENT-THE-INFORMATION-REGULATOR-ELECTED-AS-EXECUTIVE-MEMBER-OF-A-GLOBAL-BODY.pdf",
  • Network accessline 63
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-STATEMENT-THE-REGULATOR-WELCOMES-THE-JUDGMENT-OF-THE-CONSTITUTIONAL-COURT-IN-FAVOUR-OF-TRANSPARENCY_.pdf",
  • Network accessline 64
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-STATEMENT-REGULATOR-CONDUCTS-PAIA-COMPLIANCE-ASSESSMENT-BY-POLITICAL-PARTIES.pdf",         "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-STATEMENT-ENFORCEMENT-N
  • Network accessline 65
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-STATEMENT-ENFORCEMENT-NOTICE-ISSUED-TO-THE-DEPARTMENT-OF-JUSTICE-AND-CONSTITUTIONAL-DEVELOPMENT-DUE-TO-CONTRAVENTION.pdf",         "https://inforegulator.org.za/wp-content/uploa
  • Network accessline 66
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-STATEMENT-INVITATION-TO-PUBLIC-and-PRIVATE-BODIES-TO-SUBMIT-ANNUAL-REPORTS-FOR-ACCESS-TO-INFO-REQUESTS-RECIEVED.pdf",
  • Network accessline 67
    "https://inforegulator.org.za/wp-content/uploads/2020/07/pdf-Final-Version_MEDIA-BRIEFING-OF-INFORMATION-REGULATOR-ON-OUTCOMES-OF-RECEIVED-COMPLAINTS-003.pdf",
  • Network accessline 68
    "https://inforegulator.org.za/wp-content/uploads/2020/07/Media-Statement_Information-Regulator-Refers-NDOH-to-the-Enforcement-Committee.pdf",
  • Network accessline 69
    "https://inforegulator.org.za/wp-content/uploads/2020/07/Members-of-Parliament-demand-action-from-National-Treasury-on-independence-of-the-Information-Regulator_Approved.pdf",
  • Network accessline 70
    "https://inforegulator.org.za/wp-content/uploads/2020/07/Final-MEDIA-ADVISORY-INTERNATIONAL-DATA-PRIVACY-DAY-2023.pdf",
  • Network accessline 71
    "https://inforegulator.org.za/wp-content/uploads/2022/11/MEDIA%20STATEMENT-%20THE%20REGULATOR%20COMMENDS%20THE%20DEPARTMENT%20OF%20BASIC%20EDUCATION%20FOR%20COMPLIANCE%20WITH%20POPIA%20AND%20OBTAINING%20CONSENT_.pdf",
  • Network accessline 72
    "https://inforegulator.org.za/wp-content/uploads/2022/11/Media%20Statement_Information%20Regulator's%20Enforcement%20Committee_Zooms%20in%20on%20Municipalities_v2.pdf",
  • Network accessline 73
    "https://inforegulator.org.za/wp-content/uploads/2022/11/Media%20Invite%20_%20AFRICA%20DIDGITAL%20RIGHTS%20HUB.pdf",
  • Network accessline 74
    "https://inforegulator.org.za/wp-content/uploads/2022/10/Media%20Statement%20Regulator%20Approves%20Codes%20of%20Conduct%20for%20the%20Banking%20and%20Credit%20Reporting%20Sectors.pdf",
  • Network accessline 75
    "https://inforegulator.org.za/wp-content/uploads/2022/10/MEDIA%20INVITE_%20KZN%20ROADSHOW%20Final.pdf",
  • Network accessline 76
    "https://inforegulator.org.za/wp-content/uploads/2020/07/Media-Invite-IDUAI-28-September-2022.pdf",
  • Network accessline 77
    "https://inforegulator.org.za/wp-content/uploads/2020/07/Final-MEDIA-ALERT-_EXTENSION-FOR-SUBMISSION-OF-ANNUAL-REPORTS-IN-TERMS-OF-SECTION-32-of-PAIA-FOR-PUBLIC-BODIES.pdf",
  • Network accessline 78
    "https://inforegulator.org.za/wp-content/uploads/2020/07/A-REMINDER-FOR-PUBLIC-BODIES-TO-SUBMIT-THEIR-ANNUAL-REPORT-IN-TERMS-OF-SECTION-32-OF-PROMOTION-OF-ACCESS-TO-INFORMATION-ACT-2-OF-2000-PAIA.pdf",
  • Network accessline 79
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-STATEMENT_-REGULATOR-ISSUES-A-SUMMONS-AGAINST-SOUTH-AFRICAN-POLICE-SERVICE-.pdf",
  • Network accessline 80
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-STATEMENT-PUBLIC-HEARING-ON-ACCESS-TO-INFORMATION-ON-LICENSE-FEES-AND-COPYRIGHT-ROYALTIES-IN-THE-MUSIC-INDUSTRY.pdf",
  • Network accessline 81
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-STATEMENT-THE-REGULATOR-INITIATES-AN-ASSESSMENT-INTO-POSSIBLE-VIOLATIONS-OF-POPIA-FOLLOWING-ALLEGATIONS-TO-PERSONAL-INFORMATION-LEAKED-BY-SAPS-0.pdf",
  • Network accessline 82
    "https://inforegulator.org.za/wp-content/uploads/2020/07/Media-Statement_Information-Regulator-Establishes-Enforcement-Committee.pdf",
  • Network accessline 83
    "https://inforegulator.org.za/wp-content/uploads/2020/07/Final-MEDIA-STATEMENT-INFORMATION-REGULATOR-MONITORS-THE-DEPARTMENT-OF-HEALTHS-POPIA-COMPLIANCE-22-APRIL-2022.pdf",
  • Network accessline 84
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-STATEMENT-ANIC-14-APRIL-2022.pdf",
  • Network accessline 85
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-STATEMENT-THE-REGULATOR-IS-DISSATISFIED-WITH-TRANSUNIONS-RESPONSE-AND-IT-INITIATES-AN-ASSESSMENT_FINAL.pdf",
  • Network accessline 86
    "https://inforegulator.org.za/wp-content/uploads/2020/07/Human-Rights-Month-Webinar-Media-Statement-final.pdf",
  • Network accessline 87
    "https://inforegulator.org.za/wp-content/uploads/2020/07/MEDIA-STATEMENT-The-Regulator-instructs-TransUnion-to-report-in-greater-detail-regarding-their-security-compromise.pdf",
  • Network accessline 88
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ms-20220124-IDUAI-Webinar2021.pdf.pdf",
  • Network accessline 89
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ms-20220912-PublishingMatricResults.pdf",
  • Network accessline 90
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ms-20211202-appoints.pdf",
  • Network accessline 91
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ms-20211027-Experian.pdf",
  • Network accessline 92
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ms-20211014-ElectionCampaigning-POPIA.pdf",
  • Network accessline 93
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ms-20210923-IDUAI-Webinar2021.pdf",
  • Network accessline 94
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ms-20210913-ITsystems.pdf",
  • Network accessline 95
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ms-20210713-ProcessingPersonalInfo.pdf",
  • Network accessline 96
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ms-20210629-PAIA-SAHRC.pdf",
  • Network accessline 97
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ms-20210622-Exemptions-LawfulProcessing.pdf",
  • Network accessline 98
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ms-20210622-EnforcementPowers.pdf",
  • Network accessline 99
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ms-20210401-GuidanceNote-IO.pdf",
  • Network accessline 100
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ms-20210324-POPIA-Compliance.pdf",
  • Network accessline 101
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ms-20210303-Whatsapp.pdf"
  • Network accessline 105
    "enforcement-notices": {       "seen": [         "https://inforegulator.org.za/wp-content/uploads/2020/07/ENFORCEMENT-NOTICE-SAPS-MATTER-04052363.pdf",
  • Network accessline 106
    "seen": [         "https://inforegulator.org.za/wp-content/uploads/2020/07/ENFORCEMENT-NOTICE-SAPS-MATTER-04052363.pdf",         "https://inforegulator.org.za/wp-content/uploads/2020/07/ENFORCEMENT-NOTICE-HARDWICK-MATTER-04052375.pdf",
  • Network accessline 107
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ENFORCEMENT-NOTICE-SAPS-MATTER-04052363.pdf",
  • Network accessline 108
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ENFORCEMENT-NOTICE-HARDWICK-MATTER-04052375.pdf",
  • Network accessline 109
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ENFORCEMENT-NOTICE-DOJCD-MATTER-090523.pdf",
  • Network accessline 110
    "https://inforegulator.org.za/wp-content/uploads/2020/07/ENFORCEMENT-NOTICE-IN-RESPECT-OF-THE-DEPARTMENT-OF-BASIC-EDUCATION-6-November-2024.pdf",
  • Network accessline 111
    "https://inforegulator.org.za/wp-content/uploads/2020/07/FT-RAMS-CONSULTING-ENFORCEMENT-NOTICE-210224_Redacted.pdf",
  • Network accessline 112
    "https://inforegulator.org.za/wp-content/uploads/2020/07/DIS-CHEM-ENFORCEMENT-NOTICE.pdf",
  • Network accessline 113
    "https://inforegulator.org.za/wp-content/uploads/2025/04/WHATS-APP-ENFORCEMENT-NOTICE.pdf",
  • Network accessline 114
    "https://inforegulator.org.za/wp-content/uploads/2026/06/GAUTENG-DEPARTMENT-OF-HEALTH-ENFORCEMENT-NOTICE-080626.pdf",
  • Network accessline 115
    "https://inforegulator.org.za/wp-content/uploads/2026/06/SIBANYE-STILLWATER-ENFORCEMENT-NOTICE-220526-003_Redacted.pdf",
  • Network accessline 116
    "https://inforegulator.org.za/wp-content/uploads/2026/05/CJC-ENFORCEMENT-NOTICE-220526-redact_Redacted.pdf"
  • Network accessline 120
    "guidance-notes": {       "seen": [         "https://inforegulator.org.za/wp-content/uploads/2020/07/GUIDANCE-NOTE-ON-DIRECT-MARKETING-IN-TERMS-OF-THE-PROTECTION-OF-PERSONAL-INFORMATION-ACT-4-OF-2013-POPIA.pdf",
  • Network accessline 121
    "seen": [         "https://inforegulator.org.za/wp-content/uploads/2020/07/GUIDANCE-NOTE-ON-DIRECT-MARKETING-IN-TERMS-OF-THE-PROTECTION-OF-PERSONAL-INFORMATION-ACT-4-OF-2013-POPIA.pdf",         "https://inforegulator.org.za/wp-content/uploa
  • Network accessline 122
    "https://inforegulator.org.za/wp-content/uploads/2020/07/GUIDANCE-NOTE-ON-DIRECT-MARKETING-IN-TERMS-OF-THE-PROTECTION-OF-PERSONAL-INFORMATION-ACT-4-OF-2013-POPIA.pdf",
  • Network accessline 123
    "https://inforegulator.org.za/wp-content/uploads/2020/07/FINAL-GUIDANCE-NOTE-ON-THE-PROCESSING-OF-PERSONAL-INFORMATION-OF-VOTERS-AND-THE-COUNTERING-OF-MISINFORMATION-AND-DISINFORMATION-DURING-ELECTIONS.pdf",
  • Network accessline 124
    "https://inforegulator.org.za/wp-content/uploads/2020/07/Guidance-Note-Processing-Special-PersonalInformation-20210628-004.pdf",
  • Network accessline 125
    "https://inforegulator.org.za/wp-content/uploads/2020/07/InfoRegSA-Form-Application-form-for-authorisation-to-process-Special-Personal-Information-eForm-2021-1.pdf",
  • Network accessline 126
    "https://inforegulator.org.za/wp-content/uploads/2020/07/GuidanceNote-Processing-PersonalInformation-Children-20210628-1.pdf",
  • Network accessline 127
    "https://inforegulator.org.za/wp-content/uploads/2020/07/InfoRegSA-Form-Application-form-for-authorisation-to-process-Personal-Information-of-Children-eForm-2021-1.pdf",
  • Network accessline 128
    "https://inforegulator.org.za/wp-content/uploads/2020/07/InfoRegSA-GuidanceNote-PPI-Covid19-20200403.pdf",
  • Network accessline 129
    "https://inforegulator.org.za/wp-content/uploads/2020/07/InfoRegSA-GuidanceNote-PPI-PolParties-1.pdf"

scripts/__init__.py

  • Executable scriptline 1
    Executable file

scripts/build_adapters.py

  • Executable scriptline 1
    #!/usr/bin/env python3
  • Network accessline 63
    "```sql\n" + INVENTORY_SQL + "\n```\n\n"         "The same query and rules ship as `uvx popia-kit supabase sql` and `uvx popia-kit supabase check result.json`. "         "For the live site, use the free passive check at https://popiakit.co.
  • Network accessline 65
    "For the live site, use the free passive check at https://popiakit.co.za/check.\n")

scripts/cli.py

  • Executable scriptline 1
    #!/usr/bin/env python3
  • Network accessline 6
    popia-kit scan ./my-app                  code scanner (44 rules)     popia-kit operators ./my-app             third parties and the data sent to each     popia-kit site-check https://example.com passive live site check
  • Network accessline 7
    popia-kit operators ./my-app             third parties and the data sent to each     popia-kit site-check https://example.com passive live site check     popia-kit supabase sql                   read-only Supabase database check
  • Network accessline 8
    popia-kit site-check https://example.com passive live site check

scripts/popia_badge.py

  • Executable scriptline 1
    #!/usr/bin/env python3
  • Network accessline 6
    python3 scripts/popia_badge.py popia-scan.json --out popia-badge.svg     python3 scripts/popia_badge.py popia-scan.json --shields-json --out popia-badge.json     python3 scripts/site_check.py https://example.com --json site.json
  • Network accessline 7
    python3 scripts/popia_badge.py popia-scan.json --shields-json --out popia-badge.json     python3 scripts/site_check.py https://example.com --json site.json     python3 scripts/popia_badge.py site.json --label "POPIA site check" --out site-b
  • Network accessline 8
    python3 scripts/site_check.py https://example.com --json site.json
  • Network accessline 79
    l, m = escape(label), escape(msg)     title = escape(f"{label}: {msg}")     return f"""<svg xmlns="http://www.w3.org/2000/svg" width="{total}" height="20" role="img" aria-label="{title}">
  • Network accessline 80
    title = escape(f"{label}: {msg}")     return f"""<svg xmlns="http://www.w3.org/2000/svg" width="{total}" height="20" role="img" aria-label="{title}"> <title>{title}</title>
  • Network accessline 81
    return f"""<svg xmlns="http://www.w3.org/2000/svg" width="{total}" height="20" role="img" aria-label="{title}">

scripts/popia_fleet.py

  • Executable scriptline 1
    #!/usr/bin/env python3
  • Network accessline 94
    SAFE_CLONE_RX = re.compile(r"^(https://|git@[A-Za-z0-9.\-]+:)[^\s]+$")
  • Access to secretsline 104
    [Credential-related source omitted]

scripts/popia_init.py

  • Executable scriptline 1
    #!/usr/bin/env python3

scripts/popia_operators.py

  • Executable scriptline 1
    #!/usr/bin/env python3
  • Executable scriptline 1
    #!/usr/bin/env python3
  • Executable scriptline 1
    Executable file; content unavailable
  • Access to secretsline 405
    [Credential-related source omitted]
  • Access to secretsline 405
    [Credential-related source omitted]

scripts/popia_pack.py

  • Executable scriptline 1
    #!/usr/bin/env python3
  • Network accessline 57
    "[[VERIFY: Information Regulator contact details. Copy the current physical "     "address, postal address, complaints email, general enquiries email and website "     "from https://inforegulator.org.za. Do not copy details from old documen
  • Network accessline 58
    "address, postal address, complaints email, general enquiries email and website "     "from https://inforegulator.org.za. Do not copy details from old documents or "     "other organisations' notices.]]"
  • Network accessline 59
    "from https://inforegulator.org.za. Do not copy details from old documents or "
  • Network accessline 63
    REPO_URL = "https://github.com/VenusOne-Lee/popia-compliance"
  • Network accessline 760
    lines.append("")     lines.append("- **Information Regulator contact details** are a placeholder in every file that needs them. "                  "Copy the current details from https://inforegulator.org.za. This tool never supplies them.")
  • Network accessline 761
    lines.append("- **Information Regulator contact details** are a placeholder in every file that needs them. "                  "Copy the current details from https://inforegulator.org.za. This tool never supplies them.")     vitems = _verify
  • Network accessline 762
    "Copy the current details from https://inforegulator.org.za. This tool never supplies them.")
  • Network accessline 862
    "trading_name": "Example Lodge",         "physical_address": "1 Example Road, Example Town, 0000, South Africa (fictional)",         "website": "https://examplelodge.example",
  • Network accessline 863
    "physical_address": "1 Example Road, Example Town, 0000, South Africa (fictional)",         "website": "https://examplelodge.example",         "head_of_body": "Thandi Example (Managing Director)",
  • Network accessline 864
    "website": "https://examplelodge.example",

scripts/popia_scan.py

  • Executable scriptline 1
    #!/usr/bin/env python3
  • Executable scriptline 1
    #!/usr/bin/env python3
  • Access to secretsline 200
    [Credential-related source omitted]
  • Access to secretsline 200
    [Credential-related source omitted]
  • Access to secretsline 205
    [Credential-related source omitted]
  • Access to secretsline 205
    [Credential-related source omitted]
  • Network accessline 516
    })     return json.dumps({         "$schema": "https://json.schemastore.org/sarif-2.1.0.json", "version": "2.1.0",
  • Network accessline 516
    })     return json.dumps({         "$schema": "https://json.schemastore.org/sarif-2.1.0.json", "version": "2.1.0",
  • Network accessline 517
    return json.dumps({         "$schema": "https://json.schemastore.org/sarif-2.1.0.json", "version": "2.1.0",         "runs": [{"tool": {"driver": {"name": "popia_scan", "version": res["version"],
  • Network accessline 517
    return json.dumps({         "$schema": "https://json.schemastore.org/sarif-2.1.0.json", "version": "2.1.0",         "runs": [{"tool": {"driver": {"name": "popia_scan", "version": res["version"],
  • Network accessline 518
    "$schema": "https://json.schemastore.org/sarif-2.1.0.json", "version": "2.1.0",
  • Network accessline 518
    "$schema": "https://json.schemastore.org/sarif-2.1.0.json", "version": "2.1.0",
  • Network accessline 519
    "runs": [{"tool": {"driver": {"name": "popia_scan", "version": res["version"],                                       "informationUri": "https://github.com/VenusOne-Lee/popia-compliance",                                       "rules": rule_m
  • Network accessline 519
    "runs": [{"tool": {"driver": {"name": "popia_scan", "version": res["version"],                                       "informationUri": "https://github.com/VenusOne-Lee/popia-compliance",                                       "rules": rule_m
  • Network accessline 520
    "informationUri": "https://github.com/VenusOne-Lee/popia-compliance",
  • Network accessline 520
    "informationUri": "https://github.com/VenusOne-Lee/popia-compliance",

scripts/popia_vendors.py

  • Executable scriptline 1
    Executable file

scripts/regwatch.py

  • Executable scriptline 1
    #!/usr/bin/env python3
  • Network accessline 39
    DEFAULT_CONFIG = {     "user_agent": "popia-compliance-regwatch/1.0 (+https://github.com/VenusOne-Lee/popia-compliance)",
  • Network accessline 40
    "user_agent": "popia-compliance-regwatch/1.0 (+https://github.com/VenusOne-Lee/popia-compliance)",
  • Network accessline 156
    def fetch(url: str, ua: str, timeout: float) -> str:
  • Network accessline 157
    if not url.startswith("https://"):         raise ValueError("regwatch sources must be https URLs")     req = urllib.request.Request(url, headers={"User-Agent": ua, "Accept": "text/html"})
  • Network accessline 159
    req = urllib.request.Request(url, headers={"User-Agent": ua, "Accept": "text/html"})
  • Network accessline 161
    with urllib.request.urlopen(req, timeout=timeout) as r:  # nosec B310
  • Network accessline 253
    if n:                     time.sleep(delay)                 html = fetch(src["url"], ua, timeout)
  • Network accessline 254
    time.sleep(delay)                 html = fetch(src["url"], ua, timeout)             res["items"] = extract_items(html, src["url"], config)
  • Network accessline 255
    html = fetch(src["url"], ua, timeout)

scripts/rules/__init__.py

  • Executable scriptline 1
    Executable file

scripts/rules/access.py

  • Executable scriptline 1
    Executable file

scripts/rules/consent.py

  • Executable scriptline 1
    Executable file

scripts/rules/pii.py

  • Executable scriptline 1
    Executable file
  • Access to secretsline 105
    [Credential-related source omitted]

scripts/rules/repo.py

  • Executable scriptline 1
    Executable file

scripts/rules/secrets.py

  • Executable scriptline 1
    Executable file
  • Access to secretsline 24
    [Credential-related source omitted]
  • Access to secretsline 88
    [Credential-related source omitted]

scripts/rules/thirdparty.py

  • Executable scriptline 1
    Executable file

scripts/rules/web.py

  • Executable scriptline 1
    Executable file
  • Network accessline 170
    Rule(         id="SEC-009", title="Unencrypted http:// endpoint", severity="medium", popia="s19",         pattern=r"""['"`]http://(?!localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\]|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|www\.w3\.org|schemas\.|
  • Network accessline 171
    id="SEC-009", title="Unencrypted http:// endpoint", severity="medium", popia="s19",         pattern=r"""['"`]http://(?!localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\]|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|www\.w3\.org|schemas\.|xmlns|example\
  • Network accessline 172
    pattern=r"""['"`]http://(?!localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\]|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|www\.w3\.org|schemas\.|xmlns|example\.(com|org)|purl\.org|ns\.adobe)[^'"`\s]+""",
  • Access to secretsline 224
    [Credential-related source omitted]

scripts/sa_id.py

  • Executable scriptline 1
    #!/usr/bin/env python3

scripts/site_check.py

  • Executable scriptline 1
    #!/usr/bin/env python3
  • Network accessline 9
    python3 site_check.py https://example.co.za [--out report.md] [--json report.json] [--timeout 15]
  • Network accessline 11
    Programmatic:     from site_check import check, fmt_md     res = check("https://example.co.za")
  • Network accessline 13
    res = check("https://example.co.za")
  • Network accessline 38
    __version__ = "1.0.1" BOT_INFO_URL = "https://github.com/VenusOne-Lee/popia-compliance/blob/main/docs/site-check-bot.md"
  • Network accessline 39
    BOT_INFO_URL = "https://github.com/VenusOne-Lee/popia-compliance/blob/main/docs/site-check-bot.md"
  • Network accessline 124
    hint = ("Scan your code instead, bot protection does not affect it: popia-kit scan . "             "Or add the POPIA Kit skill to your AI tool: "             "https://github.com/VenusOne-Lee/popia-compliance#whats-inside . "
  • Network accessline 125
    "Or add the POPIA Kit skill to your AI tool: "             "https://github.com/VenusOne-Lee/popia-compliance#whats-inside . "             "If it is your site, you can allow the checker for one check: " + BOT_INFO_URL)
  • Network accessline 126
    "https://github.com/VenusOne-Lee/popia-compliance#whats-inside . "
  • Network accessline 168
    ctx = ssl.create_default_context()     _check_target(url, block_private)     req = urllib.request.Request(url, method=method, headers={"User-Agent": UA, "Accept": "text/html,*/*"})
  • Network accessline 170
    req = urllib.request.Request(url, method=method, headers={"User-Agent": UA, "Accept": "text/html,*/*"})

scripts/style_lint.py

  • Executable scriptline 1
    #!/usr/bin/env python3

scripts/supabase_check.py

  • Executable scriptline 1
    #!/usr/bin/env python3
  • Access to secretsline 241
    [Credential-related source omitted]
  • Access to secretsline 251
    [Credential-related source omitted]

server.json

  • Network accessline 1
    {   "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
  • Network accessline 2
    "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
  • Network accessline 4
    "title": "POPIA Kit",   "description": "POPIA privacy and security checks for South African apps: code scan, site check, guidance.",   "websiteUrl": "https://popiakit.co.za",
  • Network accessline 5
    "description": "POPIA privacy and security checks for South African apps: code scan, site check, guidance.",   "websiteUrl": "https://popiakit.co.za",   "repository": {
  • Network accessline 6
    "websiteUrl": "https://popiakit.co.za",
  • Network accessline 7
    "repository": {     "url": "https://github.com/VenusOne-Lee/popia-compliance",     "source": "github"
  • Network accessline 8
    "url": "https://github.com/VenusOne-Lee/popia-compliance",
  • Network accessline 13
    {       "registryType": "pypi",       "registryBaseUrl": "https://pypi.org",
  • Network accessline 14
    "registryType": "pypi",       "registryBaseUrl": "https://pypi.org",       "identifier": "popia-kit",
  • Network accessline 15
    "registryBaseUrl": "https://pypi.org",

templates/breach-notice-data-subject.md

  • Network accessline 51
    You also have the right to lodge a complaint with the Information Regulator. {{regulator_contact}} Verify the current contact details on https://inforegulator.org.za.

templates/breach-regulator-checklist.md

  • Network accessline 7
    This checklist lists the information to **have ready** before logging on. It does not reproduce the portal's field names, which may change. Check the portal and the Regulator's current guidance on https://inforegulator.org.za.

templates/dsar-runbook.md

  • Network accessline 133
    Every refusal or partial response must tell the data subject they may complain to the Information Regulator. {{regulator_contact}} Verify current contact details on https://inforegulator.org.za.

templates/pack-questionnaire.json

  • Network accessline 23
    {"id": "physical_address", "section": "Company", "prompt": "Physical address", "type": "text", "required": true, "default": null, "help": "Street address of the responsible party. s18(1)(b)."},     {"id": "postal_address", "section": "Compa
  • Network accessline 24
    {"id": "postal_address", "section": "Company", "prompt": "Postal address (if different)", "type": "text", "required": false, "default": null, "help": "Leave blank if the same as the physical address."},     {"id": "website", "section": "Com
  • Network accessline 25
    {"id": "website", "section": "Company", "prompt": "Website", "type": "url", "required": true, "default": null, "help": "Main website, for example https://example.co.za."},

templates/paia-manual-scaffold.md

  • Network accessline 7
    **Important:** the Information Regulator publishes a guide and template for PAIA manuals and may update them. Check the current template and guide on https://inforegulator.org.za before publishing, and align this scaffold to it. Requirement
  • Network accessline 44
    Verify current contact details on https://inforegulator.org.za.

templates/privacy-notice.md

  • Network accessline 231
    Contact details change. Verify the current details on the Regulator's website, https://inforegulator.org.za, before relying on them.

tests/__init__.py

  • Executable scriptline 1
    Executable file

tests/automation/__init__.py

  • Executable scriptline 1
    Executable file

tests/automation/fixtures/__init__.py

  • Executable scriptline 1
    Executable file

tests/automation/fixtures/regulator-list.html

  • Network accessline 9
    <a href="/about/">About</a>     <a href="#content">Skip to content</a>     <a href="https://www.facebook.com/example">Facebook profile for the example site</a>
  • Network accessline 10
    <a href="#content">Skip to content</a>     <a href="https://www.facebook.com/example">Facebook profile for the example site</a>   </nav>
  • Network accessline 11
    <a href="https://www.facebook.com/example">Facebook profile for the example site</a>
  • Network accessline 16
    <ul class="items">     <li><a href="/wp-content/uploads/2026/09/Fixture-Statement-Alpha.pdf">Fixture statement alpha on test topic one</a> <span>1 September 2026</span></li>     <li><a href="https://example.test/wp-content/uploads/2026/08/F
  • Network accessline 17
    <li><a href="/wp-content/uploads/2026/09/Fixture-Statement-Alpha.pdf">Fixture statement alpha on test topic one</a> <span>1 September 2026</span></li>     <li><a href="https://example.test/wp-content/uploads/2026/08/Fixture-Notice-Beta.pdf"
  • Network accessline 18
    <li><a href="https://example.test/wp-content/uploads/2026/08/Fixture-Notice-Beta.pdf">

tests/automation/test_badge.py

  • Executable scriptline 1
    Executable file

tests/automation/test_cli.py

  • Executable scriptline 1
    Executable file

tests/automation/test_fleet.py

  • Executable scriptline 1
    Executable file

tests/automation/test_init.py

  • Executable scriptline 1
    Executable file

tests/automation/test_mcp.py

  • Executable scriptline 1
    Executable file
  • Access to secretsline 76
    [Credential-related source omitted]

tests/automation/test_regwatch.py

  • Executable scriptline 1
    Executable file
  • Network accessline 13
    FIXTURE = ROOT / "tests" / "automation" / "fixtures" / "regulator-list.html" BASE = "https://inforegulator.org.za/media-statements/"
  • Network accessline 14
    BASE = "https://inforegulator.org.za/media-statements/"
  • Network accessline 33
    def test_resolves_relative_links(self):         self.assertEqual(self.items[0]["url"],                          "https://inforegulator.org.za/wp-content/uploads/2026/09/Fixture-Statement-Alpha.pdf")
  • Network accessline 34
    self.assertEqual(self.items[0]["url"],                          "https://inforegulator.org.za/wp-content/uploads/2026/09/Fixture-Statement-Alpha.pdf")         self.assertTrue(self.items[1]["url"].startswith("https://example.test/"))
  • Network accessline 35
    "https://inforegulator.org.za/wp-content/uploads/2026/09/Fixture-Statement-Alpha.pdf")
  • Network accessline 36
    self.assertTrue(self.items[1]["url"].startswith("https://example.test/"))
  • Network accessline 78
    def test_sources_config_has_required_pages(self):         urls = {s["url"] for s in config()["sources"]}         self.assertIn("https://inforegulator.org.za/media-statements/", urls)
  • Network accessline 79
    urls = {s["url"] for s in config()["sources"]}         self.assertIn("https://inforegulator.org.za/media-statements/", urls)         self.assertIn("https://inforegulator.org.za/enforcement-notices/", urls)
  • Network accessline 80
    self.assertIn("https://inforegulator.org.za/media-statements/", urls)
  • Network accessline 81
    self.assertIn("https://inforegulator.org.za/enforcement-notices/", urls)

tests/fixtures/demo-app/index.html

  • Network accessline 3
    <head>   <!-- Deliberately non-compliant demo fixture for popia_scan tests. Synthetic data only. -->   <script async src="https://www.googletagmanager.com/gtag/js?id=G-DEMO000000"></script>
  • Network accessline 4
    <!-- Deliberately non-compliant demo fixture for popia_scan tests. Synthetic data only. -->   <script async src="https://www.googletagmanager.com/gtag/js?id=G-DEMO000000"></script>   <script>fbq('init', '000000000000000');</script>
  • Network accessline 5
    <script async src="https://www.googletagmanager.com/gtag/js?id=G-DEMO000000"></script>

tests/fixtures/demo-app/package.json

  • Executable scriptline 1
    Executable file

tests/fixtures/demo-app/src/ai.ts

  • Executable scriptline 1
    Executable file

tests/fixtures/demo-app/src/components/SignupForm.tsx

  • Executable scriptline 1
    Executable file
  • Network accessline 8
    console.log("new signup", email, req.body);     localStorage.setItem("user_email", email);     await fetch(`http://api.demo-app.co.za/lead?email=${email}`);
  • Network accessline 9
    localStorage.setItem("user_email", email);     await fetch(`http://api.demo-app.co.za/lead?email=${email}`);   }
  • Network accessline 10
    await fetch(`http://api.demo-app.co.za/lead?email=${email}`);

tests/fixtures/demo-app/src/newsletter.ts

  • Executable scriptline 1
    Executable file

tests/fixtures/demo-app/supabase/functions/lead/index.ts

  • Executable scriptline 1
    Executable file

tests/fixtures/operators-app/.env.example

  • Network accessline 1
    SUPABASE_URL=https://fixture-project.supabase.co

tests/fixtures/operators-app/api/triage.py

  • Executable scriptline 1
    Executable file

tests/fixtures/operators-app/package.json

  • Executable scriptline 1
    Executable file

tests/fixtures/operators-app/public/contact.html

  • Network accessline 1
    <!-- Synthetic fixture for the operator inventory. --> <form action="https://formspree.io/f/fixture" method="post">
  • Network accessline 2
    <form action="https://formspree.io/f/fixture" method="post">
  • Network accessline 4
    <input name="phone" type="tel"> </form> <a href="https://www.w3.org/TR/html52/">spec</a>
  • Network accessline 6
    <a href="https://www.w3.org/TR/html52/">spec</a>

tests/fixtures/operators-app/src/checkout.ts

  • Executable scriptline 1
    Executable file
  • Access to secretsline 5
    [Credential-related source omitted]

tests/fixtures/operators-app/src/sms.js

  • Executable scriptline 1
    Executable file
  • Access to secretsline 3
    [Credential-related source omitted]
  • Network accessline 9
    async function crm(lead) {   await fetch("https://hooks.zapier.com/hooks/catch/0/fixture", { method: "POST", body: JSON.stringify({ email: lead.email }) });
  • Network accessline 10
    await fetch("https://hooks.zapier.com/hooks/catch/0/fixture", { method: "POST", body: JSON.stringify({ email: lead.email }) });
  • Network accessline 11
    await fetch("https://api.partner-crm.example.co/leads", { method: "POST", body: JSON.stringify({ phone: lead.phone }) });
  • Network accessline 12
    await fetch("https://api.operators-app.co.za/internal", { method: "POST", body: JSON.stringify({ phone: lead.phone }) });

tests/fixtures/operators-app/supabase-edge.ts

  • Executable scriptline 1
    Executable file
  • Network accessline 2
    import { Resend } from "https://esm.sh/resend@3.2.0";
  • Access to secretsline 3
    [Credential-related source omitted]

tests/fixtures/operators-app/tests/checkout.test.ts

  • Executable scriptline 1
    Executable file

tests/pack/__init__.py

  • Executable scriptline 1
    Executable file

tests/pack/kasi-bookings-answers.json

  • Network accessline 5
    "trading_name": "Kasi Bookings",   "physical_address": "10 Test Street, Soweto, Johannesburg, 0000 (fictional)",   "website": "https://kasibookings.example",
  • Network accessline 6
    "physical_address": "10 Test Street, Soweto, Johannesburg, 0000 (fictional)",   "website": "https://kasibookings.example",   "head_of_body": "Naledi Test (CEO)",
  • Network accessline 7
    "website": "https://kasibookings.example",

tests/pack/test_pack.py

  • Executable scriptline 1
    Executable file

tests/scanner/__init__.py

  • Executable scriptline 1
    Executable file

tests/scanner/test_baseline.py

  • Executable scriptline 1
    Executable file

tests/scanner/test_operators.py

  • Executable scriptline 1
    Executable file
  • Network accessline 135
    self.assertEqual(po._js_package("@supabase/supabase-js/dist/x"), "@supabase/supabase-js")         self.assertEqual(po._js_package("npm:openai@4.2.0"), "openai")         self.assertEqual(po._js_package("https://esm.sh/@supabase/supabase-js@2
  • Network accessline 136
    self.assertEqual(po._js_package("npm:openai@4.2.0"), "openai")         self.assertEqual(po._js_package("https://esm.sh/@supabase/supabase-js@2"), "@supabase/supabase-js")         self.assertIsNone(po._js_package("./local"))
  • Network accessline 137
    self.assertEqual(po._js_package("https://esm.sh/@supabase/supabase-js@2"), "@supabase/supabase-js")
  • Network accessline 159
    with tempfile.TemporaryDirectory() as d:             Path(d, "a.ts").write_text(                 f'fetch("https://hooks.zapier.com/x?id={fid}&e={email}", {{ method: "POST" }});\n',
  • Network accessline 160
    Path(d, "a.ts").write_text(                 f'fetch("https://hooks.zapier.com/x?id={fid}&e={email}", {{ method: "POST" }});\n',                 encoding="utf-8")
  • Network accessline 161
    f'fetch("https://hooks.zapier.com/x?id={fid}&e={email}", {{ method: "POST" }});\n',

tests/scanner/test_rules.py

  • Executable scriptline 1
    Executable file
  • Access to secretsline 87
    [Credential-related source omitted]
  • Access to secretsline 150
    [Credential-related source omitted]

tests/scanner/test_site_check.py

  • Executable scriptline 1
    Executable file
  • Network accessline 13
    PAGE = b"""<!doctype html><html><head> <script async src="https://www.googletagmanager.com/gtag/js?id=G-TEST"></script>
  • Network accessline 14
    <script async src="https://www.googletagmanager.com/gtag/js?id=G-TEST"></script>
  • Network accessline 42
    cls.t = threading.Thread(target=cls.srv.serve_forever, daemon=True)         cls.t.start()         cls.res = site_check.check(f"http://127.0.0.1:{cls.srv.server_port}/", timeout=5)
  • Network accessline 44
    cls.res = site_check.check(f"http://127.0.0.1:{cls.srv.server_port}/", timeout=5)
  • Network accessline 94
    cls.srv = HTTPServer(("127.0.0.1", 0), Unreadable)         threading.Thread(target=cls.srv.serve_forever, daemon=True).start()         cls.base = f"http://127.0.0.1:{cls.srv.server_port}"
  • Network accessline 96
    cls.base = f"http://127.0.0.1:{cls.srv.server_port}"

tests/security/__init__.py

  • Executable scriptline 1
    Executable file

tests/security/test_hardening.py

  • Executable scriptline 1
    Executable file
  • Access to secretsline 32
    [Credential-related source omitted]
  • Network accessline 33
    [Credential-related source omitted]
  • Network accessline 34
    [Credential-related source omitted]
  • Network accessline 35
    [Credential-related source omitted]
  • Network accessline 45
    [Credential-related source omitted]
  • Network accessline 46
    [Credential-related source omitted]
  • Network accessline 47
    [Credential-related source omitted]
  • Access to secretsline 66
    [Credential-related source omitted]
  • Access to secretsline 67
    [Credential-related source omitted]
  • Access to secretsline 68
    [Credential-related source omitted]
  • Network accessline 73
    def test_block_private_refuses_internal_targets(self):         with self.assertRaises(site_check.BlockedTarget):             site_check.check(f"http://127.0.0.1:{self.srv.server_port}/", timeout=5, block_private=True)
  • Network accessline 74
    with self.assertRaises(site_check.BlockedTarget):             site_check.check(f"http://127.0.0.1:{self.srv.server_port}/", timeout=5, block_private=True)         for bad in ("http://169.254.169.254/latest/meta-data/", "http://10.0.0.1/", "
  • Network accessline 75
    site_check.check(f"http://127.0.0.1:{self.srv.server_port}/", timeout=5, block_private=True)
  • Network accessline 76
    for bad in ("http://169.254.169.254/latest/meta-data/", "http://10.0.0.1/", "http://[::1]/"):
  • Network accessline 85
    [Credential-related source omitted]
  • Network accessline 86
    [Credential-related source omitted]
  • Network accessline 87
    [Credential-related source omitted]
  • Network accessline 99
    [Credential-related source omitted]
  • Network accessline 100
    [Credential-related source omitted]
  • Access to secretsline 116
    [Credential-related source omitted]
  • Network accessline 137
    def test_site_check_tool_blocks_private_addresses(self):         out = self.s.call_tool({"name": "popia_site_check", "arguments": {"url": "http://127.0.0.1:9/"}})
  • Network accessline 138
    out = self.s.call_tool({"name": "popia_site_check", "arguments": {"url": "http://127.0.0.1:9/"}})
  • Network accessline 144
    def test_clone_rejects_unsafe_urls(self):         for bad in ("--upload-pack=touch /tmp/pwned", "file:///etc", "/local/repo", "ext::sh -c id",                     "http://insecure.test/repo.git", "-c core.sshCommand=id"):
  • Network accessline 145
    for bad in ("--upload-pack=touch /tmp/pwned", "file:///etc", "/local/repo", "ext::sh -c id",                     "http://insecure.test/repo.git", "-c core.sshCommand=id"):             with self.assertRaises(ValueError, msg=bad):
  • Network accessline 146
    "http://insecure.test/repo.git", "-c core.sshCommand=id"):
  • Network accessline 150
    def test_git_error_redacts_tokens(self):         msg = popia_fleet._redact_git_error(f"fatal: could not read https://{SECRET}@github.com/x/y.git")
  • Network accessline 151
    msg = popia_fleet._redact_git_error(f"fatal: could not read https://{SECRET}@github.com/x/y.git")

tests/supabase/__init__.py

  • Executable scriptline 1
    Executable file

tests/supabase/run_live.sh

  • Executable scriptline 1
    #!/usr/bin/env bash
  • Dangerous shell operationline 12
    cleanup() { "${RUNAS[@]}" "$PGBIN/pg_ctl" -D "$WORK/data" -m immediate stop >/dev/null 2>&1 || true; rm -rf "$WORK"; }

tests/supabase/test_supabase_check.py

  • Executable scriptline 1
    Executable file
  • Access to secretsline 156
    [Credential-related source omitted]
  • Access to secretsline 157
    [Credential-related source omitted]
  • Network accessline 161
    self.assertIn("SUPABASE_DB_URL", err)             marker = "not" + "-a-real-" + "value"             os.environ["SUPABASE_DB_URL"] = "https://user:" + marker + "@example.com/db"
  • Network accessline 162
    marker = "not" + "-a-real-" + "value"             os.environ["SUPABASE_DB_URL"] = "https://user:" + marker + "@example.com/db"             code, _, err = run("run")
  • Network accessline 163
    [Credential-related source omitted]
  • Access to secretsline 163
    [Credential-related source omitted]
  • Access to secretsline 168
    [Credential-related source omitted]
  • Access to secretsline 169
    [Credential-related source omitted]
  • Access to secretsline 180
    [Credential-related source omitted]
  • Access to secretsline 188
    [Credential-related source omitted]
  • Access to secretsline 191
    [Credential-related source omitted]
  • Access to secretsline 192
    [Credential-related source omitted]
  • Access to secretsline 197
    [Credential-related source omitted]
  • Access to secretsline 199
    [Credential-related source omitted]
  • Access to secretsline 212
    [Credential-related source omitted]
  • Access to secretsline 214
    [Credential-related source omitted]
  • Access to secretsline 221
    [Credential-related source omitted]
  • Access to secretsline 223
    [Credential-related source omitted]

How the signals fit together

Facts from a static read of the files at the pinned commit. They describe what the code does, not why.

Some files could not be read; the facts below still hold.

Declared and executed: 5 facts
Signal confidence: 495 facts

and 445 more

Only the first 500 facts are listed.

Works with

Agent compatibility, from the files in this repository
AgentStatusEvidenceInstall to
Claude CodeCompatibleStandard SKILL.md SKILL.md.claude/skills/<skill-name>/
CodexNativeManifest adapters/agents/AGENTS.md.agents/skills/<skill-name>/
CursorNativePath adapters/cursor/.cursor/rules/popia.mdc.cursor/skills/<skill-name>/
Gemini CLICompatibleStandard SKILL.md SKILL.md.gemini/skills/<skill-name>/
GitHub CopilotCompatibleStandard SKILL.md SKILL.md.github/skills/<skill-name>/
OpenCodeCompatibleStandard SKILL.md SKILL.md.opencode/skills/<skill-name>/
  • Native The repository ships a manifest, folder or frontmatter field for this agent.
  • Compatible A standard SKILL.md that this agent can read.
  • Unknown No evidence either way.

Derived automatically from paths, manifests and frontmatter. Not tested on each agent.