frappe-web-forms — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited frappe-web-forms (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Build public-facing web forms for data collection, submissions, and customer self-service.
Ensure the target DocType exists and has the fields you want to expose.
| Setting | Purpose |
|---|---|
| Login Required | Require authentication before form access |
| Allow Edit | Let users edit their submitted entries |
| Allow Multiple | Let users submit more than one entry |
| Show as Card | Display in card layout style |
| Max Attachment Size | Limit file upload sizes |
| Success URL | Redirect after successful submission |
| Success Message | Custom message after submission |
Check "Is Standard" (visible in Developer Mode) to export the form as files:
my_app/
└── my_module/
└── web_form/
└── contact_us/
├── contact_us.json # Web form metadata
├── contact_us.py # Server-side customization
└── contact_us.js # Client-side customization# contact_us.py
import frappe
def get_context(context):
"""Add custom context variables to the web form."""
context.categories = frappe.get_all("Support Category",
filters={"enabled": 1},
fields=["name", "label"],
order_by="label asc"
)
def validate(doc):
"""Custom validation before the document is saved."""
if not doc.email:
frappe.throw("Email address is required")
# Prevent duplicate submissions
existing = frappe.db.exists("Support Ticket", {"email": doc.email, "status": "Open"})
if existing:
frappe.throw("You already have an open ticket. Please wait for a response.")// contact_us.js
frappe.ready(function() {
// Handle field changes
frappe.web_form.on("field_change", function(field, value) {
if (field === "category" && value === "Urgent") {
frappe.web_form.set_df_property("description", "reqd", 1);
}
});
// Custom validation
frappe.web_form.validate = function() {
let data = frappe.web_form.get_values();
if (data.phone && !data.phone.match(/^\+?[0-9\-\s]+$/)) {
frappe.msgprint("Please enter a valid phone number");
return false;
}
return true;
};
// Custom after-save behavior
frappe.web_form.after_save = function() {
frappe.msgprint("Thank you for your submission!");
};
});Web forms use the website theme by default. For custom styling:
<!-- Add custom CSS via Web Form → Custom CSS field -->
<style>
.web-form-container { max-width: 600px; margin: 0 auto; }
.web-form-container .form-group { margin-bottom: 1.5rem; }
.web-form-container .btn-primary { background-color: #2490EF; }
</style>/contact-us)validate function in Python file returns/throws correctlyfrappe-doctype-developmentfrappe-frontend-developmentfrappe-api-developmentvalidate() Python methodfrappe.utils.escape_html()| Mistake | Why It Fails | Fix | |
|---|---|---|---|
| Missing DocType permissions | "Permission denied" on submit | Grant Create permission to Website User or Guest role | |
| Not handling file uploads | Files don't attach to record | Configure Attach field properly; check upload limits | |
| XSS vulnerabilities | Security risk | Escape user input in display; use ` | e` filter in templates |
| Forgetting to publish form | 404 error | Check "Published" checkbox in Web Form | |
| Client-only validation | Invalid data in database | Add validate() method in web form Python file | |
| Not testing as guest user | Works for admin, fails for users | Test in incognito/logged out mode |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.