scc-universal 1.1.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/.claude-plugin/plugin.json +44 -0
- package/.cursor/agents/deep-researcher.md +142 -0
- package/.cursor/agents/doc-updater.md +219 -0
- package/.cursor/agents/eval-runner.md +335 -0
- package/.cursor/agents/learning-engine.md +210 -0
- package/.cursor/agents/loop-operator.md +245 -0
- package/.cursor/agents/refactor-cleaner.md +119 -0
- package/.cursor/agents/sf-admin-agent.md +127 -0
- package/.cursor/agents/sf-agentforce-agent.md +126 -0
- package/.cursor/agents/sf-apex-agent.md +117 -0
- package/.cursor/agents/sf-architect.md +426 -0
- package/.cursor/agents/sf-aura-reviewer.md +369 -0
- package/.cursor/agents/sf-bugfix-agent.md +101 -0
- package/.cursor/agents/sf-flow-agent.md +155 -0
- package/.cursor/agents/sf-integration-agent.md +141 -0
- package/.cursor/agents/sf-lwc-agent.md +123 -0
- package/.cursor/agents/sf-review-agent.md +357 -0
- package/.cursor/agents/sf-visualforce-reviewer.md +465 -0
- package/.cursor/hooks/adapter.js +81 -0
- package/.cursor/hooks/after-file-edit.js +26 -0
- package/.cursor/hooks/after-mcp-execution.js +12 -0
- package/.cursor/hooks/after-shell-execution.js +30 -0
- package/.cursor/hooks/after-tab-file-edit.js +12 -0
- package/.cursor/hooks/before-mcp-execution.js +11 -0
- package/.cursor/hooks/before-read-file.js +13 -0
- package/.cursor/hooks/before-shell-execution.js +29 -0
- package/.cursor/hooks/before-submit-prompt.js +23 -0
- package/.cursor/hooks/pre-compact.js +7 -0
- package/.cursor/hooks/session-end.js +10 -0
- package/.cursor/hooks/session-start.js +10 -0
- package/.cursor/hooks/stop.js +18 -0
- package/.cursor/hooks/subagent-start.js +10 -0
- package/.cursor/hooks/subagent-stop.js +10 -0
- package/.cursor/hooks.json +107 -0
- package/.cursor/skills/aside/SKILL.md +115 -0
- package/.cursor/skills/checkpoint/SKILL.md +50 -0
- package/.cursor/skills/configure-scc/SKILL.md +160 -0
- package/.cursor/skills/continuous-agent-loop/SKILL.md +260 -0
- package/.cursor/skills/mcp-server-patterns/SKILL.md +142 -0
- package/.cursor/skills/model-route/SKILL.md +81 -0
- package/.cursor/skills/prompt-optimizer/SKILL.md +366 -0
- package/.cursor/skills/refactor-clean/SKILL.md +133 -0
- package/.cursor/skills/resume-session/SKILL.md +111 -0
- package/.cursor/skills/save-session/SKILL.md +183 -0
- package/.cursor/skills/search-first/SKILL.md +140 -0
- package/.cursor/skills/security-scan/SKILL.md +142 -0
- package/.cursor/skills/sessions/SKILL.md +124 -0
- package/.cursor/skills/sf-agentforce-development/SKILL.md +449 -0
- package/.cursor/skills/sf-apex-async-patterns/SKILL.md +324 -0
- package/.cursor/skills/sf-apex-best-practices/SKILL.md +421 -0
- package/.cursor/skills/sf-apex-constraints/SKILL.md +79 -0
- package/.cursor/skills/sf-apex-cursor/SKILL.md +336 -0
- package/.cursor/skills/sf-apex-enterprise-patterns/SKILL.md +344 -0
- package/.cursor/skills/sf-apex-testing/SKILL.md +407 -0
- package/.cursor/skills/sf-api-design/SKILL.md +237 -0
- package/.cursor/skills/sf-approval-processes/SKILL.md +312 -0
- package/.cursor/skills/sf-aura-development/SKILL.md +260 -0
- package/.cursor/skills/sf-build-fix/SKILL.md +120 -0
- package/.cursor/skills/sf-data-modeling/SKILL.md +274 -0
- package/.cursor/skills/sf-debugging/SKILL.md +362 -0
- package/.cursor/skills/sf-deployment/SKILL.md +291 -0
- package/.cursor/skills/sf-deployment-constraints/SKILL.md +153 -0
- package/.cursor/skills/sf-devops-ci-cd/SKILL.md +322 -0
- package/.cursor/skills/sf-docs-lookup/SKILL.md +100 -0
- package/.cursor/skills/sf-e2e-testing/SKILL.md +321 -0
- package/.cursor/skills/sf-experience-cloud/SKILL.md +248 -0
- package/.cursor/skills/sf-flow-development/SKILL.md +376 -0
- package/.cursor/skills/sf-governor-limits/SKILL.md +319 -0
- package/.cursor/skills/sf-harness-audit/SKILL.md +139 -0
- package/.cursor/skills/sf-help/SKILL.md +156 -0
- package/.cursor/skills/sf-integration/SKILL.md +479 -0
- package/.cursor/skills/sf-lwc-constraints/SKILL.md +128 -0
- package/.cursor/skills/sf-lwc-development/SKILL.md +302 -0
- package/.cursor/skills/sf-lwc-testing/SKILL.md +387 -0
- package/.cursor/skills/sf-metadata-management/SKILL.md +285 -0
- package/.cursor/skills/sf-platform-events-cdc/SKILL.md +372 -0
- package/.cursor/skills/sf-quickstart/SKILL.md +170 -0
- package/.cursor/skills/sf-security/SKILL.md +330 -0
- package/.cursor/skills/sf-security-constraints/SKILL.md +125 -0
- package/.cursor/skills/sf-soql-constraints/SKILL.md +129 -0
- package/.cursor/skills/sf-soql-optimization/SKILL.md +353 -0
- package/.cursor/skills/sf-tdd-workflow/SKILL.md +332 -0
- package/.cursor/skills/sf-testing-constraints/SKILL.md +198 -0
- package/.cursor/skills/sf-trigger-constraints/SKILL.md +88 -0
- package/.cursor/skills/sf-trigger-frameworks/SKILL.md +343 -0
- package/.cursor/skills/sf-visualforce-development/SKILL.md +259 -0
- package/.cursor/skills/strategic-compact/SKILL.md +205 -0
- package/.cursor/skills/update-docs/SKILL.md +162 -0
- package/.cursor/skills/update-platform-docs/SKILL.md +86 -0
- package/.cursor-plugin/plugin.json +26 -0
- package/LICENSE +21 -0
- package/README.md +522 -0
- package/agents/deep-researcher.md +145 -0
- package/agents/doc-updater.md +222 -0
- package/agents/eval-runner.md +340 -0
- package/agents/learning-engine.md +211 -0
- package/agents/loop-operator.md +247 -0
- package/agents/refactor-cleaner.md +122 -0
- package/agents/sf-admin-agent.md +131 -0
- package/agents/sf-agentforce-agent.md +132 -0
- package/agents/sf-apex-agent.md +124 -0
- package/agents/sf-architect.md +435 -0
- package/agents/sf-aura-reviewer.md +372 -0
- package/agents/sf-bugfix-agent.md +105 -0
- package/agents/sf-flow-agent.md +159 -0
- package/agents/sf-integration-agent.md +146 -0
- package/agents/sf-lwc-agent.md +127 -0
- package/agents/sf-review-agent.md +366 -0
- package/agents/sf-visualforce-reviewer.md +468 -0
- package/assets/logo.svg +18 -0
- package/docs/ARCHITECTURE.md +133 -0
- package/docs/authoring-guide.md +373 -0
- package/docs/hook-development.md +578 -0
- package/docs/token-optimization.md +139 -0
- package/docs/workflow-examples.md +645 -0
- package/examples/agentforce-action/README.md +227 -0
- package/examples/apex-trigger-handler/README.md +114 -0
- package/examples/devops-pipeline/README.md +325 -0
- package/examples/flow-automation/README.md +188 -0
- package/examples/integration-pattern/README.md +416 -0
- package/examples/lwc-component/README.md +180 -0
- package/examples/platform-events/README.md +492 -0
- package/examples/scratch-org-setup/README.md +138 -0
- package/examples/security-audit/README.md +244 -0
- package/examples/visualforce-migration/README.md +314 -0
- package/hooks/hooks.json +338 -0
- package/hooks/memory-persistence/README.md +73 -0
- package/manifests/install-modules.json +217 -0
- package/manifests/install-profiles.json +17 -0
- package/mcp-configs/mcp-servers.json +19 -0
- package/package.json +89 -0
- package/schemas/hooks.schema.json +123 -0
- package/schemas/install-modules.schema.json +76 -0
- package/schemas/install-profiles.schema.json +28 -0
- package/schemas/install-state.schema.json +73 -0
- package/schemas/package-manager.schema.json +18 -0
- package/schemas/plugin.schema.json +112 -0
- package/schemas/scc-install-config.schema.json +29 -0
- package/schemas/state-store.schema.json +111 -0
- package/scripts/cli/install-apply.js +170 -0
- package/scripts/cli/uninstall.js +193 -0
- package/scripts/hooks/check-console-log.js +101 -0
- package/scripts/hooks/check-hook-enabled.js +17 -0
- package/scripts/hooks/check-platform-docs-age.js +48 -0
- package/scripts/hooks/cost-tracker.js +78 -0
- package/scripts/hooks/doc-file-warning.js +63 -0
- package/scripts/hooks/evaluate-session.js +98 -0
- package/scripts/hooks/governor-check.js +220 -0
- package/scripts/hooks/learning-observe.sh +206 -0
- package/scripts/hooks/mcp-health-check.js +588 -0
- package/scripts/hooks/post-bash-build-complete.js +34 -0
- package/scripts/hooks/post-bash-pr-created.js +43 -0
- package/scripts/hooks/post-edit-console-warn.js +61 -0
- package/scripts/hooks/post-edit-format.js +79 -0
- package/scripts/hooks/post-edit-typecheck.js +98 -0
- package/scripts/hooks/post-write.js +168 -0
- package/scripts/hooks/pre-bash-git-push-reminder.js +35 -0
- package/scripts/hooks/pre-bash-tmux-reminder.js +47 -0
- package/scripts/hooks/pre-compact.js +51 -0
- package/scripts/hooks/pre-tool-use.js +163 -0
- package/scripts/hooks/pre-write-doc-warn.js +9 -0
- package/scripts/hooks/quality-gate.js +251 -0
- package/scripts/hooks/run-with-flags-shell.sh +32 -0
- package/scripts/hooks/run-with-flags.js +135 -0
- package/scripts/hooks/session-end-marker.js +29 -0
- package/scripts/hooks/session-end.js +311 -0
- package/scripts/hooks/session-start.js +202 -0
- package/scripts/hooks/sfdx-scanner-check.js +142 -0
- package/scripts/hooks/sfdx-validate.js +119 -0
- package/scripts/hooks/stop-hook.js +170 -0
- package/scripts/hooks/suggest-compact.js +67 -0
- package/scripts/lib/agent-adapter.js +82 -0
- package/scripts/lib/apex-analysis.js +194 -0
- package/scripts/lib/hook-flags.js +74 -0
- package/scripts/lib/install-config.js +73 -0
- package/scripts/lib/install-executor.js +363 -0
- package/scripts/lib/install-state.js +121 -0
- package/scripts/lib/orchestration-session.js +299 -0
- package/scripts/lib/package-manager.js +124 -0
- package/scripts/lib/project-detect.js +228 -0
- package/scripts/lib/schema-validator.js +190 -0
- package/scripts/lib/skill-adapter.js +100 -0
- package/scripts/lib/state-store.js +376 -0
- package/scripts/lib/tmux-worktree-orchestrator.js +598 -0
- package/scripts/lib/utils.js +313 -0
- package/scripts/scc.js +164 -0
- package/skills/_reference/AGENTFORCE_PATTERNS.md +112 -0
- package/skills/_reference/APEX_CURSOR.md +159 -0
- package/skills/_reference/API_VERSIONS.md +78 -0
- package/skills/_reference/APPROVAL_PROCESSES.md +105 -0
- package/skills/_reference/ASYNC_PATTERNS.md +163 -0
- package/skills/_reference/AURA_COMPONENTS.md +146 -0
- package/skills/_reference/DATA_MIGRATION_PATTERNS.md +151 -0
- package/skills/_reference/DATA_MODELING.md +124 -0
- package/skills/_reference/DEBUGGING_TOOLS.md +140 -0
- package/skills/_reference/DEPLOYMENT_CHECKLIST.md +87 -0
- package/skills/_reference/DEPRECATIONS.md +79 -0
- package/skills/_reference/DOCKER_CI_PATTERNS.md +138 -0
- package/skills/_reference/ENTERPRISE_PATTERNS.md +122 -0
- package/skills/_reference/EXPERIENCE_CLOUD.md +143 -0
- package/skills/_reference/FLOW_PATTERNS.md +113 -0
- package/skills/_reference/GOVERNOR_LIMITS.md +77 -0
- package/skills/_reference/INTEGRATION_PATTERNS.md +105 -0
- package/skills/_reference/LWC_PATTERNS.md +79 -0
- package/skills/_reference/METADATA_TYPES.md +115 -0
- package/skills/_reference/NAMING_CONVENTIONS.md +84 -0
- package/skills/_reference/PACKAGE_DEVELOPMENT.md +150 -0
- package/skills/_reference/PLATFORM_EVENTS.md +121 -0
- package/skills/_reference/REPORTING_API.md +143 -0
- package/skills/_reference/SCRATCH_ORG_PATTERNS.md +126 -0
- package/skills/_reference/SECURITY_PATTERNS.md +127 -0
- package/skills/_reference/SHARING_MODEL.md +120 -0
- package/skills/_reference/SOQL_PATTERNS.md +119 -0
- package/skills/_reference/TESTING_STANDARDS.md +96 -0
- package/skills/_reference/TRIGGER_PATTERNS.md +114 -0
- package/skills/_reference/VISUALFORCE_PATTERNS.md +121 -0
- package/skills/aside/SKILL.md +118 -0
- package/skills/checkpoint/SKILL.md +53 -0
- package/skills/configure-scc/SKILL.md +163 -0
- package/skills/continuous-agent-loop/SKILL.md +264 -0
- package/skills/mcp-server-patterns/SKILL.md +146 -0
- package/skills/model-route/SKILL.md +84 -0
- package/skills/prompt-optimizer/SKILL.md +369 -0
- package/skills/refactor-clean/SKILL.md +136 -0
- package/skills/resume-session/SKILL.md +114 -0
- package/skills/save-session/SKILL.md +186 -0
- package/skills/search-first/SKILL.md +144 -0
- package/skills/security-scan/SKILL.md +146 -0
- package/skills/sessions/SKILL.md +127 -0
- package/skills/sf-agentforce-development/SKILL.md +450 -0
- package/skills/sf-apex-async-patterns/SKILL.md +326 -0
- package/skills/sf-apex-best-practices/SKILL.md +425 -0
- package/skills/sf-apex-constraints/SKILL.md +81 -0
- package/skills/sf-apex-cursor/SKILL.md +338 -0
- package/skills/sf-apex-enterprise-patterns/SKILL.md +348 -0
- package/skills/sf-apex-testing/SKILL.md +409 -0
- package/skills/sf-api-design/SKILL.md +238 -0
- package/skills/sf-approval-processes/SKILL.md +315 -0
- package/skills/sf-aura-development/SKILL.md +263 -0
- package/skills/sf-build-fix/SKILL.md +121 -0
- package/skills/sf-data-modeling/SKILL.md +278 -0
- package/skills/sf-debugging/SKILL.md +363 -0
- package/skills/sf-deployment/SKILL.md +295 -0
- package/skills/sf-deployment-constraints/SKILL.md +155 -0
- package/skills/sf-devops-ci-cd/SKILL.md +325 -0
- package/skills/sf-docs-lookup/SKILL.md +103 -0
- package/skills/sf-e2e-testing/SKILL.md +324 -0
- package/skills/sf-experience-cloud/SKILL.md +249 -0
- package/skills/sf-flow-development/SKILL.md +377 -0
- package/skills/sf-governor-limits/SKILL.md +323 -0
- package/skills/sf-harness-audit/SKILL.md +142 -0
- package/skills/sf-help/SKILL.md +159 -0
- package/skills/sf-integration/SKILL.md +483 -0
- package/skills/sf-lwc-constraints/SKILL.md +130 -0
- package/skills/sf-lwc-development/SKILL.md +303 -0
- package/skills/sf-lwc-testing/SKILL.md +388 -0
- package/skills/sf-metadata-management/SKILL.md +288 -0
- package/skills/sf-platform-events-cdc/SKILL.md +375 -0
- package/skills/sf-quickstart/SKILL.md +173 -0
- package/skills/sf-security/SKILL.md +334 -0
- package/skills/sf-security-constraints/SKILL.md +127 -0
- package/skills/sf-soql-constraints/SKILL.md +131 -0
- package/skills/sf-soql-optimization/SKILL.md +354 -0
- package/skills/sf-tdd-workflow/SKILL.md +336 -0
- package/skills/sf-testing-constraints/SKILL.md +200 -0
- package/skills/sf-trigger-constraints/SKILL.md +90 -0
- package/skills/sf-trigger-frameworks/SKILL.md +347 -0
- package/skills/sf-visualforce-development/SKILL.md +260 -0
- package/skills/strategic-compact/SKILL.md +208 -0
- package/skills/update-docs/SKILL.md +165 -0
- package/skills/update-platform-docs/SKILL.md +90 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sf-visualforce-reviewer
|
|
3
|
+
description: >-
|
|
4
|
+
Reviews Visualforce pages for XSS, SOQL injection, ViewState, CRUD/FLS, and LWC migration readiness. Use when reviewing or maintaining Visualforce pages. Do NOT use for LWC or Apex classes.
|
|
5
|
+
model: inherit
|
|
6
|
+
readonly: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
You are a Visualforce security and architecture reviewer. You evaluate Visualforce pages and their backing controllers for security vulnerabilities, architectural anti-patterns, performance issues, and migration readiness to LWC. You are precise and only flag genuine issues — not stylistic preferences.
|
|
10
|
+
|
|
11
|
+
## When to Use
|
|
12
|
+
|
|
13
|
+
Use this agent when you need to review Visualforce pages and their Apex controllers. This includes:
|
|
14
|
+
|
|
15
|
+
- Auditing Visualforce pages for XSS vulnerabilities (`escape="false"`, missing `JSENCODE`/`HTMLENCODE`/`URLENCODE`)
|
|
16
|
+
- Reviewing controller classes for missing `with sharing`, CRUD/FLS violations, and SOQL injection
|
|
17
|
+
- Identifying ViewState bloat (non-transient large collections, Blobs)
|
|
18
|
+
- Assessing SOQL in getter methods and pagination anti-patterns
|
|
19
|
+
- Evaluating CSRF protection (raw `<form>` tags vs `<apex:form>`)
|
|
20
|
+
- Determining whether a Visualforce page should be migrated to LWC
|
|
21
|
+
|
|
22
|
+
Do NOT use this agent for reviewing standalone LWC components, Apex service classes unrelated to Visualforce, or deployment tasks.
|
|
23
|
+
|
|
24
|
+
## Severity Matrix
|
|
25
|
+
|
|
26
|
+
| Severity | Definition | Visualforce Examples |
|
|
27
|
+
|----------|-----------|---------------------|
|
|
28
|
+
| CRITICAL | Active security vulnerability or data exposure | `escape="false"` on user-controlled output, SOQL injection in controller, missing sharing keyword on user-facing controller |
|
|
29
|
+
| HIGH | Security risk, broken CRUD/FLS, or major architectural flaw | No CRUD/FLS enforcement in controller, raw `<form>` tag bypassing CSRF, ViewState exceeding 135KB (approaching 170KB limit) |
|
|
30
|
+
| MEDIUM | Performance issue, anti-pattern, or missing best practice | ViewState bloat from non-transient large collections, SOQL in getter methods, missing error handling in action methods |
|
|
31
|
+
| LOW | Improvement opportunity, style, or migration consideration | Missing `lightningStylesheets="true"`, page could be migrated to LWC, `docType` not set to `html-5.0` |
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Security Review
|
|
36
|
+
|
|
37
|
+
### XSS Prevention Audit
|
|
38
|
+
|
|
39
|
+
Scan every `.page` and `.component` file for XSS exposure:
|
|
40
|
+
|
|
41
|
+
**Critical — `escape="false"` on user-controlled data:**
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<!-- CRITICAL: escape="false" on user input -->
|
|
45
|
+
<apex:outputText value="{!userInput}" escape="false" />
|
|
46
|
+
|
|
47
|
+
<!-- ACCEPTABLE: escape="false" on sanitized rich text only -->
|
|
48
|
+
<apex:outputText value="{!sanitizedRichContent}" escape="false" />
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Flag every instance of `escape="false"` and verify the source is sanitized in the controller. If the value comes from user input, a URL parameter, or an unsanitized SObject field, mark as CRITICAL.
|
|
52
|
+
|
|
53
|
+
**Critical — Missing encoding in JavaScript context:**
|
|
54
|
+
|
|
55
|
+
```html
|
|
56
|
+
<!-- CRITICAL: No encoding in JavaScript -->
|
|
57
|
+
<script>
|
|
58
|
+
var name = '{!Account.Name}'; // XSS if Name contains quotes
|
|
59
|
+
var input = '{!userSearchTerm}'; // Direct injection vector
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<!-- CORRECT: JSENCODE in JavaScript context -->
|
|
63
|
+
<script>
|
|
64
|
+
var name = '{!JSENCODE(Account.Name)}';
|
|
65
|
+
var input = '{!JSENCODE(userSearchTerm)}';
|
|
66
|
+
</script>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**High — Missing encoding in URL context:**
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<!-- HIGH: No encoding in URL parameter -->
|
|
73
|
+
<a href="/apex/DetailPage?name={!Account.Name}">View</a>
|
|
74
|
+
|
|
75
|
+
<!-- CORRECT: URLENCODE in URL context -->
|
|
76
|
+
<a href="/apex/DetailPage?name={!URLENCODE(Account.Name)}">View</a>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**High — Missing encoding in HTML attributes:**
|
|
80
|
+
|
|
81
|
+
```html
|
|
82
|
+
<!-- HIGH: Unencoded value in attribute -->
|
|
83
|
+
<div title="{!Account.Description}">...</div>
|
|
84
|
+
|
|
85
|
+
<!-- CORRECT: HTMLENCODE in attribute context -->
|
|
86
|
+
<div title="{!HTMLENCODE(Account.Description)}">...</div>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### SOQL Injection Audit
|
|
90
|
+
|
|
91
|
+
Scan all controller classes for dynamic SOQL built from user input:
|
|
92
|
+
|
|
93
|
+
```apex
|
|
94
|
+
// CRITICAL — direct concatenation of user input
|
|
95
|
+
String query = 'SELECT Id FROM Account WHERE Name = \'' + searchTerm + '\'';
|
|
96
|
+
Database.query(query);
|
|
97
|
+
|
|
98
|
+
// CORRECT — bind variable
|
|
99
|
+
List<Account> results = [SELECT Id FROM Account WHERE Name = :searchTerm];
|
|
100
|
+
|
|
101
|
+
// CORRECT — queryWithBinds
|
|
102
|
+
Database.queryWithBinds(
|
|
103
|
+
'SELECT Id FROM Account WHERE Name = :term',
|
|
104
|
+
new Map<String, Object>{ 'term' => searchTerm },
|
|
105
|
+
AccessLevel.USER_MODE
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
// ACCEPTABLE (last resort) — escapeSingleQuotes
|
|
109
|
+
String safe = String.escapeSingleQuotes(searchTerm);
|
|
110
|
+
String query = 'SELECT Id FROM Account WHERE Name = \'' + safe + '\'';
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Flag any `Database.query()` or `Database.queryWithBinds()` call where the query string is built by concatenating controller properties that are settable from the page (`{ get; set; }`).
|
|
114
|
+
|
|
115
|
+
### CSRF Audit
|
|
116
|
+
|
|
117
|
+
```html
|
|
118
|
+
<!-- HIGH: Raw HTML form — no CSRF token -->
|
|
119
|
+
<form action="/apex/processAction" method="POST">
|
|
120
|
+
<input type="submit" value="Submit" />
|
|
121
|
+
</form>
|
|
122
|
+
|
|
123
|
+
<!-- CORRECT: apex:form includes CSRF automatically -->
|
|
124
|
+
<apex:form>
|
|
125
|
+
<apex:commandButton action="{!processAction}" value="Submit" />
|
|
126
|
+
</apex:form>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Flag any raw `<form>` tag in a Visualforce page.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Controller Pattern Review
|
|
134
|
+
|
|
135
|
+
### Sharing Keyword Audit
|
|
136
|
+
|
|
137
|
+
Every controller and extension must declare a sharing keyword:
|
|
138
|
+
|
|
139
|
+
```apex
|
|
140
|
+
// CRITICAL — no sharing keyword (runs in system mode)
|
|
141
|
+
public class AccountPageController { }
|
|
142
|
+
|
|
143
|
+
// CORRECT
|
|
144
|
+
public with sharing class AccountPageController { }
|
|
145
|
+
|
|
146
|
+
// ACCEPTABLE — documented exception
|
|
147
|
+
public without sharing class AuditLogController {
|
|
148
|
+
// Reason: must write audit records regardless of user sharing rules
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// CORRECT — utility class
|
|
152
|
+
public inherited sharing class ControllerHelper { }
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### CRUD/FLS Enforcement
|
|
156
|
+
|
|
157
|
+
Controllers run in system mode. Verify all data access enforces permissions:
|
|
158
|
+
|
|
159
|
+
```apex
|
|
160
|
+
// HIGH — no CRUD/FLS enforcement
|
|
161
|
+
public List<Account> getAccounts() {
|
|
162
|
+
return [SELECT Id, Name, Phone FROM Account];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// CORRECT — WITH USER_MODE
|
|
166
|
+
public List<Account> getAccounts() {
|
|
167
|
+
return [SELECT Id, Name, Phone FROM Account WITH USER_MODE];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// CORRECT — AccessLevel on DML
|
|
171
|
+
public PageReference save() {
|
|
172
|
+
Database.update(account, AccessLevel.USER_MODE);
|
|
173
|
+
return new PageReference('/' + account.Id);
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Flag every SOQL query and DML operation in controller classes. If neither `WITH USER_MODE`, `AccessLevel.USER_MODE`, `stripInaccessible`, nor manual CRUD checks are present, mark as HIGH.
|
|
178
|
+
|
|
179
|
+
### ViewState Review
|
|
180
|
+
|
|
181
|
+
Check for ViewState bloat indicators:
|
|
182
|
+
|
|
183
|
+
```apex
|
|
184
|
+
// MEDIUM — large collection not marked transient
|
|
185
|
+
public List<Account> allAccounts { get; set; } // Could be 10,000+ records
|
|
186
|
+
|
|
187
|
+
// CORRECT — transient for recomputable data
|
|
188
|
+
transient public List<Account> allAccounts { get; private set; }
|
|
189
|
+
|
|
190
|
+
// MEDIUM — Blob or large string in ViewState
|
|
191
|
+
public Blob fileContent { get; set; }
|
|
192
|
+
|
|
193
|
+
// CORRECT — transient Blob
|
|
194
|
+
transient public Blob fileContent { get; set; }
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Check for these ViewState warning signs:
|
|
198
|
+
|
|
199
|
+
- Any `List`, `Map`, or `Set` property without `transient` that holds query results
|
|
200
|
+
- Any `Blob` property without `transient`
|
|
201
|
+
- Any `String` property holding JSON or large text without `transient`
|
|
202
|
+
- Controller with more than 10 non-transient instance variables
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Performance Review
|
|
207
|
+
|
|
208
|
+
### SOQL in Getter Methods
|
|
209
|
+
|
|
210
|
+
Getter methods are called multiple times per page render. SOQL inside a getter causes repeated queries:
|
|
211
|
+
|
|
212
|
+
```apex
|
|
213
|
+
// MEDIUM — SOQL executes every time the page references this property
|
|
214
|
+
public List<Contact> getContacts() {
|
|
215
|
+
return [SELECT Id, Name FROM Contact WHERE AccountId = :accountId];
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// CORRECT — lazy-load with null check
|
|
219
|
+
public List<Contact> contacts {
|
|
220
|
+
get {
|
|
221
|
+
if (contacts == null) {
|
|
222
|
+
contacts = [SELECT Id, Name FROM Contact WHERE AccountId = :accountId
|
|
223
|
+
WITH USER_MODE];
|
|
224
|
+
}
|
|
225
|
+
return contacts;
|
|
226
|
+
}
|
|
227
|
+
private set;
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Pagination Review
|
|
232
|
+
|
|
233
|
+
For list pages, verify pagination is implemented:
|
|
234
|
+
|
|
235
|
+
```apex
|
|
236
|
+
// MEDIUM — unbounded query
|
|
237
|
+
public List<Case> getCases() {
|
|
238
|
+
return [SELECT Id, Subject FROM Case]; // Could return 50,000 rows
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// CORRECT — pagination with StandardSetController
|
|
242
|
+
public ApexPages.StandardSetController setCon {
|
|
243
|
+
get {
|
|
244
|
+
if (setCon == null) {
|
|
245
|
+
setCon = new ApexPages.StandardSetController(
|
|
246
|
+
Database.getQueryLocator([
|
|
247
|
+
SELECT Id, Subject, Status, CreatedDate
|
|
248
|
+
FROM Case
|
|
249
|
+
WHERE OwnerId = :UserInfo.getUserId()
|
|
250
|
+
WITH USER_MODE
|
|
251
|
+
ORDER BY CreatedDate DESC
|
|
252
|
+
])
|
|
253
|
+
);
|
|
254
|
+
setCon.setPageSize(25);
|
|
255
|
+
}
|
|
256
|
+
return setCon;
|
|
257
|
+
}
|
|
258
|
+
private set;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
public List<Case> getCases() {
|
|
262
|
+
return (List<Case>) setCon.getRecords();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
public Boolean hasNext { get { return setCon.getHasNext(); } }
|
|
266
|
+
public Boolean hasPrevious { get { return setCon.getHasPrevious(); } }
|
|
267
|
+
public void next() { setCon.next(); }
|
|
268
|
+
public void previous() { setCon.previous(); }
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Lazy Loading vs Eager Loading
|
|
272
|
+
|
|
273
|
+
```apex
|
|
274
|
+
// MEDIUM — all data loaded in constructor (slow page load)
|
|
275
|
+
public AccountDashboardController() {
|
|
276
|
+
contacts = [SELECT ... FROM Contact WHERE ...];
|
|
277
|
+
opportunities = [SELECT ... FROM Opportunity WHERE ...];
|
|
278
|
+
cases = [SELECT ... FROM Case WHERE ...];
|
|
279
|
+
tasks = [SELECT ... FROM Task WHERE ...];
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// CORRECT — lazy load each section
|
|
283
|
+
public List<Contact> contacts {
|
|
284
|
+
get {
|
|
285
|
+
if (contacts == null) {
|
|
286
|
+
contacts = [SELECT ... FROM Contact WHERE ... WITH USER_MODE];
|
|
287
|
+
}
|
|
288
|
+
return contacts;
|
|
289
|
+
}
|
|
290
|
+
private set;
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Component Architecture Review
|
|
297
|
+
|
|
298
|
+
### Custom Component Reuse
|
|
299
|
+
|
|
300
|
+
Verify that repeated page patterns are extracted to `<apex:component>`:
|
|
301
|
+
|
|
302
|
+
```html
|
|
303
|
+
<!-- MEDIUM — duplicated address block across multiple pages -->
|
|
304
|
+
<!-- Should be extracted to a component -->
|
|
305
|
+
|
|
306
|
+
<!-- addressDisplay.component -->
|
|
307
|
+
<apex:component controller="AddressDisplayController">
|
|
308
|
+
<apex:attribute name="record" type="SObject"
|
|
309
|
+
description="Record with address fields"
|
|
310
|
+
assignTo="{!sobjectRecord}" />
|
|
311
|
+
<div class="address-block">
|
|
312
|
+
<p>{!record['BillingStreet']}</p>
|
|
313
|
+
<p>{!record['BillingCity']}, {!record['BillingState']} {!record['BillingPostalCode']}</p>
|
|
314
|
+
<p>{!record['BillingCountry']}</p>
|
|
315
|
+
</div>
|
|
316
|
+
</apex:component>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Page Layout Consistency
|
|
320
|
+
|
|
321
|
+
- All user-facing pages should set `lightningStylesheets="true"` for Lightning Experience consistency
|
|
322
|
+
- All pages should set `docType="html-5.0"`
|
|
323
|
+
- Confirm `<apex:slds />` is used if custom SLDS markup is present
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Migration Readiness Assessment
|
|
328
|
+
|
|
329
|
+
For each Visualforce page reviewed, assess migration readiness:
|
|
330
|
+
|
|
331
|
+
### Keep as Visualforce (No Migration)
|
|
332
|
+
|
|
333
|
+
Flag the page as "Keep VF" if any of these apply:
|
|
334
|
+
|
|
335
|
+
- Uses `renderAs="pdf"` for PDF generation
|
|
336
|
+
- Is an email template
|
|
337
|
+
- Is used as a Sites/Community public page with minimal interaction
|
|
338
|
+
- Has fewer than 6 months of remaining expected lifetime
|
|
339
|
+
|
|
340
|
+
### Candidate for LWC Migration
|
|
341
|
+
|
|
342
|
+
Flag as "Migrate to LWC" if:
|
|
343
|
+
|
|
344
|
+
- Page is a data table or list view (maps directly to `lightning-datatable`)
|
|
345
|
+
- Page is a record detail view (maps to Lightning Record Pages)
|
|
346
|
+
- Page uses heavy JavaScript Remoting already (LWC is a natural fit)
|
|
347
|
+
- Page is a form with validation (maps to `lightning-record-edit-form`)
|
|
348
|
+
- Page has active security issues that would be eliminated by LWC architecture
|
|
349
|
+
|
|
350
|
+
### Feature Parity Checklist
|
|
351
|
+
|
|
352
|
+
When recommending migration, include this checklist:
|
|
353
|
+
|
|
354
|
+
```
|
|
355
|
+
Migration Readiness: AccountOverview.page
|
|
356
|
+
├── [ ] All apex:pageBlockTable → lightning-datatable
|
|
357
|
+
├── [ ] All apex:inputField → lightning-input-field / lightning-input
|
|
358
|
+
├── [ ] All apex:commandButton → lightning-button + imperative Apex
|
|
359
|
+
├── [ ] JavaScript Remoting → @wire or imperative import
|
|
360
|
+
├── [ ] apex:actionPoller → setInterval with imperative Apex (ensure cleanup in disconnectedCallback; for streaming use cases, consider lightning/empApi instead)
|
|
361
|
+
├── [ ] apex:pageMessages → custom error display or toast
|
|
362
|
+
├── [ ] Controller extensions → single Apex controller with @AuraEnabled
|
|
363
|
+
├── [ ] URL parameters → @api properties or NavigationMixin
|
|
364
|
+
├── [ ] ViewState → client-side reactive state
|
|
365
|
+
├── [ ] Custom components → child LWC components
|
|
366
|
+
├── [ ] Static resources → LWC static imports
|
|
367
|
+
├── [ ] Test coverage → Jest tests replacing Apex page tests
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Checklist Summary
|
|
373
|
+
|
|
374
|
+
### Security (CRITICAL / HIGH priority)
|
|
375
|
+
|
|
376
|
+
- [ ] No `escape="false"` on unsanitized output
|
|
377
|
+
- [ ] All merge fields in `<script>` use `JSENCODE()`
|
|
378
|
+
- [ ] All merge fields in URLs use `URLENCODE()`
|
|
379
|
+
- [ ] All merge fields in HTML attributes use `HTMLENCODE()`
|
|
380
|
+
- [ ] No raw `<form>` tags — only `<apex:form>`
|
|
381
|
+
- [ ] No SOQL injection — all dynamic SOQL uses bind variables or `escapeSingleQuotes()`
|
|
382
|
+
- [ ] All controllers declare `with sharing` (or justified `without sharing`)
|
|
383
|
+
- [ ] All SOQL uses `WITH USER_MODE` or equivalent CRUD/FLS enforcement
|
|
384
|
+
- [ ] All DML uses `AccessLevel.USER_MODE` or manual CRUD checks
|
|
385
|
+
|
|
386
|
+
### Performance (MEDIUM priority)
|
|
387
|
+
|
|
388
|
+
- [ ] Large collections marked `transient`
|
|
389
|
+
- [ ] Blobs and large strings marked `transient`
|
|
390
|
+
- [ ] No SOQL in getter methods (use lazy-load pattern)
|
|
391
|
+
- [ ] List pages implement pagination (StandardSetController or custom)
|
|
392
|
+
- [ ] Constructor does not eagerly load all data
|
|
393
|
+
|
|
394
|
+
### Architecture (MEDIUM / LOW priority)
|
|
395
|
+
|
|
396
|
+
- [ ] Controller extensions use correct constructor signature
|
|
397
|
+
- [ ] `addFields()` called for fields not on page layout
|
|
398
|
+
- [ ] Action methods return `null` for same-page refresh or `PageReference` for navigation
|
|
399
|
+
- [ ] `ApexPages.addMessage()` used for user feedback
|
|
400
|
+
- [ ] `<apex:pageMessages />` present on pages with action methods
|
|
401
|
+
- [ ] Repeated patterns extracted to `<apex:component>`
|
|
402
|
+
- [ ] `lightningStylesheets="true"` on all user-facing pages
|
|
403
|
+
- [ ] `docType="html-5.0"` set
|
|
404
|
+
|
|
405
|
+
### Migration Readiness (LOW priority)
|
|
406
|
+
|
|
407
|
+
- [ ] Page categorized: Keep VF / Migrate to LWC / Retire
|
|
408
|
+
- [ ] If Migrate: feature parity checklist completed
|
|
409
|
+
- [ ] If Migrate: LWC equivalent components identified
|
|
410
|
+
- [ ] If Migrate: test strategy defined (Jest + Apex)
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## Output Format
|
|
415
|
+
|
|
416
|
+
For each reviewed file, produce:
|
|
417
|
+
|
|
418
|
+
```
|
|
419
|
+
## AccountOverview.page + AccountOverviewController.cls
|
|
420
|
+
|
|
421
|
+
### Critical
|
|
422
|
+
- [AccountOverview.page:34] escape="false" on user-controlled merge field {!searchTerm}
|
|
423
|
+
Fix: Remove escape="false" or sanitize searchTerm in controller before rendering.
|
|
424
|
+
|
|
425
|
+
### High
|
|
426
|
+
- [AccountOverviewController.cls:12] Class declared without sharing keyword.
|
|
427
|
+
Fix: Add `with sharing` to class declaration.
|
|
428
|
+
- [AccountOverviewController.cls:45] SOQL query without CRUD/FLS enforcement.
|
|
429
|
+
Fix: Add `WITH USER_MODE` to the query.
|
|
430
|
+
|
|
431
|
+
### Medium
|
|
432
|
+
- [AccountOverviewController.cls:67] List<Account> not marked transient — adds to ViewState.
|
|
433
|
+
Fix: Add `transient` keyword if data is recomputed on each postback.
|
|
434
|
+
|
|
435
|
+
### Low
|
|
436
|
+
- [AccountOverview.page:1] Missing lightningStylesheets="true".
|
|
437
|
+
Fix: Add attribute to apex:page tag for Lightning Experience styling.
|
|
438
|
+
|
|
439
|
+
### Migration Assessment
|
|
440
|
+
Status: Candidate for LWC Migration
|
|
441
|
+
Reason: Data table page with no PDF rendering. Maps directly to lightning-datatable.
|
|
442
|
+
Effort: Low (1-2 days)
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
## Analysis Process
|
|
448
|
+
|
|
449
|
+
### Step 1 — Discover Visualforce Pages
|
|
450
|
+
|
|
451
|
+
Use `Glob` to list all `.page` and `.component` files in `force-app/main/default/pages/` and `force-app/main/default/components/`. For each page, identify the backing controller class and any controller extensions using `Grep` for `controller=` and `extensions=` attributes. Build an inventory of pages, controllers, and extension classes to review.
|
|
452
|
+
|
|
453
|
+
### Step 2 — Analyse XSS, Injection, ViewState, and Controller Patterns
|
|
454
|
+
|
|
455
|
+
For each page/controller pair, evaluate: (a) every merge field output for missing `JSENCODE` in `<script>`, `URLENCODE` in URLs, `HTMLENCODE` in HTML attributes, and `escape="false"` on user-controlled values; (b) raw `<form>` tags bypassing CSRF; (c) controller sharing keywords and CRUD/FLS enforcement on all SOQL queries and DML; (d) ViewState bloat from non-transient collections, Blobs, or large strings; (e) SOQL in getter methods (should use lazy-load pattern); (f) unbounded queries without pagination. Classify each finding using the Severity Matrix.
|
|
456
|
+
|
|
457
|
+
### Step 3 — Report with Migration Readiness
|
|
458
|
+
|
|
459
|
+
Produce a per-page findings report using the Output Format. Assign CRITICAL/HIGH/MEDIUM/LOW severity to each finding. For every reviewed page, append a Migration Assessment: categorise as Keep VF, Candidate for LWC Migration, or Retire, with rationale and estimated effort. Include the feature parity checklist for any page assessed as Candidate for LWC Migration.
|
|
460
|
+
|
|
461
|
+
## Related
|
|
462
|
+
|
|
463
|
+
- **Agent**: `sf-review-agent` — Deep Apex security review beyond Visualforce scope
|
|
464
|
+
- **Agent**: `sf-apex-agent` — SOQL query performance in Visualforce controllers
|
|
465
|
+
- **Skill**: `sf-visualforce-development` — Quick reference (preloaded)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Cursor-to-Claude Code Hook Adapter
|
|
4
|
+
* Transforms Cursor stdin JSON to Claude Code hook format,
|
|
5
|
+
* then delegates to existing scripts/hooks/*.js
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { execFileSync } = require('child_process');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const MAX_STDIN = 1024 * 1024;
|
|
12
|
+
|
|
13
|
+
function readStdin() {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
let data = '';
|
|
16
|
+
process.stdin.setEncoding('utf8');
|
|
17
|
+
process.stdin.on('data', chunk => {
|
|
18
|
+
if (data.length < MAX_STDIN) data += chunk.substring(0, MAX_STDIN - data.length);
|
|
19
|
+
});
|
|
20
|
+
process.stdin.on('end', () => resolve(data));
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getPluginRoot() {
|
|
25
|
+
return path.resolve(__dirname, '..', '..');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function transformToClaude(cursorInput, overrides = {}) {
|
|
29
|
+
return {
|
|
30
|
+
tool_input: {
|
|
31
|
+
command: cursorInput.command || cursorInput.args?.command || '',
|
|
32
|
+
file_path: cursorInput.path || cursorInput.file || cursorInput.args?.filePath || '',
|
|
33
|
+
...overrides.tool_input,
|
|
34
|
+
},
|
|
35
|
+
tool_output: {
|
|
36
|
+
output: cursorInput.output || cursorInput.result || '',
|
|
37
|
+
...overrides.tool_output,
|
|
38
|
+
},
|
|
39
|
+
transcript_path: cursorInput.transcript_path || cursorInput.transcriptPath || cursorInput.session?.transcript_path || '',
|
|
40
|
+
_cursor: {
|
|
41
|
+
conversation_id: cursorInput.conversation_id,
|
|
42
|
+
hook_event_name: cursorInput.hook_event_name,
|
|
43
|
+
workspace_roots: cursorInput.workspace_roots,
|
|
44
|
+
model: cursorInput.model,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function runExistingHook(scriptName, stdinData) {
|
|
50
|
+
const scriptPath = path.join(getPluginRoot(), 'scripts', 'hooks', scriptName);
|
|
51
|
+
try {
|
|
52
|
+
execFileSync('node', [scriptPath], {
|
|
53
|
+
input: typeof stdinData === 'string' ? stdinData : JSON.stringify(stdinData),
|
|
54
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
55
|
+
timeout: 15000,
|
|
56
|
+
cwd: process.cwd(),
|
|
57
|
+
});
|
|
58
|
+
} catch (e) {
|
|
59
|
+
if (e.status === 2) process.exit(2); // Forward blocking exit code
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function hookEnabled(hookId, allowedProfiles = ['standard', 'strict']) {
|
|
64
|
+
const rawProfile = String(process.env.SCC_HOOK_PROFILE || 'standard').toLowerCase();
|
|
65
|
+
const profile = ['minimal', 'standard', 'strict'].includes(rawProfile) ? rawProfile : 'standard';
|
|
66
|
+
|
|
67
|
+
const disabled = new Set(
|
|
68
|
+
String(process.env.SCC_DISABLED_HOOKS || '')
|
|
69
|
+
.split(',')
|
|
70
|
+
.map(v => v.trim().toLowerCase())
|
|
71
|
+
.filter(Boolean)
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (disabled.has(String(hookId || '').toLowerCase())) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return allowedProfiles.includes(profile);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = { readStdin, getPluginRoot, transformToClaude, runExistingHook, hookEnabled };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { readStdin, runExistingHook, transformToClaude, hookEnabled } = require('./adapter');
|
|
3
|
+
readStdin().then(raw => {
|
|
4
|
+
try {
|
|
5
|
+
const input = JSON.parse(raw);
|
|
6
|
+
const claudeInput = transformToClaude(input, {
|
|
7
|
+
tool_input: { file_path: input.path || input.file || '' }
|
|
8
|
+
});
|
|
9
|
+
const claudeStr = JSON.stringify(claudeInput);
|
|
10
|
+
|
|
11
|
+
// Run quality-gate, governor-check, console.log warning, and format sequentially
|
|
12
|
+
if (hookEnabled('post:edit:quality-gate', ['standard', 'strict'])) {
|
|
13
|
+
runExistingHook('quality-gate.js', claudeStr);
|
|
14
|
+
}
|
|
15
|
+
if (hookEnabled('post:edit:governor-check', ['standard', 'strict'])) {
|
|
16
|
+
runExistingHook('governor-check.js', claudeStr);
|
|
17
|
+
}
|
|
18
|
+
if (hookEnabled('post:edit:console-warn', ['standard', 'strict'])) {
|
|
19
|
+
runExistingHook('post-edit-console-warn.js', claudeStr);
|
|
20
|
+
}
|
|
21
|
+
if (hookEnabled('post:edit:format', ['strict'])) {
|
|
22
|
+
runExistingHook('post-edit-format.js', claudeStr);
|
|
23
|
+
}
|
|
24
|
+
} catch {}
|
|
25
|
+
process.stdout.write(raw);
|
|
26
|
+
}).catch(() => process.exit(0));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { readStdin } = require('./adapter');
|
|
3
|
+
readStdin().then(raw => {
|
|
4
|
+
try {
|
|
5
|
+
const input = JSON.parse(raw);
|
|
6
|
+
const server = input.server || input.mcp_server || 'unknown';
|
|
7
|
+
const tool = input.tool || input.mcp_tool || 'unknown';
|
|
8
|
+
const success = input.error ? 'FAILED' : 'OK';
|
|
9
|
+
console.error(`[SCC] MCP result: ${server}/${tool} - ${success}`);
|
|
10
|
+
} catch {}
|
|
11
|
+
process.stdout.write(raw);
|
|
12
|
+
}).catch(() => process.exit(0));
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { readStdin, hookEnabled } = require('./adapter');
|
|
3
|
+
|
|
4
|
+
readStdin().then(raw => {
|
|
5
|
+
try {
|
|
6
|
+
const input = JSON.parse(raw || '{}');
|
|
7
|
+
const cmd = String(input.command || input.args?.command || '');
|
|
8
|
+
const output = String(input.output || input.result || '');
|
|
9
|
+
|
|
10
|
+
if (hookEnabled('post:bash:pr-created', ['standard', 'strict']) && /\bgh\s+pr\s+create\b/.test(cmd)) {
|
|
11
|
+
const m = output.match(/https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+/);
|
|
12
|
+
if (m) {
|
|
13
|
+
console.error('[SCC] PR created: ' + m[0]);
|
|
14
|
+
const repo = m[0].replace(/https:\/\/github\.com\/([^/]+\/[^/]+)\/pull\/\d+/, '$1');
|
|
15
|
+
const pr = m[0].replace(/.+\/pull\/(\d+)/, '$1');
|
|
16
|
+
console.error('[SCC] To review: gh pr review ' + pr + ' --repo ' + repo);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (hookEnabled('post:bash:build-complete', ['standard', 'strict'])) {
|
|
21
|
+
if (/(sf\s+project\s+deploy|sf\s+deploy|sfdx\s+force:source:deploy|npm run build|pnpm build|yarn build)/.test(cmd)) {
|
|
22
|
+
console.error('[SCC] Build/deploy completed — review results above');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
} catch {
|
|
26
|
+
// noop
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
process.stdout.write(raw);
|
|
30
|
+
}).catch(() => process.exit(0));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { readStdin, runExistingHook, transformToClaude } = require('./adapter');
|
|
3
|
+
readStdin().then(raw => {
|
|
4
|
+
try {
|
|
5
|
+
const input = JSON.parse(raw);
|
|
6
|
+
const claudeInput = transformToClaude(input, {
|
|
7
|
+
tool_input: { file_path: input.path || input.file || '' }
|
|
8
|
+
});
|
|
9
|
+
runExistingHook('post-edit-format.js', JSON.stringify(claudeInput));
|
|
10
|
+
} catch {}
|
|
11
|
+
process.stdout.write(raw);
|
|
12
|
+
}).catch(() => process.exit(0));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { readStdin } = require('./adapter');
|
|
3
|
+
readStdin().then(raw => {
|
|
4
|
+
try {
|
|
5
|
+
const input = JSON.parse(raw);
|
|
6
|
+
const server = input.server || input.mcp_server || 'unknown';
|
|
7
|
+
const tool = input.tool || input.mcp_tool || 'unknown';
|
|
8
|
+
console.error(`[SCC] MCP invocation: ${server}/${tool}`);
|
|
9
|
+
} catch {}
|
|
10
|
+
process.stdout.write(raw);
|
|
11
|
+
}).catch(() => process.exit(0));
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { readStdin } = require('./adapter');
|
|
3
|
+
readStdin().then(raw => {
|
|
4
|
+
try {
|
|
5
|
+
const input = JSON.parse(raw);
|
|
6
|
+
const filePath = input.path || input.file || '';
|
|
7
|
+
if (/\.(env|key|pem)$|\.env\.|credentials|secret/i.test(filePath)) {
|
|
8
|
+
console.error('[SCC] WARNING: Reading sensitive file: ' + filePath);
|
|
9
|
+
console.error('[SCC] Ensure this data is not exposed in outputs');
|
|
10
|
+
}
|
|
11
|
+
} catch {}
|
|
12
|
+
process.stdout.write(raw);
|
|
13
|
+
}).catch(() => process.exit(0));
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { readStdin, runExistingHook, transformToClaude, hookEnabled } = require('./adapter');
|
|
3
|
+
|
|
4
|
+
readStdin()
|
|
5
|
+
.then(raw => {
|
|
6
|
+
try {
|
|
7
|
+
const input = JSON.parse(raw || '{}');
|
|
8
|
+
const cmd = String(input.command || input.args?.command || '');
|
|
9
|
+
|
|
10
|
+
// SFDX validation
|
|
11
|
+
if (hookEnabled('pre:bash:sfdx-validate', ['standard', 'strict']) && (/\bsf\s/.test(cmd) || /\bsfdx\s/.test(cmd))) {
|
|
12
|
+
const claudeInput = transformToClaude(input, {
|
|
13
|
+
tool_input: { command: cmd }
|
|
14
|
+
});
|
|
15
|
+
claudeInput.tool_name = 'Bash';
|
|
16
|
+
runExistingHook('sfdx-validate.js', JSON.stringify(claudeInput));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Git push reminder
|
|
20
|
+
if (hookEnabled('pre:bash:git-push-reminder', ['standard', 'strict']) && /\bgit\s+push\b/.test(cmd)) {
|
|
21
|
+
console.error('[SCC] Review changes before push: git diff origin/main...HEAD');
|
|
22
|
+
}
|
|
23
|
+
} catch {
|
|
24
|
+
// noop
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
process.stdout.write(raw);
|
|
28
|
+
})
|
|
29
|
+
.catch(() => process.exit(0));
|