frappe-agent-validator — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited frappe-agent-validator (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.
Validates Frappe/ERPNext code against the complete 61-skill knowledge base, catching errors BEFORE deployment.
Purpose: Catch errors before deployment, not after
CODE VALIDATION TRIGGERS
|
+-- Code has been generated and needs review
| "Check this Server Script before I save it"
| --> USE THIS AGENT
|
+-- Code is causing errors
| "Why isn't this working?"
| --> USE THIS AGENT
|
+-- Pre-deployment validation
| "Is this production-ready?"
| --> USE THIS AGENT
|
+-- Code review for best practices
| "Can this be improved?"
| --> USE THIS AGENT
|
+-- Ops/deployment validation
| "Is my bench setup correct?"
| --> USE THIS AGENTSTEP 1: IDENTIFY CODE TYPE
Client Script | Server Script | Controller | hooks.py |
Jinja | Whitelisted | Bench/Ops | DocType JSON
STEP 2: RUN TYPE-SPECIFIC CHECKS
Apply checklist for identified code type
STEP 3: CHECK UNIVERSAL RULES
Error handling | Security | Performance | User feedback
STEP 4: VERIFY VERSION COMPATIBILITY
v14/v15/v16 features | Deprecated patterns
STEP 5: VALIDATE AGAINST SKILL CATALOG
Cross-reference with relevant frappe-* skills
STEP 6: GENERATE VALIDATION REPORT
Critical errors | Warnings | Suggestions | Corrected codeSee references/workflow.md for detailed steps.
| Check | Severity | Pattern | Fix |
|---|---|---|---|
| Import statements | FATAL | import X or from X import Y | Use frappe.utils.X() directly |
| Wrong doc variable | FATAL | self.field or document.field | Use doc.field |
| Wrong event for purpose | ERROR | Validation code in on_update | Move to validate event |
| try/except blocks | WARNING | try: ... except: | Use frappe.throw() for validation |
| No null checks | WARNING | doc.field.lower() | Add if doc.field: guard |
| Check | Severity | Pattern | Fix |
|---|---|---|---|
| Server-side API calls | FATAL | frappe.db.get_value() | Use frappe.call() |
| Missing async handling | FATAL | let x = frappe.call() | Use callback or async/await |
| No refresh after set_value | ERROR | frm.set_value() alone | Add frm.refresh_field() |
| Using cur_frm | WARNING | cur_frm.doc.field | Use frm parameter |
| No form state check | WARNING | Missing __islocal/docstatus | Add state guards |
| Check | Severity | Pattern | Fix |
|---|---|---|---|
| self.* in on_update | FATAL | self.field = X in on_update | Use self.db_set() |
| Circular save | FATAL | self.save() in lifecycle hook | Remove self.save() |
| Missing super() | ERROR | Override without super() | Add super().method() |
| v16 extend_doctype_class | ERROR | Missing super() in mixin | ALWAYS call super() first |
| No type annotations | SUGGESTION | Missing type hints (v16) | Add type annotations |
| Check | Severity | Pattern | Fix |
|---|---|---|---|
| Invalid Python syntax | FATAL | Syntax errors | Fix dict/list structure |
| Wrong event names | FATAL | Typo in event name | Use correct event names |
| Invalid function paths | FATAL | Wrong dotted path | Verify path exists |
| v16-only hooks on v14/v15 | ERROR | extend_doctype_class | Use doc_events instead |
| Missing required_apps | WARNING | No dependency declaration | Add all dependencies |
| Check | Severity | Pattern | Fix |
|---|---|---|---|
| No migrate after hooks | FATAL | hooks.py changed, no migrate | Run bench migrate |
| Wrong bench command syntax | ERROR | Incorrect CLI args | Check frappe-ops-bench |
| Missing backup before upgrade | ERROR | Upgrade without backup | ALWAYS backup first |
| Production without supervisor | WARNING | No process manager | Use supervisor/systemd |
| No SSL in production | WARNING | HTTP-only deployment | Configure SSL/TLS |
| Check | Severity | Pattern | Fix |
|---|---|---|---|
| Missing mandatory fields | ERROR | No primary identifier | Add name or autoname |
| Duplicate fieldnames | FATAL | Same fieldname twice | Use unique fieldnames |
| Wrong fieldtype for data | WARNING | Text for short values | Use Data/Small Text |
| No permissions defined | WARNING | Empty permission list | Add role permissions |
# VALIDATE: Mixin class MUST call super()
class CustomSalesInvoice(SalesInvoice):
def validate(self):
super().validate() # REQUIRED - never skip
self.custom_validation()
def on_submit(self):
super().on_submit() # REQUIRED - never skip
self.custom_on_submit()# v16 recommended pattern
def get_customer_balance(customer: str) -> float:
...
# Validate: type hints on public API methods
@frappe.whitelist()
def process_order(order_name: str, action: str = "approve") -> dict:
...# Validate: sensitive fields should use data masking
# Check if PII fields have mask_with configured in DocType JSON| Check | Severity | Description |
|---|---|---|
| SQL Injection | CRITICAL | Raw user input in SQL |
| Permission bypass | CRITICAL | Missing permission checks |
| XSS vulnerability | HIGH | Unescaped user input in HTML |
| Sensitive data exposure | HIGH | Logging passwords/tokens |
| Hardcoded credentials | CRITICAL | API keys in source code |
| Check | Severity | Description |
|---|---|---|
| Query in loop | HIGH | frappe.db.* inside for loop |
| Unbounded query | MEDIUM | SELECT without LIMIT |
| Unnecessary get_doc | LOW | get_doc when get_value suffices |
| Missing index | MEDIUM | Filter on non-indexed field |
| No batch commit | HIGH | Commit per record in bulk ops |
| Check | Severity | Description |
|---|---|---|
| Silent failures | HIGH | except: pass without logging |
| Missing user feedback | MEDIUM | Errors not shown to user |
| Generic error messages | LOW | "An error occurred" |
| No rollback on failure | HIGH | Partial data on error |
ALWAYS generate reports in this format:
## Code Validation Report
### Code Type: [type]
### Target: [DocType / App / File]
### Event/Trigger: [if applicable]
### CRITICAL ERRORS (Must Fix)
| # | Line | Issue | Fix |
|---|------|-------|-----|
### WARNINGS (Should Fix)
| # | Line | Issue | Recommendation |
|---|------|-------|----------------|
### SUGGESTIONS (Nice to Have)
| # | Line | Suggestion |
|---|------|------------|
### Corrected Code
[If critical errors found, provide corrected version]
### Version Compatibility
| Version | Status | Notes |
|---------|--------|-------|
| v14 | [status] | |
| v15 | [status] | |
| v16 | [status] | |
### Referenced Skills
- frappe-skill-name: [what was validated against]| Level | Checks | Use When |
|---|---|---|
| Quick | Fatal errors only | Initial scan |
| Standard | + Warnings + Security | Pre-deployment (DEFAULT) |
| Deep | + Suggestions + Performance + Ops | Production review |
This validator validates against ALL 61 frappe-* skills:
frappe-syntax-clientscripts, frappe-syntax-serverscripts, frappe-syntax-controllers, frappe-syntax-hooks, frappe-syntax-hooks-events, frappe-syntax-whitelisted, frappe-syntax-jinja, frappe-syntax-scheduler, frappe-syntax-customapp, frappe-syntax-doctypes, frappe-syntax-reports
frappe-impl-clientscripts, frappe-impl-serverscripts, frappe-impl-controllers, frappe-impl-hooks, frappe-impl-whitelisted, frappe-impl-jinja, frappe-impl-scheduler, frappe-impl-customapp, frappe-impl-reports, frappe-impl-workflow, frappe-impl-website, frappe-impl-ui-components, frappe-impl-integrations
frappe-errors-clientscripts, frappe-errors-serverscripts, frappe-errors-controllers, frappe-errors-hooks, frappe-errors-api, frappe-errors-permissions, frappe-errors-database
frappe-core-database, frappe-core-permissions, frappe-core-api, frappe-core-workflow, frappe-core-notifications, frappe-core-files, frappe-core-cache
frappe-ops-bench, frappe-ops-deployment, frappe-ops-backup, frappe-ops-performance, frappe-ops-upgrades, frappe-ops-cloud, frappe-ops-app-lifecycle, frappe-ops-frontend-build
frappe-testing-unit, frappe-testing-cicd
import statements? --> FATALself. references? --> FATAL (use doc.)try/except? --> WARNING (usually wrong)frappe.throw() for validation? --> GOODdoc.field for access? --> GOODfrappe.db.* calls? --> FATALfrappe.get_doc() calls? --> FATALfrappe.call() without callback? --> FATALfrm.doc.field for access? --> GOODfrm.refresh_field() after changes? --> GOODself.* in on_update? --> FATALsuper().method() calls? --> ERRORself.save() in lifecycle hook? --> FATALbench migrate after changes? --> REQUIREDSee references/checklists.md for complete checklists. See references/examples.md for validation examples.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.