Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WW-5343 Make SecurityMemberAccess an extensible bean #791

Merged
merged 20 commits into from
Nov 26, 2023
Merged

Conversation

kusalk
Copy link
Member

@kusalk kusalk commented Nov 14, 2023

WW-5343

This will allow applications to easily extend the capabilities of SecurityMemberAccess. It additionally allows us to significantly simplify the configuration loading.

Base automatically changed from WW-5350-allowlist-2 to master November 14, 2023 13:39
@@ -133,7 +134,6 @@ public class DefaultConfiguration implements Configuration {
Map<String, Object> constants = new HashMap<>();
constants.put(StrutsConstants.STRUTS_DEVMODE, Boolean.FALSE);
constants.put(StrutsConstants.STRUTS_OGNL_LOG_MISSING_PROPERTIES, Boolean.FALSE);
constants.put(StrutsConstants.STRUTS_OGNL_ENABLE_EVAL_EXPRESSION, Boolean.FALSE);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This constant is not required for bootstrapping

}

private static Set<String> toClassesSet(String newDelimitedClasses) throws ConfigurationException {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved these utility methods into their own class

private boolean enableEvalExpression;

private Set<String> excludedClasses = emptySet();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the configuration is now injected directly into SecurityMemberAccess except for devMode configuration

public Set<String> getExcludedClasses() {
return excludedClasses;
return toClassesSet(container.getInstance(String.class, StrutsConstants.STRUTS_EXCLUDED_CLASSES));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deprecated getters are still functional for API compatibility

@@ -922,8 +855,7 @@ protected Map<String, Object> createDefaultContext(Object root, ClassResolver cl
resolver = container.getInstance(CompoundRootAccessor.class);
}

SecurityMemberAccess memberAccess = new SecurityMemberAccess(allowStaticFieldAccess);
memberAccess.disallowProxyMemberAccess(disallowProxyMemberAccess);
SecurityMemberAccess memberAccess = container.getInstance(SecurityMemberAccess.class);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a prototype bean so will be a new instance per context

@@ -31,15 +31,19 @@ public interface MemberAccessValueStack {
* @deprecated please use {@link #useExcludeProperties(Set)}
*/
@Deprecated
void setExcludeProperties(Set<Pattern> excludeProperties);
default void setExcludeProperties(Set<Pattern> excludeProperties) {
useExcludeProperties(excludeProperties);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lifted the deprecated implementations into the interface for cleanliness

@@ -234,6 +234,8 @@ public final class StrutsConstants {
/** The name of the parameter to determine whether static field access will be allowed in OGNL expressions or not */
public static final String STRUTS_ALLOW_STATIC_FIELD_ACCESS = "struts.ognl.allowStaticFieldAccess";

public static final String STRUTS_MEMBER_ACCESS = "struts.securityMemberAccess";
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extension point!

@@ -31,33 +31,6 @@ public void setUp() throws Exception {
ognlUtil = container.getInstance(OgnlUtil.class);
}

public void testDefaultExcludes() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleted tests whose functionality is covered elsewhere

* Test a raw OgnlValueStackFactory and OgnlValueStack generated by it
* when no static access flags are set (not present in configuration).
*/
public void testOgnlValueStackFromOgnlValueStackFactoryNoFlagsSet() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This flag is now a required constant so this test is no longer applicable

}

@Test
public void defaultExclusionList() throws Exception {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved/rewrote some tests here from OgnlUtilTest

@kusalk kusalk marked this pull request as ready for review November 22, 2023 14:20
@@ -385,6 +386,7 @@ protected Container createBootstrapContainer(List<ContainerProvider> providers)
builder.factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON);
builder.factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON);
builder.factory(OgnlUtil.class, Scope.SINGLETON);
builder.factory(SecurityMemberAccess.class, Scope.PROTOTYPE);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this is a good idea, attacker could get access to the SMA just by having access to the Container

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given it's prototype scope, I don't believe it to make any difference

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is, the container would return a new instance, and not the one that is being used by the ValueStack

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant it will be possible creating a new instance via containers methods, yet we excluded the whole package com.opensymphony.xwork2.inject which means attacker cannot use container in the first place.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, I also don't think there's anything useful an attacker can do with a fresh SecurityMemberAccess instance

@kusalk kusalk requested a review from lukaszlenart November 26, 2023 05:55
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 21 Code Smells

83.6% 83.6% Coverage
0.0% 0.0% Duplication

Copy link
Member

@lukaszlenart lukaszlenart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm good but please update the docs to address that a new extension has been added (or at least update the ticket to inform users about it)
https://struts.apache.org/plugins/plugins-architecture#extension-points

@kusalk
Copy link
Member Author

kusalk commented Nov 26, 2023

Documentation PR is here: apache/struts-site#213

@kusalk kusalk merged commit a60842a into master Nov 26, 2023
10 checks passed
@kusalk kusalk deleted the WW-5343-sec-extend branch November 26, 2023 08:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants