@salesforce/afv-skills 1.28.0 → 1.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/package.json +1 -1
- package/skills/agentforce-architecture-analyze/README.md +2 -8
- package/skills/agentforce-architecture-analyze/SKILL.md +2 -2
- package/skills/agentforce-architecture-analyze/references/contract.json +4 -4
- package/skills/agentforce-architecture-analyze/references/soql_fields.md +1 -1
- package/skills/data360-activate/README.md +0 -1
- package/skills/data360-activate/SKILL.md +0 -2
- package/skills/data360-connect/README.md +0 -2
- package/skills/data360-connect/SKILL.md +0 -2
- package/skills/data360-harmonize/SKILL.md +0 -1
- package/skills/data360-orchestrate/README.md +0 -7
- package/skills/data360-orchestrate/SKILL.md +0 -2
- package/skills/data360-prepare/README.md +0 -1
- package/skills/data360-prepare/SKILL.md +0 -1
- package/skills/data360-query/README.md +0 -1
- package/skills/data360-query/SKILL.md +0 -1
- package/skills/data360-segment/README.md +0 -1
- package/skills/data360-segment/SKILL.md +0 -2
- package/skills/dx-code-analyzer-configure/SKILL.md +31 -13
- package/skills/dx-code-analyzer-custom-rule-create/SKILL.md +484 -0
- package/skills/dx-code-analyzer-custom-rule-create/assets/pmd-ruleset-template.xml +31 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-fields-api.md +87 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-flows.md +105 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-permissions.md +95 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-examples.md +84 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/regex-examples.md +127 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/xpath-examples.md +227 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/advanced-pmd-patterns.md +288 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/apex-ast-reference.md +127 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/eslint-custom-plugins.md +247 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/eslint-rules-discovery.md +188 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/eslint-tier2-configurable.md +114 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/eslint-tier3-custom-plugins.md +113 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/metadata-xml-rules.md +285 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/regex-rule-schema.md +174 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/troubleshooting.md +141 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-governor-limits.md +83 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-method-calls.md +108 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-security.md +45 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-structure.md +127 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns.md +131 -0
- package/skills/dx-code-analyzer-custom-rule-create/scripts/create-pmd-rule.js +209 -0
- package/skills/dx-code-analyzer-custom-rule-create/scripts/create-regex-rule.js +220 -0
- package/skills/dx-code-analyzer-run/SKILL.md +41 -8
- package/skills/experience-cms-brand-apply/SKILL.md +47 -16
- package/skills/experience-lwc-generate/README.md +3 -2
- package/skills/experience-lwc-generate/references/async-notification-patterns.md +1 -1
- package/skills/experience-lwc-generate/references/component-patterns.md +3 -3
- package/skills/experience-lwc-generate/references/flow-integration-guide.md +1 -1
- package/skills/experience-lwc-generate/references/lwc-best-practices.md +4 -4
- package/skills/experience-lwc-generate/references/scoring-and-testing.md +1 -1
- package/skills/experience-lwc-generate/references/state-management.md +2 -2
- package/skills/experience-lwc-generate/references/triangle-pattern.md +3 -3
- package/skills/experience-lwc-generate/scripts/post-tool-validate.py +2 -2
- package/skills/experience-lwc-generate/scripts/template_validator.py +1 -1
- package/skills/external-diagram-mermaid-generate/README.md +2 -2
- package/skills/external-diagram-mermaid-generate/SKILL.md +1 -3
- package/skills/external-diagram-mermaid-generate/assets/agentforce/agent-flow.md +4 -4
- package/skills/external-diagram-mermaid-generate/assets/architecture/system-landscape.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/datamodel/b2b-commerce-erd.md +3 -3
- package/skills/external-diagram-mermaid-generate/assets/datamodel/campaigns-erd.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/datamodel/consent-erd.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/datamodel/files-erd.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/datamodel/forecasting-erd.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/datamodel/fsl-erd.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/datamodel/party-model-erd.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/datamodel/quote-order-erd.md +3 -3
- package/skills/external-diagram-mermaid-generate/assets/datamodel/revenue-cloud-erd.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/datamodel/sales-cloud-erd.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/datamodel/salesforce-erd.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/datamodel/scheduler-erd.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/datamodel/service-cloud-erd.md +3 -3
- package/skills/external-diagram-mermaid-generate/assets/datamodel/territory-management-erd.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/integration/api-sequence.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/oauth/authorization-code-pkce.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/oauth/authorization-code.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/oauth/client-credentials.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/oauth/device-authorization.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/oauth/jwt-bearer.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/oauth/refresh-token.md +1 -1
- package/skills/external-diagram-mermaid-generate/assets/oauth/user-agent-social-sign-on.md +2 -2
- package/skills/external-diagram-mermaid-generate/assets/role-hierarchy/user-hierarchy.md +1 -1
- package/skills/external-diagram-mermaid-generate/references/color-palette.md +13 -13
- package/skills/external-diagram-mermaid-generate/references/diagram-conventions.md +5 -5
- package/skills/external-diagram-mermaid-generate/references/erd-conventions.md +1 -1
- package/skills/external-diagram-mermaid-generate/references/mermaid-styling.md +2 -2
- package/skills/external-diagram-mermaid-generate/references/preview-guide.md +1 -1
- package/skills/external-diagram-mermaid-generate/references/usage-examples.md +9 -9
- package/skills/external-diagram-mermaid-generate/scripts/README.md +1 -1
- package/skills/integration-connectivity-connected-app-configure/README.md +4 -5
- package/skills/integration-connectivity-connected-app-configure/SKILL.md +2 -2
- package/skills/integration-connectivity-connected-app-configure/references/example-usage.md +7 -7
- package/skills/integration-connectivity-connected-app-configure/references/migration-guide.md +1 -1
- package/skills/integration-connectivity-connected-app-configure/references/oauth-flows-reference.md +1 -1
- package/skills/integration-connectivity-connected-app-configure/references/testing-validation-guide.md +3 -3
- package/skills/integration-connectivity-generate/README.md +1 -1
- package/skills/integration-connectivity-generate/SKILL.md +6 -0
- package/skills/integration-connectivity-generate/assets/callouts/rest-queueable-callout.cls +1 -1
- package/skills/integration-connectivity-generate/assets/external-services/external-service-operations.md +1 -1
- package/skills/integration-connectivity-generate/assets/soap/wsdl2apex-guide.md +1 -1
- package/skills/integration-connectivity-generate/references/callout-patterns.md +4 -0
- package/skills/integration-connectivity-generate/references/cdc-guide.md +2 -2
- package/skills/integration-connectivity-generate/references/event-driven-architecture-guide.md +5 -5
- package/skills/integration-connectivity-generate/references/event-patterns.md +3 -2
- package/skills/integration-connectivity-generate/references/external-services-guide.md +4 -1
- package/skills/integration-connectivity-generate/references/messaging-api-v2.md +2 -2
- package/skills/integration-connectivity-generate/references/named-credentials-automation.md +1 -1
- package/skills/integration-connectivity-generate/references/named-credentials-guide.md +12 -2
- package/skills/integration-connectivity-generate/references/platform-events-guide.md +3 -3
- package/skills/integration-connectivity-generate/references/rest-callout-patterns.md +1 -1
- package/skills/integration-connectivity-generate/references/scoring-rubric.md +1 -1
- package/skills/integration-connectivity-generate/references/security-best-practices.md +5 -2
- package/skills/integration-connectivity-generate/scripts/configure-named-credential.sh +16 -5
- package/skills/integration-connectivity-generate/scripts/set-api-credential.sh +11 -2
- package/skills/integration-connectivity-generate/scripts/suggest_credential_setup.py +4 -3
- package/skills/integration-connectivity-generate/scripts/validate_integration.py +1 -1
- package/skills/mobile-platform-native-capabilities-integrate/SKILL.md +3 -3
- package/skills/omnistudio-callable-apex-generate/README.md +1 -5
- package/skills/omnistudio-callable-apex-generate/SKILL.md +14 -4
- package/skills/omnistudio-callable-apex-generate/assets/pattern_callable_vanilla.cls +8 -1
- package/skills/omnistudio-callable-apex-generate/examples/Test_VlocityOpenInterface2Conversion/TRANSCRIPT.md +1 -1
- package/skills/omnistudio-callable-apex-generate/examples/Test_VlocityOpenInterfaceConversion/TRANSCRIPT.md +1 -1
- package/skills/omnistudio-datamapper-generate/SKILL.md +1 -1
- package/skills/omnistudio-datamapper-generate/assets/completion-summary-template.md +2 -2
- package/skills/omnistudio-datamapper-generate/references/best-practices.md +9 -9
- package/skills/omnistudio-datamapper-generate/references/naming-conventions.md +6 -6
- package/skills/omnistudio-datapacks-deploy/README.md +1 -1
- package/skills/omnistudio-datapacks-deploy/examples/business-internet-plus-bundle/deploy-business-internet-plus-bundle.yaml +1 -1
- package/skills/omnistudio-dependencies-analyze/SKILL.md +12 -5
- package/skills/omnistudio-dependencies-analyze/references/dependency-patterns.md +9 -9
- package/skills/omnistudio-dependencies-analyze/references/namespace-guide.md +1 -1
- package/skills/omnistudio-epc-catalog-generate/README.md +1 -1
- package/skills/omnistudio-flexcard-generate/SKILL.md +11 -4
- package/skills/omnistudio-flexcard-generate/references/best-practices.md +6 -6
- package/skills/omnistudio-flexcard-generate/references/data-binding-guide.md +9 -9
- package/skills/omnistudio-integration-procedure-generate/SKILL.md +9 -1
- package/skills/omnistudio-integration-procedure-generate/references/element-types.md +3 -3
- package/skills/omnistudio-omniscript-generate/SKILL.md +2 -2
- package/skills/omnistudio-omniscript-generate/references/best-practices.md +1 -1
- package/skills/omnistudio-omniscript-generate/scripts/check-duplicate-omniscript.sh +8 -0
- package/skills/omnistudio-omniscript-generate/scripts/deploy-omniscript.sh +8 -0
- package/skills/platform-apex-logs-debug/README.md +2 -2
- package/skills/platform-apex-logs-debug/references/cli-commands.md +2 -2
- package/skills/platform-apex-logs-debug/references/debug-log-reference.md +16 -16
- package/skills/platform-apex-logs-debug/references/log-analysis-tools.md +2 -2
- package/skills/platform-apex-test-run/README.md +1 -1
- package/skills/platform-apex-test-run/references/test-fix-loop.md +2 -2
- package/skills/platform-apex-test-run/references/testing-best-practices.md +3 -3
- package/skills/platform-custom-field-generate/SKILL.md +86 -126
- package/skills/platform-custom-field-generate/references/advanced-picklists.md +590 -0
- package/skills/platform-data-manage/README.md +4 -4
- package/skills/platform-data-manage/references/bulk-testing-example.md +1 -1
- package/skills/platform-data-manage/references/crud-workflow-example.md +2 -2
- package/skills/platform-data-manage/references/orchestration.md +6 -6
- package/skills/platform-flexipage-generate/SKILL.md +281 -346
- package/skills/platform-flexipage-generate/references/cli_commands.md +39 -0
- package/skills/platform-flexipage-generate/references/flexipage_fieldSection.md +162 -0
- package/skills/platform-flexipage-generate/references/flexipage_richText.md +84 -0
- package/skills/platform-flexipage-generate/references/identifiers_and_regions.md +167 -0
- package/skills/platform-flexipage-generate/references/mcp_action_examples.md +104 -0
- package/skills/platform-flexipage-generate/references/record_flexipage_dynamicHighlights.md +119 -0
- package/skills/platform-flexipage-generate/references/xml_rules.md +335 -0
- package/skills/platform-flexipage-generate/scripts/resolve-component-instructions.sh +42 -0
- package/skills/platform-flexipage-generate/scripts/scan-lwc-components.sh +113 -0
- package/skills/platform-metadata-deploy/README.md +2 -2
- package/skills/platform-metadata-deploy/references/agent-deployment-guide.md +2 -2
- package/skills/platform-metadata-deploy/references/deployment-workflows.md +16 -16
- package/skills/platform-metadata-deploy/references/orchestration.md +3 -3
- package/skills/platform-metadata-deploy/references/trigger-deployment-safety.md +2 -2
- package/skills/platform-models-api-configure/SKILL.md +140 -0
- package/skills/platform-models-api-configure/scripts/get-orgjwt.sh +48 -0
- package/skills/platform-policy-rule-generate/SKILL.md +260 -0
- package/skills/platform-policy-rule-generate/references/deploy-errors.md +54 -0
- package/skills/platform-policy-rule-generate/references/fixtures-index.md +29 -0
- package/skills/platform-policy-rule-generate/references/policy-schema-full.md +250 -0
- package/skills/platform-policy-rule-generate/references/templates-access.md +132 -0
- package/skills/platform-policy-rule-generate/references/templates-advanced.md +58 -0
- package/skills/platform-policy-rule-generate/references/templates-record.md +75 -0
- package/skills/platform-policy-rule-generate/references/templates-transform.md +70 -0
- package/skills/platform-policy-rule-generate/references/templates.md +10 -0
- package/skills/platform-policy-rule-generate/references/ui-compatibility.md +142 -0
- package/skills/platform-soql-query/README.md +1 -1
- package/skills/platform-soql-query/SKILL.md +1 -1
- package/skills/platform-soql-query/references/anti-patterns.md +1 -1
- package/skills/platform-soql-query/references/field-coverage-rules.md +1 -1
- package/skills/platform-soql-query/references/query-optimization.md +1 -1
- package/skills/platform-soql-query/scripts/post-tool-validate.py +3 -3
- package/skills/platform-value-set-generate/SKILL.md +305 -0
- package/skills/data360-activate/CREDITS.md +0 -5
- package/skills/data360-connect/CREDITS.md +0 -5
- package/skills/data360-harmonize/CREDITS.md +0 -3
- package/skills/data360-orchestrate/CREDITS.md +0 -15
- package/skills/data360-orchestrate/UPSTREAM.md +0 -45
- package/skills/data360-prepare/CREDITS.md +0 -7
- package/skills/data360-query/CREDITS.md +0 -7
- package/skills/data360-segment/CREDITS.md +0 -3
- package/skills/experience-lwc-generate/CREDITS.md +0 -5
- package/skills/external-diagram-mermaid-generate/CREDITS.md +0 -46
- package/skills/external-diagram-visual-generate/CREDITS.md +0 -80
- package/skills/external-diagram-visual-generate/README.md +0 -83
- package/skills/external-diagram-visual-generate/SKILL.md +0 -207
- package/skills/external-diagram-visual-generate/assets/architecture/integration-flow.md +0 -55
- package/skills/external-diagram-visual-generate/assets/erd/core-objects.md +0 -131
- package/skills/external-diagram-visual-generate/assets/erd/custom-objects.md +0 -60
- package/skills/external-diagram-visual-generate/assets/lwc/dashboard-card.md +0 -45
- package/skills/external-diagram-visual-generate/assets/lwc/data-table.md +0 -57
- package/skills/external-diagram-visual-generate/assets/lwc/record-form.md +0 -60
- package/skills/external-diagram-visual-generate/assets/review/apex-review.md +0 -57
- package/skills/external-diagram-visual-generate/assets/review/lwc-review.md +0 -48
- package/skills/external-diagram-visual-generate/references/architect-aesthetic-guide.md +0 -257
- package/skills/external-diagram-visual-generate/references/examples-index.md +0 -35
- package/skills/external-diagram-visual-generate/references/gemini-cli-setup.md +0 -65
- package/skills/external-diagram-visual-generate/references/interview-questions.md +0 -529
- package/skills/external-diagram-visual-generate/references/iteration-workflow.md +0 -173
- package/skills/external-diagram-visual-generate/scripts/check-prerequisites.sh +0 -101
- package/skills/external-diagram-visual-generate/scripts/generate_image.py +0 -243
- package/skills/integration-connectivity-connected-app-configure/CREDITS.md +0 -3
- package/skills/integration-connectivity-generate/CREDITS.md +0 -5
- package/skills/omnistudio-callable-apex-generate/CREDITS.md +0 -9
- package/skills/omnistudio-datamapper-generate/CREDITS.md +0 -5
- package/skills/omnistudio-datapacks-deploy/CREDITS.md +0 -5
- package/skills/omnistudio-dependencies-analyze/CREDITS.md +0 -5
- package/skills/omnistudio-epc-catalog-generate/CREDITS.md +0 -14
- package/skills/omnistudio-flexcard-generate/CREDITS.md +0 -5
- package/skills/omnistudio-integration-procedure-generate/CREDITS.md +0 -5
- package/skills/omnistudio-omniscript-generate/CREDITS.md +0 -5
- package/skills/platform-apex-generate/CREDITS.md +0 -5
- package/skills/platform-apex-logs-debug/CREDITS.md +0 -22
- package/skills/platform-apex-test-generate/CREDITS.md +0 -5
- package/skills/platform-apex-test-run/CREDITS.md +0 -22
- package/skills/platform-data-manage/CREDITS.md +0 -5
- package/skills/platform-metadata-deploy/CREDITS.md +0 -25
- package/skills/platform-soql-query/CREDITS.md +0 -21
- /package/skills/omnistudio-callable-apex-generate/examples/Test_VlocityOpenInterfaceConversion/{MyCustomVlocityOpenInterface2.cls → MyCustomClass.cls} +0 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Regex Rule Schema Reference
|
|
2
|
+
|
|
3
|
+
Custom regex rules are defined inline in `code-analyzer.yml` under `engines.regex.custom_rules`.
|
|
4
|
+
|
|
5
|
+
## Complete Schema
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
engines:
|
|
9
|
+
regex:
|
|
10
|
+
custom_rules:
|
|
11
|
+
RuleName: # Must match: /^[A-Za-z@][A-Za-z_0-9@\-/]*$/
|
|
12
|
+
regex: "/pattern/flags" # REQUIRED — JavaScript regex literal format
|
|
13
|
+
description: "What this checks" # REQUIRED — Purpose of the rule
|
|
14
|
+
violation_message: "Fix this" # Optional — Message shown on violation
|
|
15
|
+
severity: 3 # Optional — 1=Critical, 2=High, 3=Moderate, 4=Low, 5=Info
|
|
16
|
+
tags: # Optional — Default: ['Recommended', 'Custom']
|
|
17
|
+
- "Custom"
|
|
18
|
+
- "Security"
|
|
19
|
+
file_extensions: # Optional — Default: all text files
|
|
20
|
+
- ".cls"
|
|
21
|
+
- ".trigger"
|
|
22
|
+
regex_ignore: "/exception/i" # Optional — Matches excluded from violations
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Field Details
|
|
26
|
+
|
|
27
|
+
### `regex` (Required)
|
|
28
|
+
|
|
29
|
+
JavaScript regex literal format: `/pattern/flags`
|
|
30
|
+
|
|
31
|
+
| Format | Example | Meaning |
|
|
32
|
+
|--------|---------|---------|
|
|
33
|
+
| `/pattern/g` | `/TODO/g` | Case-sensitive, global (find all matches) — **minimum required** |
|
|
34
|
+
| `/pattern/gi` | `/todo/gi` | Global + case-insensitive |
|
|
35
|
+
| `/pattern/gm` | `/^import/gm` | Global + multiline (^ matches each line start) |
|
|
36
|
+
|
|
37
|
+
**Common pitfalls:**
|
|
38
|
+
- **The `/g` (global) flag is REQUIRED.** Code Analyzer rejects regex patterns without at least one flag after the closing `/`. Always include `/g` (or `/gi`, `/gm`, etc.).
|
|
39
|
+
- Backslashes must be escaped: `\d` → write as `/\\d/`
|
|
40
|
+
- Dots match any character: use `/\\.cls/` not `/.cls/`
|
|
41
|
+
- Regex is tested against file content line by line
|
|
42
|
+
|
|
43
|
+
**Named capture groups** narrow the violation highlight to a specific portion of the match:
|
|
44
|
+
```yaml
|
|
45
|
+
# Without named group — entire match is highlighted:
|
|
46
|
+
regex: "/System\\.debug\\([^)]*\\)/g"
|
|
47
|
+
# Highlights: "System.debug('hello')" (entire expression)
|
|
48
|
+
|
|
49
|
+
# With named group — only captured portion highlighted:
|
|
50
|
+
regex: "/(?<target>System\\.debug)\\([^)]*\\)/g"
|
|
51
|
+
# Highlights: "System.debug" (just the method name)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Use `(?<target>...)` when the regex needs surrounding context to match correctly, but the violation should point to a narrower location. Common patterns:
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
# Highlight just the hardcoded ID, not the surrounding quotes
|
|
58
|
+
regex: "/['\"](?<target>[0-9a-zA-Z]{15,18})['\"]/g"
|
|
59
|
+
|
|
60
|
+
# Highlight the annotation, not the whole line
|
|
61
|
+
regex: "/(?<target>@SuppressWarnings\\([^)]*\\))/g"
|
|
62
|
+
|
|
63
|
+
# Highlight the method call within a larger expression
|
|
64
|
+
regex: "/(?<target>Database\\.query)\\(/g"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### `description` (Required)
|
|
68
|
+
|
|
69
|
+
Brief explanation of what the rule enforces. Used in rule listings and generated messages.
|
|
70
|
+
|
|
71
|
+
### `violation_message` (Optional)
|
|
72
|
+
|
|
73
|
+
Message shown when a violation is found. If omitted, auto-generated as:
|
|
74
|
+
> "A match of the regular expression `<regex>` was found for rule '<RuleName>': <description>"
|
|
75
|
+
|
|
76
|
+
Good violation messages tell the user HOW to fix, not just WHAT's wrong:
|
|
77
|
+
- ❌ "Hardcoded ID found"
|
|
78
|
+
- ✅ "Replace hardcoded Salesforce ID with a Custom Label or Custom Metadata reference"
|
|
79
|
+
|
|
80
|
+
### `severity` (Optional, default: 3)
|
|
81
|
+
|
|
82
|
+
| Value | Name | Meaning |
|
|
83
|
+
|-------|------|---------|
|
|
84
|
+
| 1 | Critical | Security vulnerability, must fix before deploy |
|
|
85
|
+
| 2 | High | Significant quality issue, should fix |
|
|
86
|
+
| 3 | Moderate | Recommended improvement |
|
|
87
|
+
| 4 | Low | Minor style issue |
|
|
88
|
+
| 5 | Info | Informational, no action required |
|
|
89
|
+
|
|
90
|
+
Also accepts string names: `"Critical"`, `"High"`, `"Moderate"`, `"Low"`, `"Info"`
|
|
91
|
+
|
|
92
|
+
### `tags` (Optional, default: ['Recommended', 'Custom'])
|
|
93
|
+
|
|
94
|
+
Array of tag strings. Used for `--rule-selector` filtering. Common tags:
|
|
95
|
+
- `Custom` — auto-applied to user-created rules
|
|
96
|
+
- `Security` — security-related rules
|
|
97
|
+
- `BestPractices` — coding standards
|
|
98
|
+
- `Performance` — performance anti-patterns
|
|
99
|
+
- `CodeStyle` — formatting/naming rules
|
|
100
|
+
|
|
101
|
+
### `file_extensions` (Optional, default: all text files)
|
|
102
|
+
|
|
103
|
+
Array of file extensions to scan. Each must start with `.` and match: `/^([.][a-zA-Z0-9-_]+)+$/`
|
|
104
|
+
|
|
105
|
+
Common values for Salesforce:
|
|
106
|
+
- `.cls` — Apex classes
|
|
107
|
+
- `.trigger` — Apex triggers
|
|
108
|
+
- `.js` — JavaScript/LWC
|
|
109
|
+
- `.html` — LWC HTML templates
|
|
110
|
+
- `.xml` — Metadata files
|
|
111
|
+
- `.page` — Visualforce pages
|
|
112
|
+
- `.component` — Visualforce components
|
|
113
|
+
|
|
114
|
+
### `regex_ignore` (Optional)
|
|
115
|
+
|
|
116
|
+
A second regex pattern — matches that ALSO match this pattern are excluded. Useful for reducing false positives.
|
|
117
|
+
|
|
118
|
+
⚠️ **`regex_ignore` operates per-LINE, not per-FILE.** It only skips a match if the same line also matches the ignore pattern. It does NOT exclude entire files or classes. For example, adding `/@isTest/` only suppresses violations on lines that literally contain `@isTest` — a hardcoded ID on line 50 of a test class still flags because line 50 doesn't contain `@isTest`.
|
|
119
|
+
|
|
120
|
+
To exclude entire files, use `ignores.files` in `code-analyzer.yml`:
|
|
121
|
+
```yaml
|
|
122
|
+
ignores:
|
|
123
|
+
files:
|
|
124
|
+
- "**/*Test.cls"
|
|
125
|
+
- "**/test/**"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```yaml
|
|
129
|
+
# Flag System.debug everywhere EXCEPT in lines with "// OK" comment
|
|
130
|
+
regex: "/System\\.debug/g"
|
|
131
|
+
regex_ignore: "/\\/\\/ OK/"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Validation Rules
|
|
135
|
+
|
|
136
|
+
| Rule | Error if violated |
|
|
137
|
+
|------|-------------------|
|
|
138
|
+
| Name matches `/^[A-Za-z@][A-Za-z_0-9@\-/]*$/` | "Invalid rule name" |
|
|
139
|
+
| Regex starts and ends with `/` | "Invalid regex format" |
|
|
140
|
+
| File extensions start with `.` | "Invalid file extension" |
|
|
141
|
+
| Severity is 1-5 or valid name | "Invalid severity" |
|
|
142
|
+
| No duplicate rule names | "Rule already exists" |
|
|
143
|
+
|
|
144
|
+
## Multiple Rules Example
|
|
145
|
+
|
|
146
|
+
```yaml
|
|
147
|
+
engines:
|
|
148
|
+
regex:
|
|
149
|
+
custom_rules:
|
|
150
|
+
NoHardcodedIds:
|
|
151
|
+
regex: "/['\"][0-9a-zA-Z]{15,18}['\"]/g"
|
|
152
|
+
description: "Detects hardcoded Salesforce record IDs"
|
|
153
|
+
violation_message: "Use Custom Labels or Custom Metadata instead of hardcoded IDs"
|
|
154
|
+
severity: 2
|
|
155
|
+
tags: ["Custom", "Security"]
|
|
156
|
+
file_extensions: [".cls", ".trigger"]
|
|
157
|
+
|
|
158
|
+
NoTodos:
|
|
159
|
+
regex: "/TODO|FIXME|HACK/gi"
|
|
160
|
+
description: "Flags TODO/FIXME/HACK comments that should be resolved"
|
|
161
|
+
violation_message: "Resolve or remove this TODO/FIXME/HACK before merging"
|
|
162
|
+
severity: 4
|
|
163
|
+
tags: ["Custom", "BestPractices"]
|
|
164
|
+
|
|
165
|
+
NoSuppressWarnings:
|
|
166
|
+
regex: "/(?<target>@SuppressWarnings\\([^)]*\\))/g"
|
|
167
|
+
description: "Bans @SuppressWarnings annotations — violations must be fixed, not suppressed"
|
|
168
|
+
violation_message: "Remove @SuppressWarnings and fix the underlying violation instead"
|
|
169
|
+
severity: 2
|
|
170
|
+
tags: ["Recommended", "Custom", "BestPractices"]
|
|
171
|
+
file_extensions: [".cls", ".trigger"]
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
⚠️ **Note on test-class exclusion:** The `regex_ignore` field is per-LINE only. Adding `regex_ignore: "/@isTest/i"` does NOT exclude entire test classes — it only skips lines that literally contain `@isTest`. For SOQL rules that should skip test classes, use PMD/XPath instead (which can structurally check `[not(ancestor::UserClass[ModifierNode[@Test = true()]])]`). See the "Excluding Test Classes" section in SKILL.md.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Troubleshooting Custom Rules
|
|
2
|
+
|
|
3
|
+
## Regex Rule Issues
|
|
4
|
+
|
|
5
|
+
| Problem | Cause | Fix |
|
|
6
|
+
|---------|-------|-----|
|
|
7
|
+
| "Invalid rule name" | Name starts with number or contains spaces | Use PascalCase: `NoHardcodedIds`, `BanTodoComments` |
|
|
8
|
+
| "Invalid regex" | Missing `/` delimiters | Must be `/pattern/flags` format |
|
|
9
|
+
| YAML parse error when adding regex | Quotes/backslashes in regex conflict with YAML syntax | **Use `create-regex-rule.js` script** — do not manually write regex into YAML. The script handles serialization correctly. |
|
|
10
|
+
| Rule not listed | YAML indentation wrong | Must be under `engines.regex.custom_rules` with correct nesting |
|
|
11
|
+
| No violations found | Regex doesn't match content | Test regex at regex101.com with your file content |
|
|
12
|
+
| Matches in wrong files | `file_extensions` not set | Add `file_extensions: [".cls"]` to limit scope |
|
|
13
|
+
| Matches in comments | Regex is text-based | Consider using PMD/XPath instead for code-aware matching |
|
|
14
|
+
| Too many matches | Regex too broad | Tighten pattern first (e.g., require leading `0` for IDs), then add `regex_ignore` for remaining edge cases |
|
|
15
|
+
| `regex_ignore` doesn't exclude test classes | `regex_ignore` is per-LINE, not per-FILE | It only skips lines where the ignore pattern matches. To exclude entire test files, use `ignores.files: ["**/*Test.cls"]` in `code-analyzer.yml` |
|
|
16
|
+
| Hardcoded ID regex flags normal words | Pattern `{15,18}` matches 16/17 char words | Salesforce IDs are exactly 15 OR 18 chars, start with `0`. Use: `0[a-zA-Z0-9]{14}(?:[a-zA-Z0-9]{3})?` |
|
|
17
|
+
| Special chars not working | YAML escaping issue | Use `create-regex-rule.js` script to avoid escaping issues. If manual: double-escape (`\\d` → `\d`) |
|
|
18
|
+
|
|
19
|
+
### Common Regex Escaping in YAML
|
|
20
|
+
|
|
21
|
+
| Character | In YAML | In regex |
|
|
22
|
+
|-----------|---------|----------|
|
|
23
|
+
| Backslash | `\\\\` | `\\` |
|
|
24
|
+
| Dot | `\\.` | `.` as literal |
|
|
25
|
+
| Quote | `\\"` or use single quotes | `"` |
|
|
26
|
+
| Newline | `\\n` | Line break |
|
|
27
|
+
|
|
28
|
+
## PMD/XPath Rule Issues
|
|
29
|
+
|
|
30
|
+
| Problem | Cause | Fix |
|
|
31
|
+
|---------|-------|-----|
|
|
32
|
+
| "Could not locate Java" | Java not installed or not on PATH | Install Java 11+ or set `engines.pmd.java_command` in config |
|
|
33
|
+
| "Language not supported" | Invalid language identifier | Use: apex, visualforce, html, xml, javascript |
|
|
34
|
+
| "File not found" for ruleset | Wrong path in `custom_rulesets` | Path is relative to `code-analyzer.yml` location |
|
|
35
|
+
| Rule not listed after creation | XML format wrong | Verify against template in `assets/pmd-ruleset-template.xml` |
|
|
36
|
+
| XPath returns 0 matches | Node name or attribute wrong | Re-run `sf code-analyzer ast-dump` and compare |
|
|
37
|
+
| XPath too broad | Missing attribute filter | Add `[@attribute='value']` conditions |
|
|
38
|
+
| XPath `@WithSharing='false'` returns 0 matches | PMD 7 boolean attributes are always present, not string-typed | Use XPath boolean function: `@WithSharing = false()`. NOT string `'false'`, NOT `not(@WithSharing)`. Same for `@Abstract`, `@Final`, etc. |
|
|
39
|
+
| Loop rule flags `for (x : [SELECT...])` | Used `//ForEachStatement//SoqlExpression` (matches iterable) | Scope to body: `//ForEachStatement/BlockStatement//SoqlExpression` |
|
|
40
|
+
| PMD parse error on sample code | Invalid Apex syntax in sample | Ensure sample compiles (doesn't need to deploy) |
|
|
41
|
+
|
|
42
|
+
### Debugging XPath Step by Step
|
|
43
|
+
|
|
44
|
+
1. **Dump the AST:** `sf code-analyzer ast-dump --file sample.cls`
|
|
45
|
+
2. **Find the target node:** Search the XML for the pattern (e.g., grep for "System.debug")
|
|
46
|
+
3. **Note the exact node name:** e.g., `MethodCallExpression` (not `MethodCall`)
|
|
47
|
+
4. **Note the exact attribute:** e.g., `FullMethodName='System.debug'` (not `Name` or `Image`)
|
|
48
|
+
5. **Check ancestry:** What's the parent chain? Does your XPath's `ancestor::` match?
|
|
49
|
+
6. **Simplify first:** Start with `//MethodCallExpression` → verify it matches → then add filters
|
|
50
|
+
|
|
51
|
+
### Verifying Rule Registration
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Check if rule appears in the rule list
|
|
55
|
+
sf code-analyzer rules --rule-selector regex:MyRuleName
|
|
56
|
+
sf code-analyzer rules --rule-selector pmd:MyRuleName
|
|
57
|
+
|
|
58
|
+
# Check all custom rules
|
|
59
|
+
sf code-analyzer rules --rule-selector Custom
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Verifying Rule Execution
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Test against a file that SHOULD violate
|
|
66
|
+
sf code-analyzer run --rule-selector regex:MyRuleName --target ./sample-violation.cls
|
|
67
|
+
|
|
68
|
+
# Test against a file that should NOT violate (expect 0 results)
|
|
69
|
+
sf code-analyzer run --rule-selector regex:MyRuleName --target ./clean-file.cls
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Metadata XML Rule Issues
|
|
73
|
+
|
|
74
|
+
| Problem | Cause | Fix |
|
|
75
|
+
|---------|-------|-----|
|
|
76
|
+
| XPath matches nothing on metadata | Missing `local-name()` | MUST use `*[local-name()='element']` — namespace blocks bare names |
|
|
77
|
+
| XPath matches nothing (text values) | Used `text()` — broken in PMD 7 | Use `@Text` attribute: `//*[@Text='value']` |
|
|
78
|
+
| Rule fires on wrong file type | XPath not scoped to root element | Add root check or `ancestor::*[local-name()='PermissionSet']` |
|
|
79
|
+
| `text()` comparison always returns 0 | PMD 7 XML language change | Replace ALL `text()='value'` with `@Text='value'`. This is a PMD 7 breaking change. |
|
|
80
|
+
| `@Text` matches but can't navigate up | `@Text` matches TEXT NODES, not elements | Navigate up: `//*[@Text='value']/..` (element), `/../..` (grandparent) |
|
|
81
|
+
| `[@Text]` predicate on element returns 0 matches | `@Text` lives on CHILD text nodes, not on elements | Use `/*[@Text...]` to navigate to child text node. E.g., `//*[local-name()='apiVersion']/*[number(@Text) < 60]` — NOT `//*[local-name()='apiVersion'][number(@Text) < 60]` |
|
|
82
|
+
| Child predicate `[*[local-name()='x'][@Text='y']]` returns 0 | PMD 7 doesn't support `@Text` in child predicates | Use bottom-up navigation: start from `@Text` match, go up with `../..` |
|
|
83
|
+
| Number comparison doesn't work | Need to use `@Text` not `text()` | `number(substring-before(@Text, '.'))` |
|
|
84
|
+
| Rule doesn't fire on metadata files | PMD only scans `.xml` extension by default | Add `file_extensions: { xml: [".xml"] }` under `engines.pmd`. This covers all compound extensions (`.permissionset-meta.xml`, etc.). Do NOT add compound extensions — the validator rejects them. |
|
|
85
|
+
| "Too many violations" on metadata | Rule too broad, scanning all XML | Scope to specific file types via root element check |
|
|
86
|
+
| ast-dump for XML fails with "XmlEncoding" error | Known bug in some versions | Read the raw XML file — the file content IS the DOM structure |
|
|
87
|
+
| ast-dump for XML shows flat DOM | Expected — XML AST is the literal DOM tree | Elements, text nodes, and attributes — not like Apex AST |
|
|
88
|
+
|
|
89
|
+
### Debugging Metadata XML XPath (PMD 7)
|
|
90
|
+
|
|
91
|
+
1. **Dump the AST** (or read raw file if ast-dump fails): `sf code-analyzer ast-dump --file MyField.field-meta.xml --language xml`
|
|
92
|
+
2. **Check namespace:** If file has `xmlns="..."`, ALL XPath must use `local-name()`
|
|
93
|
+
3. **Use `@Text` for text values:** Never use `text()` — it does not work in PMD 7 XML
|
|
94
|
+
4. **Start from the text node, navigate up:** `//*[@Text='TargetValue']/../..` to reach parent elements
|
|
95
|
+
5. **Test with `//*` first:** Confirm PMD is scanning the file at all (should return many violations)
|
|
96
|
+
6. **Test `@Text` matching:** `//*[@Text='YourValue']` — confirm the text node is found
|
|
97
|
+
7. **Add navigation one step at a time:** `../..`, then `[local-name()='parent']`, then sibling checks
|
|
98
|
+
8. **Check file extensions:** Ensure `file_extensions: { xml: [".xml"] }` is in your `engines.pmd` config. Just `.xml` is enough — it covers all Salesforce compound extensions.
|
|
99
|
+
|
|
100
|
+
## ESLint Rule Issues
|
|
101
|
+
|
|
102
|
+
| Problem | Cause | Fix |
|
|
103
|
+
|---------|-------|-----|
|
|
104
|
+
| Custom rules not appearing | Plugin missing `meta.docs.description` + `meta.docs.url` | Ensure plugin exports rule metadata |
|
|
105
|
+
| "Cannot find module" for plugin | Plugin not installed | Run `npm install --save-dev eslint-plugin-<name>` |
|
|
106
|
+
| Base rules conflict with custom | Both Code Analyzer base + custom config active | Set `disable_<type>_base_config: true` to suppress built-in rules |
|
|
107
|
+
| ESLint config not found | Wrong path or auto-discover off | Set `eslint_config_file` explicitly or enable `auto_discover_eslint_config` |
|
|
108
|
+
| Deprecated rules excluded | ESLint engine filters deprecated rules | Use replacement rule name (check ESLint docs) |
|
|
109
|
+
| `eslint_config_file` path in `code-analyzer.yml` fails at startup | Code Analyzer validates the path at load time | **Create `eslint.config.js` BEFORE adding it to `code-analyzer.yml`.** File must exist first. |
|
|
110
|
+
| Core rule (e.g., `no-restricted-globals`) not appearing in `sf code-analyzer rules` | Rule not enabled in `eslint.config.js` | Core ESLint rules only appear after you enable them in config AND Code Analyzer loads it. Always validate after configuration. |
|
|
111
|
+
| Created same rule in both Regex AND PMD — double-flagging | Rule defined in two engines | A rule should live in ONE engine only. Use PMD when structural exclusions (e.g., test classes) are needed; Regex for simple text patterns with no exclusions. |
|
|
112
|
+
|
|
113
|
+
## PMD Apex Rule Issues
|
|
114
|
+
|
|
115
|
+
> XPath authoring gotchas (loop-type coverage, PMD 7 boolean attributes like `@WithSharing`, etc.) live in the SKILL.md **Gotchas** table — that is the single source. This section covers PMD issues outside XPath authoring.
|
|
116
|
+
|
|
117
|
+
| Problem | Cause | Fix |
|
|
118
|
+
|---------|-------|-----|
|
|
119
|
+
| Rule works but wrong severity shows | Severity set in wrong place | Regex: edit `severity` under `engines.regex.custom_rules.<RuleName>` in `code-analyzer.yml`. PMD: set `<priority>` in the ruleset XML. |
|
|
120
|
+
| Too many false positives | Pattern too broad | Regex: tighten pattern, then add `regex_ignore` for edge cases. PMD: tighten XPath with `[not(...)]` predicates. |
|
|
121
|
+
| Want to customize threshold on a built-in rule | Can't modify built-in ruleset files | Use `<rule ref="category/...">` with nested `<properties>` override — see `references/advanced-pmd-patterns.md`. |
|
|
122
|
+
|
|
123
|
+
## Config File Issues
|
|
124
|
+
|
|
125
|
+
| Problem | Cause | Fix |
|
|
126
|
+
|---------|-------|-----|
|
|
127
|
+
| Config not picked up | File named wrong or in wrong directory | Must be `code-analyzer.yml` or `code-analyzer.yaml` at project root |
|
|
128
|
+
| YAML parse error | Invalid YAML syntax | Check indentation (2 spaces), colon spacing, quote balance |
|
|
129
|
+
| "engines.regex.custom_rules" not recognized | Wrong nesting level | Ensure correct hierarchy: `engines` → `regex` → `custom_rules` → `RuleName` |
|
|
130
|
+
| Multiple config files conflict | Both `.yml` and `.yaml` exist | Delete one — `.yaml` takes precedence over `.yml` |
|
|
131
|
+
| Custom ruleset path not found | Path not relative to config_root | `custom_rulesets` paths resolve relative to `code-analyzer.yml` directory |
|
|
132
|
+
| Rule severity override not working | Wrong config location | Use `rules.pmd.RuleName.severity` in config, not inside ruleset XML |
|
|
133
|
+
| Can't disable a specific rule | Wrong field name | Use `rules.pmd.RuleName.disabled: true` in `code-analyzer.yml` |
|
|
134
|
+
|
|
135
|
+
## Exclusion Issues
|
|
136
|
+
|
|
137
|
+
| Problem | Cause | Fix |
|
|
138
|
+
|---------|-------|-----|
|
|
139
|
+
| Rule still fires on excluded files | Glob pattern wrong | Use `**` for recursive: `"**/fflib_*"` not `"fflib_*"` |
|
|
140
|
+
| PMD exclude-pattern ignored | Pattern syntax differs from glob | PMD uses Java regex in `<exclude-pattern>`, not glob |
|
|
141
|
+
| Want to exclude one rule from a category | Missing `<exclude>` in ref | Use `<rule ref="category/..."><exclude name="RuleName"/></rule>` |
|
package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-governor-limits.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# XPath Patterns for Governor Limits
|
|
2
|
+
|
|
3
|
+
[← Back to XPath Patterns Index](xpath-patterns.md)
|
|
4
|
+
|
|
5
|
+
## Governor Limit Patterns
|
|
6
|
+
|
|
7
|
+
### SOQL inside loops
|
|
8
|
+
|
|
9
|
+
⚠️ **Must scope to `/BlockStatement//`** — ForEachStatement has the iterable SOQL as a direct child alongside BlockStatement. Without this, `for (Contact c : [SELECT...])` is a false positive.
|
|
10
|
+
|
|
11
|
+
**ForEachStatement child structure (from ast-dump):**
|
|
12
|
+
```text
|
|
13
|
+
ForEachStatement
|
|
14
|
+
├── VariableDeclarationStatements ← loop variable declaration
|
|
15
|
+
├── VariableExpression ← loop variable reference
|
|
16
|
+
├── BlockStatement ← LOOP BODY — only match here
|
|
17
|
+
└── SoqlExpression (or VariableExpression) ← ITERABLE — do NOT flag
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```xpath
|
|
21
|
+
//ForEachStatement/BlockStatement//SoqlExpression
|
|
22
|
+
|
|
|
23
|
+
//ForLoopStatement/BlockStatement//SoqlExpression
|
|
24
|
+
|
|
|
25
|
+
//WhileLoopStatement/BlockStatement//SoqlExpression
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Catches:** `for (Id accId : ids) { Account acc = [SELECT ...]; }` (SOQL in body)
|
|
29
|
+
**Does NOT catch (correct):** `for (Contact c : [SELECT Id FROM Contact]) { ... }` (SOQL as iterable)
|
|
30
|
+
|
|
31
|
+
### DML inside loops
|
|
32
|
+
|
|
33
|
+
Same principle — scope to BlockStatement:
|
|
34
|
+
|
|
35
|
+
```xpath
|
|
36
|
+
//ForEachStatement/BlockStatement//DmlInsertStatement
|
|
37
|
+
|
|
|
38
|
+
//ForEachStatement/BlockStatement//DmlUpdateStatement
|
|
39
|
+
|
|
|
40
|
+
//ForEachStatement/BlockStatement//DmlDeleteStatement
|
|
41
|
+
|
|
|
42
|
+
//ForEachStatement/BlockStatement//DmlUpsertStatement
|
|
43
|
+
|
|
|
44
|
+
//ForLoopStatement/BlockStatement//DmlInsertStatement
|
|
45
|
+
|
|
|
46
|
+
//ForLoopStatement/BlockStatement//DmlUpdateStatement
|
|
47
|
+
|
|
|
48
|
+
//ForLoopStatement/BlockStatement//DmlDeleteStatement
|
|
49
|
+
|
|
|
50
|
+
//ForLoopStatement/BlockStatement//DmlUpsertStatement
|
|
51
|
+
|
|
|
52
|
+
//WhileLoopStatement/BlockStatement//DmlInsertStatement
|
|
53
|
+
|
|
|
54
|
+
//WhileLoopStatement/BlockStatement//DmlUpdateStatement
|
|
55
|
+
|
|
|
56
|
+
//WhileLoopStatement/BlockStatement//DmlDeleteStatement
|
|
57
|
+
|
|
|
58
|
+
//WhileLoopStatement/BlockStatement//DmlUpsertStatement
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Shorter variant** (ForEach only, most common):
|
|
62
|
+
```xpath
|
|
63
|
+
//ForEachStatement/BlockStatement//DmlInsertStatement
|
|
64
|
+
| //ForEachStatement/BlockStatement//DmlUpdateStatement
|
|
65
|
+
| //ForEachStatement/BlockStatement//DmlDeleteStatement
|
|
66
|
+
| //ForEachStatement/BlockStatement//DmlUpsertStatement
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Database methods inside loops
|
|
70
|
+
|
|
71
|
+
```xpath
|
|
72
|
+
//ForEachStatement/BlockStatement//MethodCallExpression[@FullMethodName='Database.query']
|
|
73
|
+
|
|
|
74
|
+
//ForEachStatement/BlockStatement//MethodCallExpression[@FullMethodName='Database.insert']
|
|
75
|
+
|
|
|
76
|
+
//ForEachStatement/BlockStatement//MethodCallExpression[@FullMethodName='Database.update']
|
|
77
|
+
|
|
|
78
|
+
//ForEachStatement/BlockStatement//MethodCallExpression[@FullMethodName='Database.delete']
|
|
79
|
+
|
|
|
80
|
+
//ForLoopStatement/BlockStatement//MethodCallExpression[@FullMethodName='Database.query']
|
|
81
|
+
|
|
|
82
|
+
//WhileLoopStatement/BlockStatement//MethodCallExpression[@FullMethodName='Database.query']
|
|
83
|
+
```
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# XPath Patterns for Method Calls and Annotations
|
|
2
|
+
|
|
3
|
+
[← Back to XPath Patterns Index](xpath-patterns.md)
|
|
4
|
+
|
|
5
|
+
## Method Call Patterns
|
|
6
|
+
|
|
7
|
+
### Ban System.debug (non-test classes)
|
|
8
|
+
|
|
9
|
+
```xpath
|
|
10
|
+
//MethodCallExpression[@FullMethodName='System.debug']
|
|
11
|
+
[not(ancestor::UserClass[ModifierNode[@Test = true()]])]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**AST evidence:** `MethodCallExpression` has `@FullMethodName='System.debug'`. Test classes have `ModifierNode[@Test = true()]`.
|
|
15
|
+
|
|
16
|
+
**Catches:** `System.debug('anything');` in production classes
|
|
17
|
+
**Does NOT catch (correct):** Same call inside `@IsTest` classes
|
|
18
|
+
|
|
19
|
+
### Ban Database.query (dynamic SOQL — SOQL injection risk)
|
|
20
|
+
|
|
21
|
+
```xpath
|
|
22
|
+
//MethodCallExpression[@FullMethodName='Database.query']
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**AST evidence:** `MethodCallExpression[@FullMethodName='Database.query']` (line 118 of verified AST)
|
|
26
|
+
|
|
27
|
+
To exclude test classes:
|
|
28
|
+
```xpath
|
|
29
|
+
//MethodCallExpression[@FullMethodName='Database.query']
|
|
30
|
+
[not(ancestor::UserClass[ModifierNode[@Test = true()]])]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Ban Test.isRunningTest() in production code
|
|
34
|
+
|
|
35
|
+
```xpath
|
|
36
|
+
//MethodCallExpression[@FullMethodName='Test.isRunningTest']
|
|
37
|
+
[not(ancestor::UserClass[ModifierNode[@Test = true()]])]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Ban specific method calls (generic pattern)
|
|
41
|
+
|
|
42
|
+
```xpath
|
|
43
|
+
//MethodCallExpression[@FullMethodName='ClassName.methodName']
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Common examples:
|
|
47
|
+
- `@FullMethodName='System.debug'`
|
|
48
|
+
- `@FullMethodName='Database.query'`
|
|
49
|
+
- `@FullMethodName='Test.isRunningTest'`
|
|
50
|
+
- `@FullMethodName='UserInfo.getUserId'`
|
|
51
|
+
- `@FullMethodName='Limits.getQueries'`
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Annotation Patterns
|
|
56
|
+
|
|
57
|
+
### @AuraEnabled without cacheable=true
|
|
58
|
+
|
|
59
|
+
```xpath
|
|
60
|
+
//Method/ModifierNode/Annotation[@Name='AuraEnabled'
|
|
61
|
+
and not(AnnotationParameter[@Name='cacheable' and @Value='true'])]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**AST evidence:** `Annotation[@Name='AuraEnabled']` with child `AnnotationParameter[@Name='cacheable'][@Value='true']`
|
|
65
|
+
|
|
66
|
+
**Catches:** `@AuraEnabled public static ...` (no cacheable)
|
|
67
|
+
**Does NOT catch (correct):** `@AuraEnabled(cacheable=true) public static ...`
|
|
68
|
+
|
|
69
|
+
### @future methods (recommend Queueable instead)
|
|
70
|
+
|
|
71
|
+
```xpath
|
|
72
|
+
//Method/ModifierNode/Annotation[@Name='Future']
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**AST evidence:** Annotation `@Name='Future'` (note: PMD normalizes `@future` → `Future` in the Name attribute, but `@RawName='future'`)
|
|
76
|
+
|
|
77
|
+
### @SuppressWarnings usage (audit/ban)
|
|
78
|
+
|
|
79
|
+
```xpath
|
|
80
|
+
//Annotation[@Name='SuppressWarnings']
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**AST evidence:** `Annotation[@Name='SuppressWarnings']` with `AnnotationParameter[@Name='value'][@Value='PMD.RuleName']`
|
|
84
|
+
|
|
85
|
+
To flag specific suppressions:
|
|
86
|
+
```xpath
|
|
87
|
+
//Annotation[@Name='SuppressWarnings']/AnnotationParameter[contains(@Value, 'ApexCRUDViolation')]
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### @IsTest without testFor parameter
|
|
91
|
+
|
|
92
|
+
From GitHub issue #2008:
|
|
93
|
+
```xpath
|
|
94
|
+
//UserClass/ModifierNode/Annotation[
|
|
95
|
+
@Name='IsTest'
|
|
96
|
+
and not(AnnotationParameter[@Name='testFor'])
|
|
97
|
+
]
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### @IsTest class without System.runAs
|
|
101
|
+
|
|
102
|
+
```xpath
|
|
103
|
+
//UserClass[ModifierNode[@Test = true()]]
|
|
104
|
+
/Method[ModifierNode[@Test = true()]]
|
|
105
|
+
/BlockStatement[not(.//RunAsBlockStatement)]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**AST evidence:** `RunAsBlockStatement` is the node for `System.runAs(...) { }` blocks.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# XPath Patterns for Security
|
|
2
|
+
|
|
3
|
+
[← Back to XPath Patterns Index](xpath-patterns.md)
|
|
4
|
+
|
|
5
|
+
## Security Patterns
|
|
6
|
+
|
|
7
|
+
### Class without sharing declaration
|
|
8
|
+
|
|
9
|
+
```xpath
|
|
10
|
+
//UserClass[
|
|
11
|
+
@Nested = false()
|
|
12
|
+
and ModifierNode[@WithSharing = false() and @WithoutSharing = false() and @InheritedSharing = false()]
|
|
13
|
+
]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**AST evidence:** `ModifierNode` has `@WithSharing`, `@WithoutSharing`, `@InheritedSharing` — all `false()` means no sharing keyword declared (implicit without sharing).
|
|
17
|
+
|
|
18
|
+
Added `@Nested = false()` to exclude inner classes (which inherit from parent).
|
|
19
|
+
|
|
20
|
+
> ⚠️ **PMD 7 boolean attributes:** `@WithSharing`, `@WithoutSharing`, `@InheritedSharing`, and `@Nested` are boolean-typed in PMD 7 — string comparison (`='false'`) errors with "Cannot compare xs:boolean to xs:string". Always use `= false()` / `= true()`. See [xpath-patterns.md](xpath-patterns.md) for the full list.
|
|
21
|
+
|
|
22
|
+
### SOQL without WITH USER_MODE or SECURITY_ENFORCED
|
|
23
|
+
|
|
24
|
+
```xpath
|
|
25
|
+
//SoqlExpression[
|
|
26
|
+
not(contains(@CanonicalQuery, 'WITH USER_MODE'))
|
|
27
|
+
and not(contains(@CanonicalQuery, 'WITH SECURITY_ENFORCED'))
|
|
28
|
+
]
|
|
29
|
+
[not(ancestor::UserClass[ModifierNode[@Test = true()]])]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**AST evidence:** `SoqlExpression[@CanonicalQuery]` contains the full normalized query text.
|
|
33
|
+
|
|
34
|
+
### Hardcoded Salesforce IDs (structural — string literals 15-18 chars)
|
|
35
|
+
|
|
36
|
+
```xpath
|
|
37
|
+
//LiteralExpression[
|
|
38
|
+
@LiteralType='STRING'
|
|
39
|
+
and string-length(@Image) >= 15
|
|
40
|
+
and string-length(@Image) <= 18
|
|
41
|
+
]
|
|
42
|
+
[not(ancestor::UserClass[ModifierNode[@Test = true()]])]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Note:** `matches()` function may not be available in all PMD XPath versions. The string-length check catches most cases. For precision, use Regex engine instead.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# XPath Patterns for Code Structure, Tests, and Naming
|
|
2
|
+
|
|
3
|
+
[← Back to XPath Patterns Index](xpath-patterns.md)
|
|
4
|
+
|
|
5
|
+
## Code Structure Patterns
|
|
6
|
+
|
|
7
|
+
### DML without try-catch
|
|
8
|
+
|
|
9
|
+
```xpath
|
|
10
|
+
//DmlInsertStatement[not(ancestor::TryCatchFinallyBlockStatement)]
|
|
11
|
+
|
|
|
12
|
+
//DmlUpdateStatement[not(ancestor::TryCatchFinallyBlockStatement)]
|
|
13
|
+
|
|
|
14
|
+
//DmlDeleteStatement[not(ancestor::TryCatchFinallyBlockStatement)]
|
|
15
|
+
|
|
|
16
|
+
//DmlUpsertStatement[not(ancestor::TryCatchFinallyBlockStatement)]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**AST evidence:** DML nodes sit inside `TryCatchFinallyBlockStatement` when wrapped in try-catch.
|
|
20
|
+
|
|
21
|
+
To exclude test classes:
|
|
22
|
+
```xpath
|
|
23
|
+
//DmlInsertStatement[
|
|
24
|
+
not(ancestor::TryCatchFinallyBlockStatement)
|
|
25
|
+
and not(ancestor::UserClass[ModifierNode[@Test = true()]])
|
|
26
|
+
]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Empty catch blocks (swallowed exceptions)
|
|
30
|
+
|
|
31
|
+
```xpath
|
|
32
|
+
//CatchBlockStatement[BlockStatement[not(*)]]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**AST evidence:** `CatchBlockStatement` contains a `BlockStatement`. Empty body = `BlockStatement` with no children.
|
|
36
|
+
|
|
37
|
+
### Nested if statements (max depth 3)
|
|
38
|
+
|
|
39
|
+
```xpath
|
|
40
|
+
//IfBlockStatement[
|
|
41
|
+
ancestor::IfBlockStatement[
|
|
42
|
+
ancestor::IfBlockStatement[
|
|
43
|
+
ancestor::IfBlockStatement
|
|
44
|
+
]
|
|
45
|
+
]
|
|
46
|
+
]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Methods with too many parameters
|
|
50
|
+
|
|
51
|
+
```xpath
|
|
52
|
+
//Method[@Arity >= 5 and @Constructor = false()]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**AST evidence:** `Method[@Arity]` gives parameter count directly.
|
|
56
|
+
|
|
57
|
+
### Logic in trigger (should delegate to handler)
|
|
58
|
+
|
|
59
|
+
For trigger files (UserTrigger node):
|
|
60
|
+
```xpath
|
|
61
|
+
//UserTrigger/BlockStatement//DmlInsertStatement
|
|
62
|
+
|
|
|
63
|
+
//UserTrigger/BlockStatement//DmlUpdateStatement
|
|
64
|
+
|
|
|
65
|
+
//UserTrigger/BlockStatement//SoqlExpression
|
|
66
|
+
|
|
|
67
|
+
//UserTrigger/BlockStatement//MethodCallExpression[@FullMethodName='Database.query']
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Test Quality Patterns
|
|
73
|
+
|
|
74
|
+
### Test method without assertions
|
|
75
|
+
|
|
76
|
+
```xpath
|
|
77
|
+
//Method[ModifierNode[@Test = true()]]
|
|
78
|
+
/BlockStatement[
|
|
79
|
+
not(.//MethodCallExpression[
|
|
80
|
+
contains(@FullMethodName, 'System.assert')
|
|
81
|
+
or contains(@FullMethodName, 'Assert.')
|
|
82
|
+
])
|
|
83
|
+
]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Catches:** Test methods with no `System.assert*` or `Assert.*` calls anywhere in their body.
|
|
87
|
+
|
|
88
|
+
### Test method without System.runAs
|
|
89
|
+
|
|
90
|
+
```xpath
|
|
91
|
+
//Method[ModifierNode[@Test = true()]]
|
|
92
|
+
/BlockStatement[not(.//RunAsBlockStatement)]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**AST evidence:** `System.runAs(user) { ... }` becomes `RunAsBlockStatement` in the AST.
|
|
96
|
+
|
|
97
|
+
### Test using seeAllData=true
|
|
98
|
+
|
|
99
|
+
```xpath
|
|
100
|
+
//Annotation[@Name='IsTest']/AnnotationParameter[@Name='seeAllData' and @Value='true']
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Naming Convention Patterns
|
|
106
|
+
|
|
107
|
+
### Class name doesn't match file (non-test)
|
|
108
|
+
|
|
109
|
+
This is better enforced by the built-in rule, but the XPath pattern for a specific convention:
|
|
110
|
+
```xpath
|
|
111
|
+
//UserClass[@Nested = false() and not(starts-with(@Image, 'Test')) and not(ends-with(@Image, 'Test'))]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Method naming (non-standard)
|
|
115
|
+
|
|
116
|
+
```xpath
|
|
117
|
+
//Method[
|
|
118
|
+
@Constructor = false()
|
|
119
|
+
and not(ModifierNode[@Test = true()])
|
|
120
|
+
and not(starts-with(@Image, 'get'))
|
|
121
|
+
and not(starts-with(@Image, 'set'))
|
|
122
|
+
and not(starts-with(@Image, 'is'))
|
|
123
|
+
and matches(@Image, '^[A-Z]')
|
|
124
|
+
]
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Catches:** Methods starting with uppercase in non-test code (Apex convention is camelCase).
|