Ok,I think I have confirmation that actions for non-rendered components are indeed not reachable according to the spec.
Section 2.2.2 of the specification states:
During the Apply Request Values phase, the JSF implementation must
call the processDecodes() method of the UIViewRoot of the component
tree.[P1-end] This will normally cause the processDecodes() method of
each component in the tree to be called recursively, as described in
the Javadocs for the UIComponent.processDecodes() method.
It also states:
During the decoding of request values, some components perform special
processing, including: Components that implement ActionSource (such as
UICommand), which recognize that they were activated, will queue an
ActionEvent. The event will be delivered at the end of Apply Request
Values phase if the immediate property of the component is true, or at
the end of Invoke Application phase if it is false.
So ActionSource components will only queue an action if they are processed according to processDecodes. Looking at the javadoc for that:
Perform the component tree processing required by the Apply Request
Values phase of the request processing lifecycle for all facets of
this component, all children of this component, and this component
itself, as follows.
- If the rendered property of this UIComponent is false, skip further
processing.
So the first check must be whether or not the component is rendered, and if not, skip the rest. The ActionSource is never queued and the action never invoked.
One more note, it does appear that ViewState is only reliable for CSFR prevention as of JSF 2.2 per the spec:
https://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-869
Previous implementations were apparently too predictable and didn't cover GET requests. The spec now requires this.
So, while it may still be a good practice to secure the server side as well, it does appear it is sufficient to control rendering of the ActionSource component.