grails / grails-core

The Grails Web Application Framework
http://grails.org
Apache License 2.0
2.79k stars 950 forks source link

encodeAsHTML is unavailable by default in Grails 3.3.8 projects but other encoders are #13679

Open Razorfang opened 2 months ago

Razorfang commented 2 months ago

Expected Behavior

As specified in the Grails 3.3.8 documentation here, the HTML codec should come as standard, and thus should provide the encodeAsHTML method on all objects. However, I believe that for Grails 3.3.8, it is not the case.

I have created the following tests to verify that codecs work by default due to issues I am having in a project I am trying to port from Grails 2.5.6 to Grails 6.2.0. I am doing this porting in intermediate steps, so I am currently trying to port from Grails 2.5.6 to Grails 3.3.8.

I tried to create a blank project to see if the issue I was having with codecs was isolated to me, but I don't think it is. I feel that I am missing something.

Here are the tests, located in the file src/test/groovy/EncoderTestsSpec.groovy:

import spock.lang.Specification

class EncoderTestsSpec extends Specification {

  def 'Ensure we can call encodeAsHTML successfully in our application'() {

    expect:
    '"<script>"'.encodeAsHTML() == '&quot;&lt;script&gt;&quot;'
  }

  def 'Ensure we can call encodeAsBase64 successfully in our application'() {

    expect:
    '"<script>"'.encodeAsBase64() == 'IjxzY3JpcHQ+Ig=='
  }

  def 'Ensure we can call encodeAsMD5 successfully in our application'() {

    expect:
    '"<script>"'.encodeAsMD5() == '94936c5d221680f28f4128694dfd5c3f'
  }
}

I expect all of these tests to pass.

Actual Behaviour

The base64 and md5 tests pass but the html test does not. I get the following error in my test output:

Condition failed with Exception:

'"<script>"'.encodeAsHTML() == '&quot;&lt;script&gt;&quot;'
             |
             groovy.lang.MissingMethodException: No signature of method: java.lang.String.encodeAsHTML() is applicable for argument types: () values: []
             Possible solutions: encodeAsHex(), encodeAsSHA1(), encodeAsMD5(), encodeAsSHA256()

    at EncoderTestsSpec.Ensure we can call encodeAsHTML successfully in our application(ExampleSpec.groovy:8)
Caused by: groovy.lang.MissingMethodException: No signature of method: java.lang.String.encodeAsHTML() is applicable for argument types: () values: []
Possible solutions: encodeAsHex(), encodeAsSHA1(), encodeAsMD5(), encodeAsSHA256()
    ... 1 more

Steps To Reproduce

import spock.lang.Specification

class EncoderTestsSpec extends Specification {

def 'Ensure we can call encodeAsHTML successfully in our application'() {

expect:
'"<script>"'.encodeAsHTML() == '&quot;&lt;script&gt;&quot;'

}

def 'Ensure we can call encodeAsBase64 successfully in our application'() {

expect:
'"<script>"'.encodeAsBase64() == 'IjxzY3JpcHQ+Ig=='

}

def 'Ensure we can call encodeAsMD5 successfully in our application'() {

expect:
'"<script>"'.encodeAsMD5() == '94936c5d221680f28f4128694dfd5c3f'

} }


### Environment Information

I am using a Macbook air running MacOS Sonoma 14.6.1. I am running grails 3.3.8. The following commands are what I copy-paste to set up my environment:

jenv global 1.8.0.412 export JAVA_HOME=$(jenv javahome) sdk install grails 3.3.8 sdk use grails 3.3.8 sdk install groovy 2.4.15 sdk use groovy 2.4.15 export GRAILS_HOME=$(dirname $(dirname $(which grails))) export GROOVY_HOME=$(dirname $(dirname $(which groovy))) export PATH=$JAVA_HOME/bin:$GRAILS_HOME/bin:$GROOVY_HOME/bin:$PATH


My application.yml file is unchanged from the default:

grails: profile: web codegen: defaultPackage: virtualhome gorm: reactor:

Whether to translate GORM events into Reactor events

        # Disabled by default for performance reasons
        events: false

info: app: name: '@info.app.name@' version: '@info.app.version@' grailsVersion: '@info.app.grailsVersion@' spring: main: banner-mode: "off" groovy: template: check-template-location: false

Spring Actuator Endpoints are Disabled by Default

endpoints: enabled: false jmx: enabled: true


grails: mime: disable: accept: header: userAgents:


hibernate: cache: queries: false use_second_level_cache: false use_query_cache: false dataSource: pooled: true jmxExport: true driverClassName: org.h2.Driver username: sa password: ''

environments: development: dataSource: dbCreate: create-drop url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE test: dataSource: dbCreate: update url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE production: dataSource: dbCreate: none url: jdbc:h2:./prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE properties: jmxEnabled: true initialSize: 5 maxActive: 50 minIdle: 5 maxIdle: 25 maxWait: 10000 maxAge: 600000 timeBetweenEvictionRunsMillis: 5000 minEvictableIdleTimeMillis: 60000 validationQuery: SELECT 1 validationQueryTimeout: 3 validationInterval: 15000 testOnBorrow: true testWhileIdle: true testOnReturn: false jdbcInterceptors: ConnectionState defaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED


My build.gradle file is also completely unchanged:

buildscript { repositories { mavenLocal() maven { url "https://repo.grails.org/grails/core" } } dependencies { classpath "org.grails:grails-gradle-plugin:$grailsVersion" classpath "org.grails.plugins:hibernate5:${gormVersion-".RELEASE"}" classpath "com.bertramlabs.plugins:asset-pipeline-gradle:2.14.8" } }

version "0.1" group "virtualhome"

apply plugin:"eclipse" apply plugin:"idea" apply plugin:"war" apply plugin:"org.grails.grails-web" apply plugin:"asset-pipeline" apply plugin:"org.grails.grails-gsp"

repositories { mavenLocal() maven { url "https://repo.grails.org/grails/core" } }

dependencies { compile "org.springframework.boot:spring-boot-starter-logging" compile "org.springframework.boot:spring-boot-autoconfigure" compile "org.grails:grails-core" compile "org.springframework.boot:spring-boot-starter-actuator" compile "org.springframework.boot:spring-boot-starter-tomcat" compile "org.grails:grails-web-boot" compile "org.grails:grails-logging" compile "org.grails:grails-plugin-rest" compile "org.grails:grails-plugin-databinding" compile "org.grails:grails-plugin-i18n" compile "org.grails:grails-plugin-services" compile "org.grails:grails-plugin-url-mappings" compile "org.grails:grails-plugin-interceptors" compile "org.grails.plugins:cache" compile "org.grails.plugins:async" compile "org.grails.plugins:scaffolding" compile "org.grails.plugins:hibernate5" compile "org.hibernate:hibernate-core:5.1.5.Final" compile "org.grails.plugins:gsp" console "org.grails:grails-console" profile "org.grails.profiles:web" runtime "org.glassfish.web:el-impl:2.1.2-b03" runtime "com.h2database:h2" runtime "org.apache.tomcat:tomcat-jdbc" runtime "com.bertramlabs.plugins:asset-pipeline-grails:2.14.8" testCompile "org.grails:grails-gorm-testing-support" testCompile "org.grails:grails-web-testing-support" }

bootRun { jvmArgs('-Dspring.output.ansi.enabled=always') addResources = true String springProfilesActive = 'spring.profiles.active' systemProperty springProfilesActive, System.getProperty(springProfilesActive) }

assets { minifyJs = true minifyCss = true }



### Example Application

_No response_

### Version

3.3.8
Razorfang commented 1 month ago

Do later versions of Grails have this same issue? Can I fix this by skipping version 3 and upgrading from 2.5.6 to version 4.x or later?

Razorfang commented 1 month ago

Okay, so I think there might have been some confusion here.

I created another new Grails 3.3.8 application with the following index.gsp file:

<!doctype html>
<html>
<head>
    <meta name="layout" content="main"/>
    <title>Welcome to Grails</title>
</head>
<body>
<h1>${"Test".encodeAsHTML()}</h1>
</body>
</html>

This displayed with zero issue.

So it seems that encodeAsHTML cannot be called inside groovy files, only gsp files.