Read only URL and Form scope

There’s no reason to write to the URL or Form scopes. Doing so just causes confusion for developers later on in code when writing/debugging. Can these be made to be read only in Lucee 6? Perhaps as configurable option in Lucee admin so backwards compatibility can be maintained for legacy software.

FormVars = Form.duplicate(); // to work with a modified writable form scope

The same could easily apply to CGI scope and maybe others.

2 Likes

setting defaults on those scopes is a perfectly good reason for them to be writeable

if implemented, that would only be an opt in change

all that said, being able to generally make a struct readonly is an interesting idea, @markdrew was asking for a const type in a chat we had this week

3 Likes

That’s a reason for changing some struct that contains values that were sent with a request. It’s not a reason to change the original values.

And @fast.tuna even covers that in his original post.

There is a strong case to be made that the form / url / headers / cookies(*) data structures are read-only because they represent what was received with the request from the client, and CFML can’t change what was received from the client, ergo it makes sense to not be able to change them.


That said, as this is easily dealt with via coding guidelines (“don’t change the form scope, etc”, then don’t allow code that changes the form scope into yer codebase, if that’s yer position on it), not sure it’s something that ought to be given much focus in Lucee.

(*) I can’t remember if changes to the cookies struct are sent back with the response, or they actively have to be set in the response with <cfcookie>?

Fair enough, although there is a use case for reusing variables (for resource limiting/concurrency), regardless of the scope. Off the top of my head converting from a numeric message returned in the url to a human readable one is a good example:

switch( url.msg ) {
	
	case 201:
		url.msg = 'Your request was successful.';
	break;

	case 404:
		url.msg = 'Your request produced no results.';
	break;

	case 500:
		url.msg = 'There was an error processing your request.';
	break
}

echo( url.msg );

That said, I typically use a request context (rc) ‘scope’ and never touch url or form directly… except in legacy code.

They’re persisted back to the browser in the response.

100% agree.

How do you propose / intend to sanitize these untrusted variables if read only?

Rule 1: never trust or blindly accept user input. Classic and relevant.

Take a deep copy, sanitise the deep copy and use what’s been sanitised.

When working on large pieces of code you can then dump both the original and copy to compare. From my experience I’ve found It’s quicker and less tedious to code when you can dump Form/URL scopes and know beyond any doubt that they haven’t been modified without having to backtrack to certify this.

This is how I caught the offending code. Leaving them as writable avoids steepening the learning curve for the language but I feel bad devs exploit these philosophy to stay in this language when really they should have moved on.

I’ve also found most devs haven’t been following this so was kinda thinking Lucee could by default encourage best practice over making the language easier to pick up.

Or you can use half the memory and save CPU by sanitizing the input.

Short version: I agree this probably makes sense for your use case. I’m not sure it’s a good idea for the engine.

I’d like to keep my webapp firewall / Lucee script protection sanitizing inputs by having direct access to modify these values before the framework gets ahold of them without having to change server settings during the update (if on by default). If off by default, would most intentionally turn it on?

I would rather see Immutable object support added to cfml where it’s up to the devs as to which types/scopes are unable to be changed.

I’ll just second the idea that I update the existing scopes all the time when setting default values. This is especially helpful for Checkbox form inputs that don’t submit a value unless check. I have lots of code like this in my controllers:

param name="form.isSubmitted" type="boolean" default=false;
// Setup default checkbox values.
param name="form.thingOneEnabled" type="boolean" default=false;
param name="form.thingTwoEnabled" type="boolean" default=false;
param name="form.thingThreeEnabled" type="boolean" default=false;

Just my personal approach.

3 Likes

Can anyone explain this to me in small, byte sized (Pun intended) pieces why We need a READ ONLY chunk of memory area for form or URL at the dynamic engine level for something that isnt specifically engine related?

I like the option, I am just trying to understand the USE CASE for this. IE Sure I can write code that blankly blocks all post methods at the application engine level, but if that really needed?

In many other languages they dont bother as this is controlled at the LOW LEVEL, which is the web server.

In HTTPD its

<Location />
   <LimitExcept GET POST>
       order deny,allow
       deny from all
   </LimitExcept>
</Location>

In Tomcat you would add this to your server.xml

<security-constraint>
  <web-resource-name>FOO PLACE</web-resource-name>
  <url-pattern>/FOO/*</url-pattern>
  <http-method>PUT</http-method>
  <http-method>DELETE</http-method>
  <http-method>OPTIONS</http-method>
  <http-method>GET</http-method>
 </web-resource-collection>
 <auth-constraint />
 </security-constraint>

In IIS you would put this in as a Request filtering, or you could build the XML .web config file.