Merge pull request #228 from apache/fix/tiles

Cleans up docs about Tiles
diff --git a/source/core-developers/basic-validation.md b/source/core-developers/basic-validation.md
index 32a7c4e..0c9f93a 100644
--- a/source/core-developers/basic-validation.md
+++ b/source/core-developers/basic-validation.md
@@ -70,6 +70,7 @@
 		return name;
 	}
 
+	@StrutsParameter
 	public void setName(String name) {
 		this.name = name;
 	}
@@ -78,6 +79,7 @@
 		return age;
 	}
 
+	@StrutsParameter
 	public void setAge(int age) {
 		this.age = age;
 	}
@@ -86,6 +88,7 @@
 		return answer;
 	}
 
+	@StrutsParameter
 	public void setAnswer(String answer) {
 		this.answer = answer;
 	}
diff --git a/source/core-developers/client-validation.md b/source/core-developers/client-validation.md
index 106eb75..987be8d 100644
--- a/source/core-developers/client-validation.md
+++ b/source/core-developers/client-validation.md
@@ -74,6 +74,7 @@
 		return name;
 	}
 
+	@StrutsParameter
 	public void setName(String name) {
 		this.name = name;
 	}
@@ -82,6 +83,7 @@
 		return age;
 	}
 
+	@StrutsParameter
 	public void setAge(int age) {
 		this.age = age;
 	}
@@ -90,6 +92,7 @@
 		return answer;
 	}
 
+	@StrutsParameter
 	public void setAnswer(String answer) {
 		this.answer = answer;
 	}
diff --git a/source/core-developers/conversion-validator.md b/source/core-developers/conversion-validator.md
index 5f42510..f0fdf75 100644
--- a/source/core-developers/conversion-validator.md
+++ b/source/core-developers/conversion-validator.md
@@ -81,7 +81,8 @@
     public Integer getMyIntegerField() { 
         return this.myIntegerField; 
     }
-    
+
+    @StrutsParameter
     public void setMyIntegerField(Integer myIntegerField) {
         this.myIntegerField = myIntegerField;
     }
diff --git a/source/core-developers/file-upload-interceptor.md b/source/core-developers/file-upload-interceptor.md
index 5dd64b6..28427e2 100644
--- a/source/core-developers/file-upload-interceptor.md
+++ b/source/core-developers/file-upload-interceptor.md
@@ -90,14 +90,17 @@
        private String contentType;
        private String filename;
 
+       @StrutsParameter
        public void setUpload(File file) {
           this.file = file;
        }
 
+       @StrutsParameter
        public void setUploadContentType(String contentType) {
           this.contentType = contentType;
        }
 
+       @StrutsParameter
        public void setUploadFileName(String filename) {
           this.filename = filename;
        }
diff --git a/source/core-developers/file-upload.md b/source/core-developers/file-upload.md
index d360311..dee678e 100644
--- a/source/core-developers/file-upload.md
+++ b/source/core-developers/file-upload.md
@@ -101,14 +101,17 @@
     private String contentType;
     private String filename;
 
+    @StrutsParameter
     public void setUpload(File file) {
         this.file = file;
     }
 
+    @StrutsParameter
     public void setUploadContentType(String contentType) {
         this.contentType = contentType;
     }
 
+    @StrutsParameter
     public void setUploadFileName(String filename) {
         this.filename = filename;
     }
@@ -185,6 +188,7 @@
         return this.uploads;
     }
 
+    @StrutsParameter
     public void setUpload(File[] upload) {
         this.uploads = upload;
     }
@@ -193,6 +197,7 @@
         return this.uploadFileNames;
     }
 
+    @StrutsParameter
     public void setUploadFileName(String[] uploadFileName) {
         this.uploadFileNames = uploadFileName;
     }
@@ -201,6 +206,7 @@
         return this.uploadContentTypes;
     }
 
+    @StrutsParameter
     public void setUploadContentType(String[] uploadContentType) {
         this.uploadContentTypes = uploadContentType;
     }
@@ -232,6 +238,7 @@
         return this.uploads;
     }
 
+    @StrutsParameter
     public void setUpload(List<File> uploads) {
         this.uploads = uploads;
     }
@@ -240,6 +247,7 @@
         return this.uploadFileNames;
     }
 
+    @StrutsParameter
     public void setUploadFileName(List<String> uploadFileNames) {
         this.uploadFileNames = uploadFileNames;
     }
@@ -248,6 +256,7 @@
         return this.uploadContentTypes;
     }
 
+    @StrutsParameter
     public void setUploadContentType(List<String> contentTypes) {
         this.uploadContentTypes = contentTypes;
     }
diff --git a/source/core-developers/type-conversion-annotation.md b/source/core-developers/type-conversion-annotation.md
index f163f93..26aacdd 100644
--- a/source/core-developers/type-conversion-annotation.md
+++ b/source/core-developers/type-conversion-annotation.md
@@ -92,21 +92,25 @@
    private HashMap keyValues = null;
 
    @TypeConversion(type = ConversionType.APPLICATION)
+   @StrutsParameter
    public void setConvertInt( String convertInt ) {
        this.convertInt = convertInt;
    }
 
    @TypeConversion(converterClass = XWorkBasicConverter.class)
+   @StrutsParameter
    public void setConvertDouble( String convertDouble ) {
        this.convertDouble = convertDouble;
    }
 
    @TypeConversion(rule = ConversionRule.COLLECTION, converterClass = String.class)
+   @StrutsParameter
    public void setUsers( List users ) {
        this.users = users;
    }
 
    @TypeConversion(rule = ConversionRule.MAP, converterClass = BigInteger.class)
+   @StrutsParameter
    public void setKeyValues( HashMap keyValues ) {
        this.keyValues = keyValues;
    }
diff --git a/source/core-developers/type-conversion.md b/source/core-developers/type-conversion.md
index dd2918f..2bb1253 100644
--- a/source/core-developers/type-conversion.md
+++ b/source/core-developers/type-conversion.md
@@ -322,6 +322,7 @@
     private List beanList = new ArrayList();
     private Map beanMap = new HashMap();
 
+    @StrutsParameter(depth = 2)
     public List getBeanList() {
         return beanList;
     }
@@ -330,6 +331,7 @@
         this.beanList = beanList;
     }
 
+    @StrutsParameter(depth = 2)
     public Map getBeanMap() {
         return beanMap;
     }
diff --git a/source/core-developers/using-non-field-validators.md b/source/core-developers/using-non-field-validators.md
index da37d0d..b5e3a2e 100644
--- a/source/core-developers/using-non-field-validators.md
+++ b/source/core-developers/using-non-field-validators.md
@@ -42,6 +42,7 @@
         return someText;
     }
 
+    @StrutsParameter
     public void setSomeText(String someText) {
         this.someText = someText;
     }
@@ -50,6 +51,7 @@
         return someTextRetype;
     }
 
+    @StrutsParameter
     public void setSomeTextRetype(String someTextRetype) {
         this.someTextRetype = someTextRetype;
     }
@@ -58,6 +60,7 @@
         return someTextRetypeAgain;
     }
 
+    @StrutsParameter
     public void setSomeTextRetypeAgain(String someTextRetypeAgain) {
         this.someTextRetypeAgain = someTextRetypeAgain;
     }
diff --git a/source/core-developers/using-visitor-field-validator.md b/source/core-developers/using-visitor-field-validator.md
index 77403e1..34d7029 100644
--- a/source/core-developers/using-visitor-field-validator.md
+++ b/source/core-developers/using-visitor-field-validator.md
@@ -40,6 +40,7 @@
 
     private User user;
 
+    @StrutsParameter(depth = 1)
     public User getUser() {
         return user;
     }
diff --git a/source/core-developers/validation-annotation.md b/source/core-developers/validation-annotation.md
index 79aea0a..f101b10 100644
--- a/source/core-developers/validation-annotation.md
+++ b/source/core-developers/validation-annotation.md
@@ -48,6 +48,7 @@
 
      @RequiredFieldValidator(type = ValidatorType.FIELD, message = "You must enter a value for bar.")
      @IntRangeFieldValidator(type = ValidatorType.FIELD, min = "6", max = "10", message = "bar must be between ${min} and ${max}, current value is ${bar}.")
+     @StrutsParameter
      public void setBar(int bar) {
          this.bar = bar;
      }
diff --git a/source/core-developers/validation.md b/source/core-developers/validation.md
index 7e18143..0653f0a 100644
--- a/source/core-developers/validation.md
+++ b/source/core-developers/validation.md
@@ -292,6 +292,7 @@
 @RequiredStringValidator(key = "errors.required", messageParams = {
     "getText('username.field.name')"
 })
+@StrutsParameter
 public void setUsername(String username) {
     this.username = username;
 }
diff --git a/source/core-developers/wildcard-mappings.md b/source/core-developers/wildcard-mappings.md
index c697fcb..edeef03 100644
--- a/source/core-developers/wildcard-mappings.md
+++ b/source/core-developers/wildcard-mappings.md
@@ -96,6 +96,8 @@
 @Namespace{"/users/{userID}");
 public class DetailsAction exends ActionSupport {
   private Long userID;
+
+  @StrutsParameter
   public void setUserID(Long userID) {...}
 }
 ```
diff --git a/source/getting-started/coding-actions.md b/source/getting-started/coding-actions.md
index d6355d0..e7ee6a5 100644
--- a/source/getting-started/coding-actions.md
+++ b/source/getting-started/coding-actions.md
@@ -105,6 +105,7 @@
     return userName;
 }
 
+@StrutsParameter
 public void setUserName(String userName) {
     this.userName = userName;
 }
@@ -128,7 +129,8 @@
 ![coding_actions_form_submit_result.png](attachments/coding_actions_form_submit_result.png)
 
 When the form is submitted, Struts will call any set methods of the HelloWorldAction class that match the form field 
-names. So in this example method `setUserName` was called and passed the value the user entered in the `userName` form field.
+names and are annotated with `@StrutsParameter`. So in this example method `setUserName` was called and passed the value
+the user entered in the `userName` form field.
 
 On the `index.jsp` we also have a Struts 2 action link (see tutorial [Using Struts 2 Tags](using-tags)) that includes 
 a query string parameter: `userName=Bruce+Phillips`. If you click on that link you should see the following result:
diff --git a/source/getting-started/processing-forms.md b/source/getting-started/processing-forms.md
index 1579d69..7245e3e 100644
--- a/source/getting-started/processing-forms.md
+++ b/source/getting-started/processing-forms.md
@@ -128,7 +128,7 @@
 Note the four Struts 2 textfield tags. Each tag has a name value that includes an attribute of the `Person` class 
 (e.g. `firstName`). The name attribute's value also has a reference to an object called `personBean`. This object is 
 of type `Person`. When we create the Action class that handles this form submission, we'll have to specify that object 
-in that Action class (see below).
+in that Action class and annotate it (see below).
 
 The complete name value, `personBean.firstName`, instructs Struts 2 to use the input value for that textfield as 
 the argument to the personBean object's `setFirstName` method. So if the user types "Bruce" in the textfield that has 
@@ -166,7 +166,8 @@
         
         return SUCCESS;
     }
-    
+
+    @StrutsParameter(depth = 1)
     public Person getPersonBean() {
         return personBean;
     }
@@ -178,8 +179,18 @@
 }
 ```
 
-In the `Register` class note that we've declared an attribute named `personBean` of type `Person` and there is a public 
-get and set method for this object.
+In the `Register` class, note that we've declared an attribute named `personBean` of type `Person`, there are public 
+getter and setter methods for this object, and the getter is annotated with `@StrutsParameter(depth = 1)`.
+
+In the previous [Coding Struts 2 Actions](coding-actions) tutorial, we annotated the username **setter**, which took a
+simple String as its parameter type, with `@StrutsParameter`. In this example, we are using a "Bean" object (sometimes
+referred to as a DTO or model object) to encapsulate the form data. When we choose to use a DTO instead of a primitive,
+String, or other TypeConverter supported object, we must annotate the **getter** method instead, and also assign a depth
+corresponding to how deep the DTO graph is. In this case, the `Person` object does not have any further DTOs or
+collections within it, so a depth of 1 will suffice.
+
+For more information on these annotations and their security implications, please refer
+to [Security](../security/index#defining-and-annotating-your-action-parameters).
 
 The `Register` class also overrides the `execute` method. The `execute` method is the one we will specify in the 
 `struts.xml` to be called in response to the register action. In this example, the `execute` method just returns 
@@ -187,12 +198,13 @@
 method we would call upon other classes (Service objects) to perform the business processing of the form, such as storing 
 the user's input into a data repository.
 
-The `personBean` object of type `Person` declared in the Register Action class matches the `personBean` name we used in 
-the form's textfields. When the form is submitted, the Struts 2 framework will inspect the Action class and look for 
-an object named `personBean`. It will create that object using the `Person` class's default constructor. Then for each 
-form field that has a name value of personBean.someAttribute (e.g `personBean.firstName`) it will call the personBean's 
-public set method for that attribute and pass it the form field's value (the user input). This all happens before 
-the execute method occurs.
+The `personBean` getter of return type `Person` declared in the Register Action class matches the `personBean` name we
+used in the form's textfields. When the form is submitted, the Struts 2 framework will inspect the Action class and look
+for a getter for `personBean`. If it returns `null` and a matching setter exists, it will create that object using the
+`Person` class's default constructor and set it using the setter. Note that the setter can be omitted if your Action
+initialises the field on construction. Then for each form field that has a name value of personBean.someAttribute 
+(e.g `personBean.firstName`) it will call the personBean's public set method for that attribute and pass it the form
+field's value (the user input). This all happens before the execute method occurs.
 
 When Struts 2 runs the `execute` method of class `Register`, the `personBean` object in class `Register` now has values 
 for its instance fields that are equal to the values the user entered into the corresponding form fields.
diff --git a/source/plugins/junit/index.md b/source/plugins/junit/index.md
index 94d5127..225905e 100644
--- a/source/plugins/junit/index.md
+++ b/source/plugins/junit/index.md
@@ -44,6 +44,7 @@
         return name;
     }
 
+    @StrutsParameter
     public void setName(String name) {
         this.name = name;
     }
diff --git a/source/plugins/portlet/struts-2-portlet-tutorial.md b/source/plugins/portlet/struts-2-portlet-tutorial.md
index ff1cf2f..2dd99a3 100644
--- a/source/plugins/portlet/struts-2-portlet-tutorial.md
+++ b/source/plugins/portlet/struts-2-portlet-tutorial.md
@@ -187,10 +187,12 @@
    private String name;
    private String url;
 
+   @StrutsParameter
    public void setName(String name) {
       this.name = name;
    }
 
+   @StrutsParameter
    public void setUrl(String url) {
       this.url = url;
    }
@@ -259,10 +261,12 @@
 	
    private PortletPreferences portletPreferences;
 
+   @StrutsParameter
    public void setName(String name) {
       this.name = name;
    }
 
+   @StrutsParameter
    public void setUrl(String url) {
       this.url = url;
    }
@@ -484,6 +488,7 @@
 	
    private PortletPreferences portletPreferences;
 
+   @StrutsParameter
    public void setBookmarkName(String bookmarkName) {
       this.bookmarkName = bookmarkName;
    }
@@ -563,7 +568,8 @@
    public String getOldName() {
       return oldName;
    }
-	
+
+   @StrutsParameter
    public void setOldName(String oldName) {
       this.oldName = oldName;
    }
@@ -572,10 +578,12 @@
       return url;
    }
 
+   @StrutsParameter
    public void setUrl(String url) {
       this.url = url;
    }
 
+   @StrutsParameter
    public void setName(String name) {
       this.name = name;
    }
diff --git a/source/security/index.md b/source/security/index.md
index 5735feb..ab5c64f 100644
--- a/source/security/index.md
+++ b/source/security/index.md
@@ -113,9 +113,76 @@
 <%@ page contentType="text/html; charset=UTF-8" %>
 ```
 
+### Defining and annotating your Action parameters
+
+> Note: Since 6.4 using `struts.parameters.requireAnnotations=true`. Or by default from 7.0.
+
+Request parameters, such as those submitted by a form, can be stored on your Struts Action class by defining getters and
+setters for them. For example, if you have a form with a field called `name`, you can store the value of that field by
+defining a `public void setName(String name)` method on your Action class, and then importantly, annotating this method
+with `@StrutsParameter`. The presence of this annotation indicates that the method is intended for parameter injection
+and is safe to be invoked by any user who can view the Action.
+
+```java
+private String name;
+
+@StrutsParameter
+public void setName(String name) {
+    this.name = name;
+}
+```
+
+If you wish to populate a DTO (Data Transfer Object) instead of setting the parameters directly on the Action class, you
+can define a getter for the DTO on your Action class instead. For example, define a method `public MyDto getFormData()`
+which is also annotated by `@StrutsParameter(depth = 1)`. Then, a parameter with name `formData.fullName` will be mapped
+to the setter `setFullName` on that DTO. Note that the `@StrutsParameter` annotation has a `depth` field which dictates
+the depth to which parameter injection is permitted. The default value is 0, which only allows setting parameters
+directly on the Action class as in the first example. A `depth` of 1 indicates that the immediate public properties of
+an object returned by the getter are permitted to be set. If you have further nested objects, you can increase
+the `depth` accordingly. Do not set this `depth` field to a value greater than the minimum required for your use case.
+
+```java
+private MyDto formData = new MyDto();
+
+@StrutsParameter(depth = 1)
+public MyDto getFormData() {
+    return formData;
+}
+
+public static class MyDto {
+    private String fullName;
+
+    public void setFullName(String fullName) {
+        this.fullName = fullName;
+    }
+}
+```
+
+It is critical that any method you annotate with `@StrutsParameter` is safe for any user who can view that corresponding
+action to invoke (including any public methods on objects returned by that method and so forth). Any getters you
+annotate should only ever return a DTO or a collection/hierarchy of DTOs. Do NOT mix business logic or service
+references with your parameter injection methods and DTOs. Additionally, any database DTOs should be entirely separate
+from request parameter/form DTOs.
+
+Do NOT, under any circumstance, annotate a method that returns one of the following unsafe objects:
+- live Hibernate persistent objects
+- container or Spring-managed beans, or any other live components/services
+- objects (or objects that contain references to objects) that contain setter methods that are used for anything other
+  than setting form parameter values
+
+If you are finding updating your application with this new annotation time-consuming, you can temporarily combine the
+above option with `struts.parameters.requireAnnotations.transitionMode=true`. When this mode is enabled, only 'nested'
+parameters, i.e. DTOs or Collections represented by public getters on Action classes, will require annotations. This
+means public setters will still be exposed for parameter injection. Notably,
+the [auto-allowlisting capability](#allowlist-capability), which is also supported by these annotations, is not degraded
+in any way, so it proves a useful transitioning option for applications that wish to enable the OGNL allowlist as soon
+as possible.
+
 ### Do not define setters when not needed
 
-You should carefully design your actions without exposing anything via setters and getters, thus can leads to potential 
+> Note: Only relevant if you are not using `struts.parameters.requireAnnotations=true` as per the previous section.
+
+You should carefully design your actions without exposing anything via setters and getters, this can lead to potential 
 security vulnerabilities. Any action's setter can be used to set incoming untrusted user's value which can contain 
 suspicious expression. Some Struts `Result`s automatically populate params based on values in 
 `ValueStack` (action in most cases is the root) which means incoming value will be evaluated as an expression during 
@@ -307,12 +374,14 @@
 
 Note that before disabling access to the ActionContext from OGNL expressions, you should ensure that your application
 does not rely on this capability. OGNL expressions may access the context directly using the `#` operator, or indirectly
-using the OgnlValueStack's fallback to context lookup capability. As of Struts 6.4.0, the Set and Action Struts
-components require ActionContext access from OGNL expressions.
+using the OgnlValueStack's fallback to context lookup capability. As of Struts 6.4.0, the Set, Iterator and Action
+Struts components require ActionContext access from OGNL expressions.
 
 To disable access to the ActionContext from OGNL expressions, set the following constants in your `struts.xml` or
-`struts.properties` file. Please also refer to the documentation below for further details on these configuration
-options.
+`struts.properties` file. The option `struts.ognl.excludedNodeTypes` is an [OGNL Guard](#Struts-OGNL-Guard) setting
+which completely forbids the context accessing syntax node. The `struts.ognl.valueStackFallbackToContext` option
+disables ValueStack behaviour which allows the context to be accessed indirectly via a fallback behaviour triggered when
+an OGNL expression does not evaluate to a valid value.
 
 ```xml
 <constant name="struts.ognl.valueStackFallbackToContext" value="false"/>
@@ -362,32 +431,34 @@
 
 #### Additional Options
 
-We additionally recommend enabling the following options and hope to enable them by default in a future major version.
+We additionally recommend enabling the following options (enabled by default in 7.0).
 
  * `struts.ognl.allowStaticFieldAccess=false` - static methods are always blocked, but static fields can also optionally be blocked
  * `struts.disallowProxyMemberAccess=true` - disallow proxied objects from being used in OGNL expressions as they may present a security risk
  * `struts.disallowDefaultPackageAccess=true` - disallow access to classes in the default package which should not be used in production
  * `struts.ognl.disallowCustomOgnlMap=true` - disallow construction of custom OGNL maps which can be used to bypass the SecurityMemberAccess policy
- * `struts.ognl.valueStackFallbackToContext=false` - disable fallback to OGNL context lookup if expression does not evaluate to a valid value
 
 #### Allowlist Capability
 
-> Note: since Struts 6.4.
+> Note: Since Struts 6.4. Or by default from 7.0.
 
-For even more stringent OGNL protection, we recommend enabling the allowlist capability with `struts.allowlist.enable`.
+For the most stringent OGNL protection, we recommend enabling the allowlist capability with `struts.allowlist.enable`.
 
 Now, in addition to enforcing the exclusion list, classes involved in OGNL expression must also belong to a list of
 allowlisted classes and packages. By default, all required Struts classes are allowlisted as well as any classes that
 are defined in your `struts.xml` package configurations.
 
+We highly recommend enabling the [parameter annotation](#defining-and-annotating-your-action-parameters) capability to
+ensure any necessary parameter injection types are allowlisted, in addition to its other benefits.
+
 You can add additional classes and packages to the allowlist with:
 
 - `struts.allowlist.classes`: comma-separated list of allowlisted classes.
 - `struts.allowlist.packages`: comma-separated list of allowlisted packages, matched using string comparison via
-  `startWith`. Note that classes in subpackages are also allowlisted.
+  `startsWith`. Note that classes in subpackages are also allowlisted.
 
-Generally, the only additional classes or packages you will need to configure are those model classes that you wish to
-be constructed/manipulated by Struts form submissions (i.e. parameter injected).
+Depending on the functionality of your application, you may not need to manually allowlist any classes. Please monitor
+your application logs for any warnings about blocked classes and add them to the allowlist as necessary.
 
 #### Extensibility
 
@@ -410,7 +481,7 @@
 excluded node types. This will mitigate against a host of String concatenation attacks.
 
 For applications using a minimal number of Struts features, you may find the following list a good starting point.
-Please be aware that this list WILL break certain Struts features:
+Please be aware that this list WILL break certain Struts features.
 
 ```xml
 <constant name="struts.ognl.excludedNodeTypes"