{"id":50405982,"url":"https://github.com/dmptrluke/django-formguard","last_synced_at":"2026-05-31T01:30:59.676Z","repository":{"id":345435740,"uuid":"1185882610","full_name":"dmptrluke/django-formguard","owner":"dmptrluke","description":"Layered form protection for Django.","archived":false,"fork":false,"pushed_at":"2026-05-12T02:26:51.000Z","size":269,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-12T03:27:59.798Z","etag":null,"topics":["antispam","django","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dmptrluke.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-19T03:14:11.000Z","updated_at":"2026-05-12T02:26:28.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dmptrluke/django-formguard","commit_stats":null,"previous_names":["dmptrluke/django-formguard"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/dmptrluke/django-formguard","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmptrluke%2Fdjango-formguard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmptrluke%2Fdjango-formguard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmptrluke%2Fdjango-formguard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmptrluke%2Fdjango-formguard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dmptrluke","download_url":"https://codeload.github.com/dmptrluke/django-formguard/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmptrluke%2Fdjango-formguard/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33716338,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["antispam","django","python"],"created_at":"2026-05-31T01:30:57.961Z","updated_at":"2026-05-31T01:30:59.658Z","avatar_url":"https://github.com/dmptrluke.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# django-formguard\n\nInvisible form protection for Django. A pluggable check pipeline that catches\nbots without any user interaction.\n\n**WARNING**: This library is still under development. Expect breaking changes until 1.0.0.\n\n## How It Works\n\nFormGuard runs a series of checks against each form submission:\n\n1. **Field trap** - hidden honeypot field that bots fill in\n2. **Token** - signed timestamp that catches fast or tampered submissions\n3. **JS challenge** - nonce computation that requires JavaScript execution\n4. **Interaction proof** - detects real user input (accessible to screen readers)\n\nAll checks are invisible to real users. Check failures are handled as standard\nform validation errors, with customizable failure handling for honeypot-style\nsilent rejection.\n\nFormGuard is extensible. [Cloudflare Turnstile](docs/turnstile.md) is available\nas a contrib check, and you can [write your own](docs/custom-checks.md).\n\n## Installation\n\n```bash\npip install django-formguard\n```\n\nAdd `'formguard'` to your `INSTALLED_APPS`:\n\n```python\nINSTALLED_APPS = [\n    ...\n    'formguard',\n]\n```\n\n## Quick Start\n\n### 1. Set up your form\nAdd `GuardedFormMixin` to your form.\n\n```python\nfrom django import forms\nfrom formguard.conf import default_checks\nfrom formguard.forms import GuardedFormMixin\n\nclass ContactForm(GuardedFormMixin, forms.Form):\n    guard_checks = default_checks(exclude=['formguard.checks.InteractionCheck'])\n\n    name = forms.CharField()\n    email = forms.EmailField()\n    message = forms.CharField(widget=forms.Textarea)\n```\n\n`default_checks()` returns the built-in check list. Use `include` and `exclude`\nto customize it, or omit `guard_checks` entirely to use all defaults.\n\n### 2. Set up your template\n\nInclude `{{ form.media }}` in your `\u003chead\u003e` so the honeypot CSS and JS\nchecks load. \n\n```html\n\u003chead\u003e\n    {{ form.media }}\n\u003c/head\u003e\n\u003cbody\u003e\n    \u003cform method=\"post\"\u003e\n        {% csrf_token %}\n        {{ form }}\n        \u003cbutton type=\"submit\"\u003eSend\u003c/button\u003e\n    \u003c/form\u003e\n\u003c/body\u003e\n```\n\nIf you render fields manually, include\n`{{ form.guard_fields }}` to add the FormGuard fields.\n\n### 3. Check submissions in your view\n\nGuard checks run inside `is_valid()`, so your view uses standard Django patterns.\n\n**Class-based views** - use `GuardedFormViewMixin`:\n\n```python\nfrom django.views.generic import FormView\nfrom formguard.views import GuardedFormViewMixin\n\nclass ContactView(GuardedFormViewMixin, FormView):\n    form_class = ContactForm\n    template_name = 'contact.html'\n    success_url = '/thanks/'\n\n    def form_valid(self, form):\n        send_email(form.cleaned_data)\n        return super().form_valid(form)\n```\n\nIf a check fails, the form returns an error message. To silently redirect bots\nto a fake success page instead, see [Failure Handling](docs/failure-handling.md).\n\n**Function-based views** - see [Function-Based Views](docs/function-based-views.md)\n\n## Settings\n\nAll settings are optional. Defaults work out of the box. All four built-in\nchecks are enabled by default.\n\n```python\n# add a custom check alongside the builtins\nfrom formguard.conf import BUILTINS\nFORMGUARD_CHECKS = BUILTINS + [\n    'myapp.checks.TurnstileCheck',\n]\n\n# tune the built-in checks\nFORMGUARD_FIELD_TRAP_FIELD_NAME = 'website'  # default\nFORMGUARD_TOKEN_MIN_SECONDS = 3              # default\nFORMGUARD_TOKEN_MAX_SECONDS = 3600           # default\n```\n\n## Further Reading\n\n- [Function-Based Views](docs/function-based-views.md) - using FormGuard without class-based views\n- [Failure Handling](docs/failure-handling.md) - customize what happens when a guard check fails\n- [Configuration](docs/configuration.md) - per-form checks, manual rendering, settings reference\n- [Built-in Checks](docs/checks.md) - reference for each check\n- [Cloudflare Turnstile](docs/turnstile.md) - add Turnstile CAPTCHA verification\n- [Custom Checks](docs/custom-checks.md) - write your own checks\n- [Testing](docs/testing.md) - test helpers for guarded forms\n- [Signals](docs/signals.md) - hook into bot detection events\n\n## CSP Compatibility\n\nFormGuard uses external static files for CSS and JS (no inline styles or\nscripts), so it works with Content Security Policy out of the box when\n`'self'` is in your `script-src` and `style-src`.\n\nFor strict nonce-based CSP policies, use\n[django-csp-helpers](https://github.com/dmptrluke/django-csp-helpers) to add\nnonces to the `{{ form.media }}` output.\n\n## License\n\nThis software is released under the MIT license.\n```\nCopyright (c) 2026 Luke Rogers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmptrluke%2Fdjango-formguard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdmptrluke%2Fdjango-formguard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmptrluke%2Fdjango-formguard/lists"}