Quantcast
Channel: Adam Cameron's Dev Blog
Viewing all 1332 articles
Browse latest View live

ColdFusion 11: good news... will be able to run on Linux too!

$
0
0
G'day:
Just a short one... I figured it would be good if someone publicises this fact a bit more:


So that's cool! They're gonna implement the new PDF functionality on Linux too. I really don't think Adobe did themselves any PR favours by initially saying it was Windows only, but at least they are recovering a bit here.

Now all we need to do is to be a bit more emphatic that having this feature as enterprise-only is not on, either.

Oh... speaking of Linux... I also noticed this comment on the Adobe ColdFusion blog earlier:

Ram Kulkarni // Mar 11, 2014 at 7:49 PM
@Danny, ColdFusion Thunder can now run on Linux. I forgot to list this point. I have updated the post now.
So that is good news too. And perhaps something Adobe should be making more of a point of publicising.

Anyway... good news.

Cheers.

--
Adam

Testbox: investigating which method/function/callback gets called when

$
0
0
G'day:
I had a bit of a wrestle with working out a glitch in my TestBox code y/day, and as a result ended up with a test CFC which shows where and when everything seems to run. I'll reproduce it here in case it saves anyone some time.




// TestExecutionSequence.cfc
component extends="testbox.system.testing.BaseSpec" {

function beforeAll(){
writeOutput("in beforeAll()<br>");
}
function afterAll(){
writeOutput("in afterAll()<br>");
}

function run(){
writeOutput("top of run()<br>");

describe("outer describe", function(){
writeOutput("top of outer describe()'s callback<br>");

beforeEach(function(){
writeOutput("in outer describe()'s beforeEach()<br>");
});

afterEach(function(){
writeOutput("in outer describe()'s afterEach()<br>");
});

describe("inner describe", function(){
writeOutput("top of inner describe()'s callback<br>");

beforeEach(function(){
writeOutput("in inner describe()'s beforeEach()<br>");
});

afterEach(function(){
writeOutput("in inner describe()'s afterEach()<br>");
});

it("tests the negative", function(){
writeOutput("top of inner describe()'s first it()'s callback<br>");
expect(sgn(-1)).toBeLT(0);
writeOutput("bottom of inner describe()'s first it()'s callback<br>");
});

writeOutput("bottom of inner describe()'s callback<br>");
});

it("tests the truth", function(){
writeOutput("top of first it()'s callback<br>");
expect(true).toBeTrue();
writeOutput("bottom of first it()'s callback<br>");
});

it("tests falsehood", function(){
writeOutput("top of second it()'s callback<br>");
expect(false).notToBeTrue();
writeOutput("bottom of second it()'s callback<br>");
});

writeOutput("bottom of outer describe()'s callback<br>");
});


describe("second outer describe", function(){
writeOutput("top of second outer describe()'s callback<br>");

it("tests the absolute", function(){
writeOutput("top of second outer describe()'s first it()'s callback<br>");
expect(abs(-1)).toBe(1);
writeOutput("bottom of second outer describe()'s first it()'s callback<br>");
});

writeOutput("bottom of second outer describe()'s callback<br>");
});

writeOutput("bottom of run()<br>");
}

}

And here's the output (ignoring the test results, which are immaterial here):

top of run()
   top of outer describe()'s callback
     top of inner describe()'s callback
     bottom of inner describe()'s callback
   bottom of outer describe()'s callback
   top of second outer describe()'s callback
   bottom of second outer describe()'s callback
bottom of run()
in beforeAll()
    in outer describe()'s beforeEach()
        top of first it()'s callback
   
   
bottom of first it()'s callback

    in outer describe()'s afterEach()
    in outer describe()'s beforeEach()
        top of second it()'s callback
   
   
bottom of second it()'s callback
    in outer describe()'s afterEach()
        in inner describe()'s beforeEach()
        in outer describe()'s beforeEach()
            top of inner describe()'s first it()'s callback
   
        bottom of inner describe()'s first it()'s callback
        in inner describe()'s afterEach()
    in outer describe()'s afterEach()
    top of second outer describe()'s first it()'s callback
    bottom of second outer describe()'s first it()'s callback
in afterAll()


(note the indentation is not part of the original output, I just added it to try to add clarity to what's running when. I'm not sure I successed).

There's a few things to note here.

beforeAll()

I expected this to run... before everything. Before anything at all. Not just before the first test (ie: a call to the first it()'s callback). Where it runs is convenient, but I think there should be perhaps a beforeAll() and a beforeTests(), and beforeAll() should truly run before anything else.

Same with afterAll().

beforeAll() and beforeEach()

It seems slightly odd to me that beforeAll() is just a function, whereas beforeEach() is a call to a function which takes a function expression (which is then executed). I certainly didn't guess that, and had to refer to the docs.

Nested describe()

It quite cool how one can nest describe() calls, for subcategorising test output. This makes for a good visual cue when reading the results:


Anyway. That's it. Just a FYI, really.

--
Adam

Testbox: using a callback as a mocked method

$
0
0
G'day:
John Whish hit me up the other day, wondering had I looked at a new TestBox (well: at the time it was just for MockBox) feature I'd requested and has been implemented. Rather embarrassedly I had forgotten about it, and had not looked at it. That's a bit disrespectful of Luis and/or Brad (or whoever else put the hard work in to implement it), and for that I apologise. However I have looked at it now.

The feature request is this one, MOCKBOX-8:


Luis, we discussed this briefly @ SotR.

Currently one can specify a expression to use for a $results() value, and the value of that expression is then used as the results for the mocked method.

It would be occasionally handy for some stub code to be run to provide the results when a mocked method is called. This could be effected by being able to pass a callback to $results(), which is then called to provide the results when the mocked method itself is called.

Ideally a mocked method should always be able to be fairly dumb, but sometimes in an imperfect world where the mocked method has other dependencies which cannot be mocked out, an actual usable result is necessary. We've needed to do this about... hmmm... 0.5% of the times in out unit tests I think, so this is quite edge-case-y.

Cheers.
I can't recall what exactly we were needing to do at the time, but basically we were testing one method, and that called a different method which we wanted to mock-out. However we still wanted the mocked method to produce "real" results based on its inputs (for some reason).

MockBox allows to mock a method like this:

someObject.$("methodToMock").$results(0,1,2,3,4,etc);

And each call to methodToMock() will return 0, 1, 2 [etc] for each consecutive call, for as many arguments one cares to pass to the $results() method. Once it's used all the passed-in options it cycles back to the beginning again. This is great, but didn't work for us. What we needed was for the methodToMock() to actually run a stub function when it's called. Hence the enhancement request.

The Ortus guys have implemented this, so here's an example of it in action.

Firstly a MXUnit-compat scenario, running on ColdFusion 9 (so no need for function expressions or "closures" as the TestBox docs tends to refer to them as):

// C.cfc
component {

public struct function getStructWithUniqueKeys(required numeric iterations){
var struct = {};
for (var i=1; i <= iterations; i++){
struct[getId()] = true;
}
return struct;

}

private string function getId(){
// some convoluted way of generating a key which we don't want to test
return ""; // doesn't matter what it is, we won't be using it
}

}

Here we have a CFC which has a method we want to test, getStructWithUniqueKeys(). It calls getId() to get a unique ID. For whatever reason we don't want our calls to getStructWithUniqueKeys() to call the real getId() (maybe it requires a DB connection or something?), so we want to mock-out getId(). However we still need it to return unique IDs.

Here's our test CFC:


// TestMxUnitCompat.cfc
component extends="mxunit.framework.TestCase" {

function beforeTests(){
variables.testInstance = new C();
new testbox.system.testing.MockBox().prepareMock(testInstance);
testInstance.$("getId").$callback(mockedGetId);
}

function testGetStructWithUniqueKeys(){
var iterations = 10;
var result = testInstance.getStructWithUniqueKeys(iterations=iterations);
assertEquals(iterations, structCount(result), "Incorrect number of struct keys created, implying non-unique results were returned from mock");
}

private function mockedGetId(){
return createUuid();
}

}

Here I'm using Mockbox to prepare my test object for mocking (which just injects a bunch of MockBox helper methods into it), and then we mock getId() so that instead of calling the realgetId() method, we use mockedGetId() as a proxy for it. And mockedGetId() just uses createUuid() to return a unique key.

And, pleasingly, this all works fine:


Note that if I use the actualgetId() method, the test actually fails because I'm not returning a unique ID from it. Good stuff. And I'm pleased it runs on CF9 and in MXUnit compat mode.

But let's move forward into 2014, and use some BDD-inspired test syntax:

// TestMockUsingCallback.cfc
component extends="testbox.system.testing.BaseSpec" {

function beforeAll(){
variables.testInstance = new C();

$mockbox.prepareMock(testInstance);
testInstance.$("getId").$callback(function(){
return createUuid();
}
);

testInstance.$("getTomorrow").$callback(mockedGetTomorrow);

testInstance.$("emphasiseString").$callback(function(s){
return "<em>#s#</em>";
});
}

function run(){
describe("Test getStructWithUniqueKeys() which has had its call to getId() mocked-out", function(){
it("returns a correctly-sized struct", function(){
var iterations = 10;
var result = testInstance.getStructWithUniqueKeys(iterations);

expect(structCount(result)).toBe(iterations);
});
});

describe("Test getTomorrow(), which has been mocked with a predefined-function not a function expression", function(){
it("returns returns tomorrow's date", function(){
expect(
dateCompare(now()+1, testInstance.getTomorrow(), "d")
).toBe(0);
});
});

describe("Test mocked emphasiseString(), which takes an argument", function(){
it("uses the argument", function(){
var result = testInstance.emphasiseString("G'day World");
expect(result).toBe("<em>G'day World</em>");
});
});
}

private function mockedGetTomorrow(){
return dateAdd("d", 1, now());
}

}

Here I'm performing the same test as before of getStructWithUniqueKeys(); so there's not much more to say about that. Note that this time I am not using a defined function as the callback, I am using an inline function expression. I've switched to using ColdFusion 11 for running these tests, which supports function expressions (ColdFusion 9 does not).

One thing I am doing different here - for the sake of expediency - is the other two tests don't test methods of C.cfc, they simply mock fictitious methods in testInstance. This works fine.

The second test is analogous to how we implemented the test in CF9: using a predefined function instead of a function expression as the callback.

The third test pushes the $callback() function further, in that the mocked function takes an argument, and accordingly the callback ought to receive it. I say "ought to" by design. Here's the results:


Damn. note that our third test is erroring. Because the argument doesn't get passed to the callback. I had better talk to Luis about this, as it's kinda important that the arguments do find their way into the callback, in case they're needed.

Still: this is a cool feature to have. It's not the sort of thing one needs all the time, but better to have and not need, than need and not have.

Cheers TestBox dudes.

--
Adam

Do not sponsor me to go to CF.Objective()

$
0
0
G'day:
A coupla days ago I posted a guilty blog post "Shamelessful plug", which mentioned that Gavin was running a charity drive ("Charity Corner - Diabetes and Fly a Foul Mouthed Fusioner Fund") to try to fund me to get to CF.Objective() this year.

I am calling quits on this.

I hope I don't make any of the existing donors (thank-you!) cross, but I'm going to give the money away. Or back to you. But hopefully you let me give it away.

I just spotted this Twitter message from my long-standing mate Jared:

He needs help (details here), and more than I need a ticket to a conference, so I've instructed Gavin to give him the money gathered so far.

If any of my donors are unhappy with this decision (and I understand it'd not what you put the money in for, so that's fine), contact me privately and I'll get your money back to you.

And if you were thinking of sponsoring me: thank-you, but please sponsor Jared instead. Cheers.

Thanks for the kind thoughts & gestures everyone, but... ballocks to that: let's help our industry mate out when he needs it.

--
Adam

ColdFusion 11: More on this thing

$
0
0
G'day:
I've been thinking about this new <cfinclude> security feature some more, based in no small part on the excellent reader comments on my earlier article "Is Adobe knowingly leaving exploits in ColdFusion 9 and 10 unpatched?".



Firstly I'll repeat my response to one of Sean's comments, to contextualise a baseline:

If there's a case to address, and this is a sensible way of dealing with it: no problem. However we didn't know (beyond speculation) whether either of those is true. Rupesh has now clarified there is a case to address, but I remain skeptical as to whether this is at all a sensible approach.

It comes down to communication, and Adobe's seeming inability to do it coherently. On the face of it this was just a stupid thing to do, especially when the rationale was no more developed than "because security". Part of dealing with security responsibly is to communicate the situation. Not to detail the instructions of how to implement the exploit, but at least documenting what the issue is actually addressing. Even had Adobe implemented a more even-handed solution from the outset, without explaining the situation, the community cannot make an informed decision as to whether it's a ill-conceived annoyance that might as well be disabled, or the most critical and real threat ever. I still think this one falls closer to the former than the latter.

Furthermore, without discussion and input from the community, I will never be certain (nor should any of us be) that Adobe are actually addressing the issue sensibly, or just giving it a quick hack by way of lip-service. Let's face it: they do have a habit of doing the bare-minimum amount of work necessary to be able to look themselves in the mirror (although they set a low tide mark even for that).

And also - finally - a helpful comment from Adobe's technical mouthpiece, Rupesh:

  • Rupesh Kumar
    6:51:43 AM GMT+00:00 Mar 13, 2014
    As I said earlier, this basically provides a way to you to decide what you want to compile and what not, when the file is being included. This was added after we saw few cases where an application included a file and somehow the bad guys were able to write to this file being included. The file in one of the case was actually a log file where the application was logging invalid user input apart from other errors/log messages. As you would figure, the bad input was actually some CFM code which went into the log file and when this got included, the user given code got executed. The application had not expected this file to be compiled. As we understand, there are many applications which use cfinclude to include static content. 

    This is a good change. You are getting a control to decide what to compile. You will also get some performance improvement because your JS, HTM, CSS files would not be compiled, loaded and processed by the server. If you don't need any of this and want to compile everything, you can always use "wildcard". 

    To answer whether this should be given as an update to CF 9 & 10, we would not want to break applications while applying updates in their production servers. We can add this where by default we would allow all the extensions to be compiled which then does not fix anything immediately. So how do we go about it? Do you think it needs to be made available in CF 9 & CF 10?
There are some comments following that up, and you should read them.


But, anyway, it's as Aaron predicted. Well done Aaron (that might sound patronising, I mean it sincerely).

OK, so some modification of how code can be invoked might be in order here. But I really don't think what Adobe have done here is addressing the potential issue correctly. I think it's a knee-jerk, half-thought-through focus on a tangent than actually sensibly addressing the issue. So whilst we're in the business of half-thought-through solutions, here are my thoughts.

My problem with the approach Adobe are taking here is that it's addressing a problem after the fact. It's waiting until harmful code is already on the system, and then preventing it from running. What they should be doing is addressing how the code gets there in the first place, and also the ability for code to have impact outside its intended remit.

If this was one minor facet of a broader solution, and a couple of tweaks and augmentation were made to its implementation - and if Adobe explained themselves like professionals - I'd have no problem with it.

Let's look at the issue.

So the issue here is that it's been possible for baddies to gain access to a ColdFusion system, and then write code to the file system, and execute that code to exploit the server. This is very bad. And it is a real threat which has been exploited in the wild: "APSB13-13 / Security update: Hotfix available for ColdFusion".

So the issue here is this:
  1. baddies access the system via ColdFusion Administrator by circumventing login security
  2. baddies can write to files
  3. baddies can execute the files
Adobe have taken steps against the first one, by removing a security hole or two, and generally hardening access to the server. And to promote the idea of people following the Lockdown Guides (CF9, CF10).

Adobe have also taken some steps against the second one. I was surprised to find that there are vey few places in CF Administrator that allows one to write to the file system. But one place is via a scheduled task. A scheduled task can be configured to hit any URL (not just on the ColdFusion server), and the response from that URL can be written to file. This was a recipe for disaster, and it proved to be one. Adobe have changed this so that only .txt and .log files can be written. They can still be written to anywhere on the file system though, as far as I can tell. As .txt and .log files are not (unless specifically configured to do so) executable when browsed to, the risk here was perceived to be minimal. However one can still include a .txt or .log file, and the ColdFusion server will compile and execute it as if it is CFML code. Some people leverage this functionality: not as an exploit, but as a normal part of site operations, they might include a .js file to process some embedded CFML, or one organisation I know uses it to do "mail merges" on .docx templates. There are legit use cases for doing this (the latter is more legit than the former, IMO).

But still: they are allowing potential baddies to write to the file system whilst they are in CF Administrator.

And Adobe also allow baddies to place files where they can be executed. Either by being browsed to, or in theory, being included. The browsing risk is minimal unless the web server has been actively configured to pass .txt and/or .log files to CF, but I think that risk is minimal, and if the web admin has done this, they have probably done it for a reason.

This leaves the small risk that baddy-generated code could be executed via an existing CFM file, via <cfinclude> (or <cfmodule>, etc). However this would require one of two things to be true:
  1. The baddy has the ability to write their own CFM file to the file system, to include the nefarious code. If they can do that... they don't need the .log or .txt file, they can just use their own .cfm file;
  2. An existing application has <cfinclude> code in place which can be manipulated via the URL (or form, or some manner of external-user input) to directory-traverse to where the nefarious code is an inadvertently include it. Basically something like this:

    <cfinclude template="#URL.fileToInclude#">

    The weakness here is that someone might pass ../../../path/to/naughty/code.log, and ColdFusion will execute it. Obviously if one is doing something as ludicrous as that - without very strict validation - one one ought to be shot. But also validate the value of URL.fileToInclude so that it can only reference the intended range of files. But Adobe cannot control the crappy code developers will write.
All the focus here has been given to the last step of the exploit. Adobe's not worrying about where the baddy writes the file (or that they even can write the file!), nor are they focusing on whether the file can be accessed from another application. They're just addressing the last link of the chain. After most of the exploit has been allowed to succeed.

Now as I said in my reponse to Sean, I am completely fine with hamstringing <cfinclude> if there's been equal attention given to the other steps of the exploit.

Where scheduled tasks can write files

They could prevent scheduled tasks from writing anywhere except for one dedicated directory. Or at least have a configuration option to enforce this. This reduces the surface area of potential threats.


What scheduled tasks can write

Prevent them from saving CFML to the file system. Configurable. Or it could take a JSONP sort of approach and prepend any file with CFML in it with a <cfabort> to prevent it from running.

From where code can be included

Have an application setting that code can only be referenced via canonical paths that exist within the website, web application, and any mappings (or custom tag directories). Thus preventing traversal into areas that the application is not expecting.

How code is executed

Some talk regarding this modification to <cfinclude> behaviour is to unilaterally hamstring how it works when including not CFM files, in that it will basically work like a combination of <cffile> and <cfoutput>, rather than actually processing the file. This is just fucking stupid. If someone is <cfinclude>-ing a file, it's because they want it to be <cfinclude>-ed. Not because they want to <cffile> and <cfoutput> it. Because that's what they'd be doing if they wanted to do that. And it's quite rubbish to do this at file-extension-level, rather than slightly more thoughtfully.

A parameter could be added to <cfinclude> to alter the compilation / exeuction behaviour, eg:

<cfinclude template="foo.notCfm"><!--- process as per usual --->
<cfinclude template="foo.notCfm" compile="true"><!--- definitely compile it --->
<cfinclude template="foo.notCfm" compile="false"><!--- definitely don't compile it --->

And then have a setting in the secure profile which sets the default behaviour for non-CFML files. Or a range of specified file extensions (both whitelist and blacklist, and including wildcards).

The benefit here is that a developer on the Adobe ColdFusion Team is not unilaterally second-guessing how we write our code. Because let's not forget there: the issue is not how we write our code, it's how Adobe facilitates baddies writing their code.

Adobe are addressing this issue via lip-service, rather than via actually assessing how best to deal with the issue at hand. This is my problem with their approach here.

Comment?

--
Adam

ColdFusion-UI-the-Right-Way:

$
0
0
G'day:
This is a bit of a lazy one, I have to admit. I've added an example for how to use the Google Maps API directly instead of <cfmap> into the ColdFusion-UI-the-Right-Way project, but it's a very very simple example. Still: it demonstrates the code and pushes the punter in the right direction. I'll do a more interesting example when I do one for <cfmapitem>, I promise.

I've reproduced the details below:


cfmap

The <cfmap> tag provides the ability to create maps in CFML. <cfmap> uses the Google Map API under the hood to provide the maps.

For this chapter we will make use of the Google Maps API directly.

Here's an example using <cfmap> to render a basic satellite map of Whatipu, just out of Auckland, New Zealand:

Listing 1 : exampleUsingCfMap.cfm

<cfmap
centerlatitude = "-37.0477058"
centerlongitude = "174.500332"
type = "satellite"
zoomlevel = "16"
width = "800"
height = "600"
/>

This renders:




Now here is an analogous map, totally written in simple HTML, JavaScript and CSS.

Listing 2 : exampleUsingGoogleMaps.html

<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="exampleUsingGoogleMaps.css">
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>
</head>
<body>
<div id="map-canvas"></div>
<script src="./exampleUsingGoogleMaps.js"></script>
</body>
</html>

Listing 3 : exampleUsingGoogleMaps.js

var map;
function initialise() {
var mapOptions = {
zoom : 15,
center : new google.maps.LatLng(-37.0477058,174.500332),
mapTypeId : google.maps.MapTypeId.SATELLITE
};
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
}

google.maps.event.addDomListener(window, 'load', initialise);

Listing 4 : exampleUsingGoogleMaps.css

html, body, #map-canvas {
width : 800px;
height : 600px;
}


As you can see, the only mark-up needed is the map-canvas<div>. The rest is done with JavaScript (and a touch of CSS to style the canvas). Here's the render:


Resources


Alternatives


  • TBC


That was it: pretty simple. I'd never really used either of <cfmap> or the Google Maps API before, and they were both as easy as each other to work out what to do (by RTFMing, basically). But of course having knowledge of the Google Maps API is useful and transportable knowledge to have, whereas having knowledge of <cfmap> is too niche to be of much use. If you want to get some maps onto your CFML-driven website, don't use <cfmap>, just use the API directly. It's just as easy, it's got far more flexibility, and it's a useful skill to know compared to using <cfmap>.

--
Adam

It is Adobe's fault, OK?

$
0
0
G'day:
OK, so it's just reached the news media that a Citroën website was exploited via an unpatched ColdFusion security hole (The Guardian: "Citroen becomes the latest victim of Adobe ColdFusion hackers").


It's - as yet - unclear as to when this happened: the wording in the Guardian is vague, so it could have happened last week, our it could have happened last year. Our it could have had the exploit mechanism "installed" last year but only utilised recently (this is my own interpretation of the article), or only discovered recently. I have reached out to the Guardian for clarification of this, and will update this article if they respond.

But these are definite facts:
  • the server was exploited via poor security in ColdFusion;
  • the server did not have a patch applied that would have mitigated the issue;
  • the server was not appropriately locked down for a production server, irrespective of patch level.
So the admins of this server were negligent. I am not contesting that. I am also not saying they were purposely negligent, but that doesn't matter: they had not done their jobs properly.

But this does not absolve Adobe from two things:
  1. the default install process leaves a large security vector exposed to the world, in that it exposes /CFIDE to the public by default;
  2. the exploiters used that vector and a general poor approach to security and password management in ColdFusion Administrator to breach Coldfusion servers.
Bugs happen. And shit happens. And I class #2 in that bracket. And Adobe got the patch sorted out reasonably quickly once they discovered it. But it still happened.

#1 is inexcuseable, and has been known to be a vector for exploitation for years. Only in ColdFusion 10 did Adobe first give a nod to this, and optionally allows the person doing the installation to use a "secure profile" when installing CF, which includes the ability - from the outset - to prevent public access to files in /CFIDE. As of ColdFusion 11, the default installation route is still to expose /CFIDE.

I make a point of saying this because as soon as this Citroën news came out, Adobe apologists started floundering around the place saying it's not ColdFusion's fault, but entirely the fault of the administrators who didn't keep their servers patched.

Yes, the administrators need to shoulder responsibility here. But this in no way completely absolves Adobe for getting them into this situation in the first place. They'd not have anything to patch if ColdFusion didn't have the issue in the first place.

As a follow-up to this situation, I have asked Rakshith what steps Adobe took to ensure their client-base was aware of this security hole, and I await feedback on that, which I will share. I don't think it's unreasonable with a security issue like this that Adobe should have been proactive in making sure ColdFusion was patched wherever possible. Each of these news stories hurts both Adobe's and ColdFusion's credibility, after all.

--
Adam

Ben Nadel falls down the isValid() rabbit hole too

$
0
0
G'day:
This'll be very quick as I only have half an hour lunch today, so typing quickly.

Ben's just posted a blog article "IsValid() Accepts Emails With Leading And Trailing Whitespace In ColdFusion". This make me cry a little bit more inside, given we've already had all the drama with integers:

So now we find out isValid() struggles with spaces too. FFS.

To be brief about it, here are some examples:

<cfoutput>
<cfset notAnEmailAddress = " not.valid@example.com ">
isValid("email","#notAnEmailAddress#"): #isValid("email",notAnEmailAddress)#<br>

<cfset notANumeric = " 1 ">
isValid("integer","#notANumeric#"): #isValid("integer",notANumeric)#<br>
isValid("numeric","#notANumeric#"): #isValid("numeric",notANumeric)#<br>
isNumeric("#notANumeric#"): #isNumeric(notANumeric)#<br>
isValid("float","#notANumeric#"): #isValid("float",notANumeric)#<br>

<cfset notAUuid = " #createUuid()# ">
isValid("uuid","#notAUuid#"): #isValid("uuid",notAUuid)#<br>
</cfoutput>

Output on ColdFusion 11:

isValid("email"," not.valid@example.com "): YES
isValid("integer"," 1 "): NO
isValid("numeric"," 1 "): YES
isNumeric(" 1 "): YES
isValid("float"," 1 "): YES
isValid("uuid"," 55241439-D067-E5E6-F12E28DB9B75FCE9 "): YES


No - for goodness sake - they are not. That is not an email address, that is not numeric or a float, and that is not a UUID. How hard can this be, Adobe?

I didn't bother checking any more, because I just got annoyed. More than usual.

Then I got annoyed at Railo too:

isValid("email"," not.valid@example.com "): true
isValid("integer"," 1 "): true
isValid("numeric"," 1 "): true
isNumeric(" 1 "): true
isValid("float"," 1 "): true
isValid("uuid"," D39DD376-A238-43C7-9647E6BA439007EE "): false


And they can't say "we're just following ColdFusion's lead here", because they don't.

And OpenBD has different results again:

isValid("email"," not.valid@example.com "): YES
isValid("integer"," 1 "): YES
isValid("numeric"," 1 "): YES
isNumeric(" 1 "): YES
isValid("float"," 1 "): NO
isValid("uuid"," 40D8905A-422E-4C18-BE71EEB51DB51C59 "): YES


What's so bloody hard about not messing this up, guys?

And that's all I have time to say on this.

--
Adam

ColdFusion 11: bug triage (and fixing?) seems to have stalled

$
0
0
G'day:
Ages back I wrote an article "212 untriaged ColdFusion 10 bugs", and indicated my horror at how many bugs in the current version of ColdFusion that Adobe had seen fit to simply ignore. I don't mean "not fix", I meant "just completely ignore; ie: not even acknowledge the bug had been raised".

I followed this up a coupla months later with "Bug watch: 212206 untriaged ColdFusion 10 bugs"; in that two months they had managed to triage a net of six tickets that had been raised.

Just a week later we were down to 165 untriaged bugs: "Good work Adobe ColdFusion Team!", and they had made good progress from there, getting it well below 100 untriaged bugs, and got it down to a low of 40 on Jan 22 this year. Again: good work!

However since then, the tally has been steadily creeping up again. It doesn't look there's much triage activity going on. Now note - I keep repeating this because people seem to be excellent at "not getting it" - I'm only talking untriaged bugs. Bugs that not only have Adobe not fixed (or concluded not to fix, for whatever reason), or are in the process of fixing... these are just ones they haven't bothered to even look at. I am a bit bemused by this, seeing we're in the thick of ColdFusion 11 development. This is the time they should be hammering these issues! I get that they've got a lot of work on, but this is all part of that work. They need to address these issues!

As well as tracking untriaged ColdFusion 10 issues, I am now also tracking CF9 and CF11 ones. With CF9 there was a similar pattern... Adobe hit the outstanding ones hard for a week or two, but then seemed to get bored and stopped doing it. And ColdFusion 11 bugs are just bumbling along, pretty much under control I think. Although - seriously - I'm surprised Adobe aren't looking at these tickets as soon as they come in... given the community are volunteering their time to test @ the moment, it seems a bit disrespectful of Adobe to not keep on top of the ColdFusion 11 bugs as soon as they come in. There's only just shy of a dozen untriaged ones right now, but that's not changing much. And there aren't that many CF11 bugs coming in that these dozen represent new ones coming in every day: they're the same old ones sitting there being ignored.

Not good.

Anyway, here's some charts showing the progress of the triage. On the whole it's good because CF9 and CF10 are tending towards fewer outstanding bugs, and ColdFusion 11 whilst increasing is not increasing too much.

That said, I dunno how much testing it's getting... every time I look at ColdFusion 11 I can pretty much instantly find a bug of some description, so I would have thought any other testers would also. I am not testing it currently, due to workload from other directions occupying my attention.

The figures contributing to the charts come from a daily scrape of the bugbase, doing a search for "Open" / "Unverified" tickets there are on a given day. It's the same info I update @cfmlnotifier with.

Make of that lot what you will.

--
Adam

Survey: Adobe's approach to client communications regarding security issues

$
0
0
G'day:
I haven't done a survey for a while! This is a follow-up to my earlier article "It is Adobe's fault, OK?", and just to capture the zeitgeist.

So here it is: "Adobe Communications", and the description text is:

This is just a quick survey to gauge how effective Adobe have been at communicating to their clients regarding security issues that have arisen in ColdFusion
There's seven questions: all bar one can be answered via a mouseclick, and the last asks for a comment. There's other boxes for comments along the way too.

I will always remind people that I do not target a statistically meaningful demographic, nor do I gather enough results to be sensibly analysed, but it is nice to see other people's opinions on stuff sometimes.

So go fill it out if you like. I'll summarise the results when the response stream dries up.

Cheers.

--
Adam





An insight into the mind of a CFML developer

$
0
0
G'day:
I was going to respond to this on the blog I found it (the Adobe ColdFusion one), but I'll do it here instead so I'm not polluting their blog with my anguish / irritation. Be warned: this is a fairly mean-spirited article, and singles out an individual.

Ram's (it's not Ram I'm singling out here; this is just framing the situation) doing a good job at blogging stuff about ColdFusion 11 and its mobile features, and this article is an example: "ColdFusion mobile features are not just about cfclient, but it is necessary" (it's old: it's from November, and it's been sitting idle until today). I'm not personally interested in the content, but I subscribe to these threads to see the discussion and what other people are thinking. This works, because today I found myself reading the comments and going "what the hell are you thinking?"

The comments have turned to discussing what would benefit CFML more, and the idea of ceasing with the crufty UI stuff (which <cfclient> basically is: "UI stuff", I mean), and perhaps work with the actual developers to improve the language. I think the zeitgeist (not subscribed to by everyone, granted) is we don't really need the help of the ColdFusion Team writing our client side code for us because a) they're not very good at it; b) there are a lot of other people out there who are really good at it; c) it's a bit career-stifling to only know how to do this stuff via ColdFusion. Some people seemed optimistic about <cfclient>, which is good... for them. You know my opinion.

Anyway, no comments since November 19 last year, until today when we get this chap Nate posting this comment:

I have been developing cf since the 1st version what was made way back when. It was attractive because it was easy and easy to make a database enabled application. I think the community has ruined CF. Ya all want to code in java script make everything OO, put everything in a cfc. make a bunch of simple stuff terribly complex. Phone gap mobile apps just add to that. CF should be simple, auto generate everything, and make all the jquery bs and the ajax that goes with it be behind the scenes. CF was once easy and simple and I could hire an 8th graded to code it, and it worked great. Ya ruined that and now cf sucks more than ever. Just saying..
Jesus Nate. I dunno what's worse. That you think that, or that you think that so adamantly that you bloody write it down and tell everyone. How bloody embarrassing. It's embarrassing for you, Nate, and it's also embarrassing for our community to have people with this level of lack of ambition, nous, and purchase on reality putting their heads above the parapet. Yes of course all communities have people like this. This doesn't make it a good thing.

I despair. It's just so monumentally ignorant. All these new-fangled "features" ("features" like OO, JS, mobile development: more realities of life than "features") should be seen as opportunities to improve one's self, and indeed opportunities to make one's job easier. OO isn't there just so people can feel smug; it's there to make life easier! I'm surprised you didn't mention the concept of "frameworks" as being a blighted complexity on the world (although I'm sure it's only because you forgot to). All this stuff makes it far easier to work with our sites.

Any of us that's been in the web business since the 1990s started out using Cold Fusion to automate cookie-cutter style templates to make dynamic web pages. And that's where the expectations were for web sites back then. There wasn't much to 'em. So that approach was adequate (and Cold Fusion was very good at filling that niche). However 20-odd years later, the requirements placed on us these days simply don't scale following those old school development practices. All OO and frameworks and the like really do is organise our code more, to make it more maintainable. Easier. This is a good thing. Similarly with JavaScript. It's not some fiend from the depths; it's a bloody useful tool, and the various JS libraries out there make it so very easy to use. Again: it's easier. It's easier than not doing it.

One can tell you've let your skills go stagnant by clinging on to this idea that "the old way" of doing CFML is the best way; had you tried to extend yourself, you'd easily see that there's more than one way to do something, beyond <cfset>, <cfquery>, <cfloop> and <cfif> (what's the fifth one? <cfoutput> I guess). And learning more than one way to do something improves one's capabilities across the board (eg: learning JS will improve both your JS and your general ability to program, so will improve your CFML as well).

You comment that 8th graders used to be able to do CFML. Well if they hadn't been scared of CFML because of its poor reputation in the industry (caused in no small part by people like yourself, Nate), they'd be writing code streaks ahead of you, mate. Whilst those 8th graders are off coding circles around you, I hope you have a very secure job in the corner of some IT office in the civil service somewhere (where no-one has any expectations of quality), because if you needed to find a job, you'd be bloody screwed. Or you'd be screwing whoever accidentally hired you.

Wake the hell up.

--
Adam

3

Survey results: Adobe's approach to client communications regarding security issues

$
0
0
G'day:
Well today's last minute push ("3") to get the survey over 50 results has worked. I've got 5679 (the number changed cos I started this on Fri, but finished it on Sun; the other responses came in during that period) now. So here're the results.

Just to remind you, the topic was:
This is just a quick survey to gauge how effective Adobe have been at communicating to their clients regarding security issues that have arisen in ColdFusion

Q1: Have you supplied Adobe with legitimate and currently-accurate contact details when downloading or purchasing ColdFusion?


This is the proportion of people who are happy to provide completely legit email details when downloading ColdFusion. They could just provide a junk address, but they don't. So they're obviously OK with Adobe contacting them.


Almost everyone has given Adobe their contact details. I certainly have. And this is not some generic Adobe contact page, it's the one when we download ColdFusion. So definitely appropriate for contact regarding CF issues. I have heard many people get follow-up calls when they download CF. I always get an email. Every single time I download CF with the same credentials. So Adobe are not shy to send marketing email.

Q2: Do you hold an account with Adobe for any other reason, for which you have supplied currently-accurate contact details?


This is just to check whether a lot of people might have Adobe logins from something else. In hindsight this question should have come before the first one. Oh well.


Predictable.

Well... if the titles weren't truncated.  They are:
  • I have an account which has currently-accurate contact details
  • I have an account but the contact details are not accurate
  • I do not have an account with Adobe

Q3: Were you notified when Adobe had everyone's account details stolen from them, and did they advise you to change your password? 


This is a test whether Adobe (as opposed to specifically the ColdFusion Team) are prepared to email everyone when there's a problem...



I actually find this to be quite a low number! But still: it's a lot higher than the next number. But before we move onto that, we had some comments:

Not for corporate account but general Adobe one

I don't think so, but I don't remember for sure.

I received about 70(!) notices of password resets on a slew of test accounts I had created over the years.

I was notified three times, over the space of a month or so (all for the same account)
70. Nice. Sorry to hear that. The last comment is my one... I had my bloody password - for the same account - reset three times by Adobe. Interestingly... they let me set it back to the same password as before, each time.

Q4: Have you ever been directly notified by Adobe regarding security issues in ColdFusion, be they patches or simply notifications of exploits?




Lucky people who got notified! I never have been. Nor had almost 89% of the rest of the respondents. Also bear in mind the sort of person reading my blog are fairly "active" in their ColdFusion pursuits, not people locked away in some enterprise corporate silo of ignorance. So if there was contact to be received, they'd be receiving it.

This is very very very poor on the part of the Adobe ColdFusion Team.

Feedback:

Never received any email regarding ColdFusion.

Once I signed up for the mailing list.

Not that I recall.

Not that I can recall.

They never release anything except to the person who purchased the product. Often times I only realize it by the little explosion icon saying there is an update in cfadmin.

Yes, when signed up to the Adobe alert notification at http://www.adobe.com/cfusion/entitlement/index.cfm?e=szalert
Right... I'm signing up for that. I did not know about it.

Outside of the account breach I don't believe I've ever been notified. I *DO* however always get a followup email after I have downloaded ColdFusion asking me about my experience / sales pitch. So they have my address, and know I'm using ColdFusion :)
Indeed.

How about sending materials out to all the CFUG's? User groups don't exist solely to echo Adobe marketing you know. They'd be glad to help out.
This is a bloody good idea!

Not as far as I can recall.


Q5: What is the primary way you find out about security issues in ColdFusion?


OK, so how do we find out:

That's:
  • Adobe Security Bulletins (notifications delivered to you)
  • Adobe ColdFusion Blog
  • Adobe ColdFusion Forums
  • Adobe ColdFusion Team Twitter statuses (@ColdFusion)
  • From the ColdFusion community (blogs, Twitter traffic, etc)
  • Other (please specify)

No surprises. Well I guess only two people picked "Adobe ColdFusion Team Twitter statuses (@ColdFusion)", which suggests that's not a great place to find out about this stuff. Which is telling. You'd think that'd be the first place to hear about it from!

The responses for "Other" were:

The Adobe ColdFusion Facebook page. I've signed up for security bulletins now, thanks to this survey, I assume now I am going to get bulletins regarding all their products rather than just ColdFusion :(

It's random.

ColdFusion updated.

coldfusionbloggers.org

I actually use all of these because... I had signed up for the security bulletin emails but I never would receive any of them. I finally had to sign up again under a different email address to begin receiving the emails. To this day I never found out what the issue was with the original email I used. Regardless I still check all of these streams because I can't rely on Adobe to let me know.
This is a good idea. This does not absolve the ColdFusion Team from needing to shoulder some responsibility here!

Mainly community and news sites. Often when looking for bug fixes that might be in hotpatches.

Depends. Adobe Security Notifications most of the time, HackMyCF email notifications are most reliable though. Even remember when one patch seemed to be announced on Facebook first.
Is the HackMyCF emails something one only gets as a Foundeo client, or as a community service they do? I was unaware of it.

Multiple ways. Again from the Adobe notification from above and twitter. Honestly, the Adobe ColdFusion Blog does not communicate that well. Usually any notification there is cryptic and has to be corrected via comments on the blog post
This is my experience too. I think they need to stop the techo guys from delivering the message. They can define what the message is, but they should get someone a bit better at interpersonal communications to articulate the message.

Q6: Reaction to comment

This was asking for a reaction:
In regards to this article - http://krebsonsecurity.com/2014/03/the-long-tail-of-coldfusion-fail/ - which details hackers exploiting an insecure and unpatched ColdFusion server to steal data, how reasonable do you think this conclusion is: "the story is not about ColdFusion fails at all, but about IT failing to keep servers up to date to deal with already addressed security holes".
Here people diverged my expectations:


  • Very reasonable
  • Somewhat reasonable
  • Neither reasonable or unreasonable
  • Somewhat unreasonable
  • Very unreasonable
My reaction here is "somewhat unreasonable". I discussed this separately: "It is Adobe's fault, OK?". I guess it's positive for Adobe that people blame them less for their security cock-ups than the victims of the security cock-ups. But, yes, there's no real excuse if an issue has been identified and fixed. Although I think we definitely see a failure to communicate this information going on. Would an admin who knows about issues like that not patch a server?

Comments:
Maybe IT knew about the exploits but weren't given the time of day by their management to make the fixes. I think it's as likely that bad management is to blame as IT's lack of knowledge. I think that patches are slow to be released by Adobe too, but that's not exclusive to Adobe. I don't feel it was CF's fault.

One of the servers in that article is one that I had to fix after it was exploited. The server wasn't fully patched and it was assumed the hosting company was doing updates.
Were they supposed to be doing the updates, or was this a breakdown in communications? For co-hosted servers I'd expect the hosting company to be doing it. But for dedicated servers, I'd expect the default to be that it's up to the client to deal with it, even if "dealing with it" is "paying the hosting company to look after it". If one is in this situation, I'd be confirming with the hosting company how they go about this sort of thing though.

Every system has security issues. It is up to us as a user or administrator to patch the system. The vendor has done its job by providing a patch and announcing it. However in this particular case, it would be better if ColdFusion has an auto update like in Windows or OS X. And, make sure every current or potential customers receive the information.

The servers should be able to check for updates and notify you through its own cfmail.

The truth is that admins did not patch their servers, but the story came across as blaming the use of ColdFusion, patched or not. I see very few stories about data breaches that blame the admins. Generally whatever software is being used takes the blame. This is not just for ColdFusion, it's just that CF doesn't seem to get any positive press, and Adobe sits quietly while their product takes a beating.
In Adobe's defence, I dunno how good it would look if Adobe themselves had declared "well it's there bloody fault for not patching! However it would not be too far-fetched of them to contact the companies whose exploitation stories are made public, go over their server security with them and get them back on track, by way of some positive PR, and looking like they give a shit. They could put some positive spin on this. But they don't seem to bother.

I would say that ultimately, IT administrators are at fault if their servers aren't fully patched. However, given how painful, complicated, and frought with opportunities for screwing up a ColdFusion installation were with the way updates were installed on ColdFusion 9 and below, I give admins a little bit of a break. Just think: how many blog articles has Charlie Aerhart written to explain how to install the various updates and hotfixes for ColdFusion 9?
Very true. One thing Adobe could consider is to back-port ColdFusion 10's auto-updater to ColdFusion 9 too. They seem to play down the fact that ColdFusion 9 is still supposedly a supported platform.

I'm still on cf9 in production, so patching isn't easy. Generally it's a complex manual process, and the directions need some careful consideration to follow. Making a mistake can take a server down. And as this survey points out, notification doesn't arrive in my inbox, I need to stumble upon it somehow. Compare that with Centos, for instance, a free open source OS. I get email notifications the moment patches are released and an update is just a few command line keystrokes away. No complex instructions, manual downloads, etc to put me off doing it until I have my wits about me in a calm, distraction free moment (hour or two ...) I'm also reminded of the lockdown guide. I'm grateful the Pete Freitag wrote it and put it out there for free, but one gets the impression that he withholds enough information, and / or glosses over it, to make it at least somewhat likely that he's hired as a consultant. This should be coming from Adobe, not a private consultant with an interest in obtaining clients who apparently develops it in his spare time. And it should come with the documentation when the product is released, not a year afterward, which has been the case with the lockdown guide. To be fair to Pete, I don't think he's being paid to produce this, he likely can't afford to make it comprehensive. And yet, it's a key document underpinning CF security. Without it, securing a cf server would be much more difficult, utterly susceptible to misinformation, as amateur opinions traded and debated online would be the only source of information. But all that said, Adobe should be responsible for developing and providing this information! It should not be left to a single person with a family to support to do it in his spare time when he can get to it. I think this is a root of ACF's security problem.
This is a great contrast. And patching an OS must be a more complex task to patching an app server, wouldn't it?

The public perception has obviously been affected by the high profile articles on this topic. It seemed to me , in most cases, a good majority were versions that had not been upgrade or patched. There's only so much hand holding a company can do on that end of things.

I would have to lay partial blame on the company's IT department because the resources and tools are out there to guide you through the process of properly locking down a production ColdFusion server. That being said, I would lay equal, if not more, blame on Adobe for not communicating the importance of locking down a production server and/or making the process of locking down a production ColdFusion server easier. ColdFusion has never been an easy server to configure and manage, perhaps it's all the non Adobe moving parts (eg. various versions of IIS, apache, etc), at least that's what Adobe will tell you, but it seems like there's real room for improvement.

It is reasonable, however, all it takes is 1 unpatched server to bring down a company's product. This has happened in other languages too, but honestly, no one is running those types of applications in those languages so the global impact is minimal.

It is a failure of CF in being as open by default as it is. However, it is also largely a failure in that firms don't want to pay the high upgrade fees, so will often use older versions for a long time. This cost also leads to there being just one production server, no good testing environment where updates can be tested before deployment to prod. Also, I believe too often admins know very little about how to work with CF and will often just leave CF up and running with no action. Finally, too often the CF Dev is the admin as well, so updating the server may be at the back of their mind, but getting their regular day to day work done first takes priority. After writing all of that, I think it it actually maybe more of an Adobe Fail than a CF fail - the marginalized nature of the language just makes IT support for it more costly and difficult.
I've nothing to add, but it's a great comment.

CF is a enterprise class product it should be secured and as bulletproof as possible. You wouldn't buy a Ferrari and anticipate needing to upgrade it's door locks or blame your mechanic for not knowing the door lock should be replaced.

That statement is not a complete picture and ignores the fact that there was indeed a significant security hole in ColdFusion.

The reality of the matter is that there are way TOO MANY unsecure ColdFusion servers out there. All one has to do is read the list of servers attacked by Laurie Love just in the New Jersey indictment, http://www.justice.gov/usao/nj/Press/files/Love,%20Lauri%20Indictment%20News%20Release.html Adobe does themselves no favor in that ColdFusion is not installed by default with secure settings. Microsoft learned this lesson with IIS, Adobe hasn't learned this. Also "Secure Profile" isn't all that secure. All the recommendations in the lockdown guides need to be integrated directly into the installer, not a separate guide that an admin might see. If they can't, the links to the guides need to be lit up in bright neon saying your install is not secure unless you do this. The other problem is patching ColdFusion before 10 (and still in 10, all the various instructions of do you need to re-run the connector or not) isn't easy. They expect people to upgrade to get bug fixes that have been broken forever (wait "works as designed", "deferred/not enough time", or "backwards compatibility"). And when they DO get a bug fix out how many times has it had to be re-released due to shit QA or it breaks something else unrelated (spreadsheet functionality) or introduces a vulnerability. This is what people are PAYING for and they wonder why it is "dying". Perception is reality, if a CIO has it in his head that ColdFusion in their environment is a huge risk, they are going to get it out of there. Without a quality message to the opposite from Adobe, it will persist. As seen with eLightBulbs.com, people don't upgrade if it is working for them and there are no compelling business reason to upgrade, regardless of the "new shiny" or latest (3 year old) mobile/social trend Adobe has targeted for "Enterprise Customers".
Very good point / idea. The lockdown guide should not be random ad hoc guidance aimed at the administrator, but taken on board by Adobe as how ColdFusion installs out of the box.

It's about CF fails to a certain extent, too. Unfortunately Adobe makes it impossible for sysadmins to keep on track with updates. Even in CF 10 where they have introduced some level to automated system, some patches are a pain in the neck to install. One wonders why Railo can do incredibly simple "2 mins" patching (or even fully automated) and Adobe can't.

Every time you have to patch an application server, you have to re-test everything. Some of ColdFusion's security patches have broken previously working code.The login / authentication security hole was actually INTRODUCED BY ADOBE as a hotfix for CF8 - and then left open in CF9 and CF10!
This is a ludicrous state of affairs.

The admins are at fault, sure. But it's Adobe's fault the situation was there in the first place. It's because of the ColdFusion security hole that this became "a thing", not because the admins didn't patch something.
The last one is my observation. I've already commented on this topic enough, so I'll leave the other comments stand on their own merit.

Q7: what do you think?


In your opinion should Adobe actively attempt to contact their client-base when (any?/significant?) security breaches in ColdFusion are identified, or is it the responsibility of the ColdFusion system administrators to ensure they are aware of and act upon security issues that might arise (and/or add any other comments you like; this is the last question, so have your say).

And you thought:
yes absolutely

It would make everyone's life easier if Adobe made it more obvious in My Adobe area how to register for security advise. It's a bit buried away in it's current form.

If there is no communication from Adobe, sys admins will not be urged to patch servers immediately. In a perfect world, servers/software will be up to date, but this doesn't happen. If Adobe doesn't inform their clients about security related issues with current versions, nobody will put in their time to investigate IF something is going on security wise with their CF installs. Pro active information from Adobe is very needed and the current way of handling these issues is a big show stopper for me to continue to work with Adobe coldfusion. I switched over to Railo and sticking with them.

Both... I believe Adobe should communicate better and more, but it's also the administrators responsibility to keep their servers up to date. It shocked me several times already when I'm consulting at clients and figure out the server administrators don't know where to find the information. They should look harder and at the same time Adobe should make it easier to find it.

They should be issuing notices, but it is also my job to make sure all our servers are patched.

I think one should opt out about these notifications.

Yes, they should make it one of their primary responsibilities. It's certainly not difficult to send out e-mails to their customers or even take the time to call their contacts directly. Why not put them in contact with their vendors for hardening their servers?

They should attempt to contact their client-base for any security breaches. They should do this via email, blogs, twitter or any other means of contact they have. That said, it's still the responsibility of the ColdFusion system administrators to ensure they're aware of any issues.

The only time Adobe has contacted me after downloading a product is to attempt to sell me more products. I suggest they implement something like their Adobe Cloud application for CF Developers and Network Administrators that proactively checks for updates and announcements.

I think Adobe definitely needs to be better at providing information about security patches. In the past I signed up for their security patch notification list, but I never saw any patch announcements. That said, knowing that Adobe does not announce these, server admins should be watching other sources to find vulnerabilities. There are a lot of 3rd party security notification services that would keep you informed. Also, CF 10 will tell you when patches are available (in CF Admin), so if you're lucky enough to have CF 10, you just need to pay attention when you go into the CF Admin.

There should be an option in your adobe profile to receive communication regarding patches and security issues that is enabled by default, but can be disabled.

Of course, Adobe should actively attempt to reach out their customers. On the other end, administrators are responsible to patch their system. Perhaps, both parties need to improve their communication, so they can act in timely manner.

I think Adobe should generally be more organised. Their bulletins are all over the place and are even sometime updated days after being initially published. There's not one master source of information. Sometime I hear about it in their blog, sometime not. The auto updater is a step forward but for Cf 9 it's not an option. The fact that things like unofficial updater 2 even exist should have them in shame.

Adobe should make every effort to contact its customers when there's a known security issue.

Adobe should proactively attempt to contact anyone that has ever dl'd CF to make them aware of patches when released, whether the patches are security related or not. CF10 makes life easier, but I still need to log in to my CF10 server to find out if patches are available - that reminds me, gonna do that now.

It should be both.

Yes, Adobe should contact their client base when security breaches in ColdFusion are identified

Both. Adobe should push notifications to all CF system administrators. At the same time, those system admins should be keeping up with what's going on in their community.

I don't belive Microsoft contacts their client-base every time there is a security patch in Windows or Office products. They just push out the fixes on patch Tuesday. When there is a dangerous zero-day security issue, they may release a patch on a non-scheduled day, but I don't thing they actually notify clients - usually such issues get a fair amount of media attention which gets the word out. With the updater built into CF10/11, putting out security fixes becomes easier - but admins still need to stay on top of their servers and install the updates. Security issues still need to be communicated to the customers so they know immediate action is necessary. So many organizations are still running on ColdFusion 9, so Adobe still needs to contact clients directly when security issues are uncovered and fixed. Those customers unwilling to upgrade to currently supported versions of ColdFusion (i.e. version 8 or below, and for whatever reasons) really have no one to blame but themselves if they fall prey to a security breach.

Adobe notifying their client base would be a big plus. Unfortunately, Adobe has never really been that kind of company. Any decent admin will have other sources of security/patch information and stay well informed.

Absolutely! I pay for licenses and support. If Centos can do this, they can. And as I said, they should also be responsible for developing, testing and publishing the lockdown guide, and it should come out as or before a server version is released!

Yes, it's their language and their profit margins that are on the line, as well as our necks!

I think System Administrator should be aware of all the security issues and if any patches are released it should be acted immediately in test server and in production

It might help them to keep some of their remaining customers. The whole CF10 debacle (like patches that destroy your scheduled tasks setting) and Adobe not being able to secure even their server (well, they're only a company that DEVELOPS AND SELLS server software and wants to advise their customers on how to secure their servers). were the last straw (after other insults like making so many features unusable in the Standard edition for the license to become a waste of money) and we migrated our company to Railo.

Yes, Adobe should notify the client base when security issues occur, and yet it is the responsibility of the system admins and developers to make sure they are on top of the issue, and not just depend on vendors for notification. The current hole is the communication is not just that the security issues exist, but the best way to fix them when they occur.

Primary it is the CF SA's responsibility. BUT in case of major threat, a direct contact from Adobe wouldn't hurt.

Sys admins.

It should be the SA's responsibility to provide proper contact information to Adobe and maintain it's currency, and Adobe should be responsible to provide that information to those individuals. Adobe should also make it easy to find news about recent issues.

Any help a vendor can provide makes them a more valuable vendor to keep in the future.

There is a grey area here for me on this question. I believe, since the the license information for the CF purchase is tied to an Adobe account, that efforts could easily be made to notify people in a more timely fashion for zero hour exploits. The other side to this is in certain companies, and organizations, the person that needs that notification would not be the email associated with the purchase. You then end up in a situation where some administrative assistant or purchasing agent is receiving the emails. I suppose you could tail this argument out of control and assess them marking it as spam/junk, clicking a link to not receive updates anymore, etc.

Yes, Adobe should actively attempt to contact their clients if they are aware of a security hole.

In my opinion ... Adobe should halt CF 11, make sure that the update process works seamlessly, configure the install of CF on all platforms so that it is hardened by default, and get the community and white hat hackers to pound it until people are as reasonably confident as possible that it as secure as possible. You know, bug bounties and all that. Of course new vulnerabilities might become known in the future, but that is what the update process is for. Once CF 11 is 'the most secure version of ColdFusion ever' then Adobe should release it and reach out to anyone who has purchased ColdFusion in the past as well as post in their blogs on their website and in their marketing to get people to upgrade. During the install process the user should be prompted to enter their email address to be notified of updates and security alerts. In the admin interface the user should likewise be able to subscribe to an update/security alert. Ditto on the Adobe website under your account. Adobe should hire and make known the name of the CF security officer responsible within the organization whose job it is to review incoming reports, bugs, news, follow the latest in web attack trends, etc who will be responsbile to make sure that security issues get a high profile within Adobe and that those are communicated to people who sign up for security notices. The person should have a blog and be approachable so that users with security questions can ask advice and get referrals. Through these kinds of steps Adobe can get in front of and put to rest this issue, which has the potential to continue to harm the product and its users. The old saying 'justice must not only be done, but be seen to be done' can be rephrased in this case as 'ColdFusion must not only be as secure as possible, but we must show the world that we will do whatever it takes to make sure it stays that way'. Good survey!
This is a great comment. I like the idea of Adobe actually "'fessing up" and proactively taking some responsibility for all this. The idea of making an active - marketed - point of going to extra lengths to harden the product is good PR in my opinion (not that I know much about PR!)

Yes, Adobe should actively attempt to contact their client-base when any security breaches in ColdFusion are identified.

When significant ColdFusion security breaches occur, it would be nice to receive alerts from Adobe, considering they already received our contact information when we registered with them. If nothing else, it would be nice to have RSS feeds for each product's security events. I find it kind of obnoxious that in order to receive security updates from Adobe (through email), I have to sign up to receive notices about ALL products. If I'm only interested in CF security bulletins, the community should have the option to only receive those updates.

If you're a registered user of ColdFusion, you should be actively contacted by Adobe when security breaches are identified. Since signing up for Security Bulletins and subscribing to the Adobe Product Security Incident Response Team (PSIRT) Blog, I will say that I'm happy with the level of communications regarding security issues in ColdFusion.

Yes, definitely

Yes, they should. I don't recall getting much information from them even though we have Gold support through them.

Adobe needs to take responsibility for the product that they are selling. If a LAMP site gets hacked, there is less 'salt in the wound' since the stack itself costs nothing. But justifying paying 7k+ one would expect a significantly higher level of security and some accountability in the event of a hack.

Both, but Adobe should be informing customers.

I think they both have the responsibility. As much grounds should be covered when security issues arise. Being 100% responsible for proactive actions should be the goal. Adobe needs to work on their communication skills, especially the upper management responsible for overseeing ColdFusion. And System administrators can't just sit back and assume all is right in the world. Not as long as we constantly have criminals looking for ways to steal data.

Admins are responsible. Security issues hit back to Adobe and the CFML community though. So better communication might pay off in the end.

It would be nice for Adobe to be more pro-active at pushing out security hotfix updates.

Both are responsible Adobe should contact the user base and make them aware. But any responsible Admin should be actively following the news etc for all the software installed on their server. --- Coldfusion is insecure by default. After installing it, admins are expected to make it secure. This is completely the opposite to what it should be.

Both. Adobe should have a plan and methods (email, social etc) to send out notifications, but the Sys Admins should still be on the ball.

Yes of course.

Adobe should actively attempt to contact their client-base. But even if they do so, this doesn't take the resonsibility from administrators to keep their systems up-to-date. Breaking into a CF Server through an openly available unsecured CFIDE dir isn't a security breach, IMHO. It's more like inviting trouble.

It should be the responsibility of both. Adobe should however make it as easy as possible to find out or stay updated. There should be no reason to not know. Additionally were all lazy by nature so making it easy is the most likely way to avoid future breeches.

Adobe needs to be much more active in communicating security issues, and in pushing those notifications to interested devs/admins. If they are not going to use our contact information, they should offer some sort of push-based mailing list we can register for through which they can deliver notifications directly to us.

Yes. The always seem to follow up when ever I download a copy of ColdFusion, it should become an automatic opt-in for security notices. For as big as Adobe is, you'd think they'd have a decent communication/pr groups but seems like those resources are not given to the ColdFusion team. Then again Adobe as a whole doesn't communicate well regarding security.

I'm 99% certain of signed up for Adobes security notices in the past but i honestly can't remember ever getting an email. I just signed up again. http://www.adobe.com/cfusion/entitlement/index.cfm?e=szalert

Both! Being/staying secure should never be optional knowledge. One or both sides would be pretty ignorant otherwise. People pay good money to have a secure (or as secure as possible) product produced and maintained by Adobe/ACF Team. Without a doubt, whatever contact info is stamped on the account referenced to said server license(s), Adobe should indefinitely contact that client regarding vital info and/or steps to prevent and lock down any potential issues; not just publicly on a website. This alone is a priceless value that renders Adobe more valuable to the client in my opinion. On the flip side, whoever is maintaining said server(s) should damn well be on their game in regards to keeping the environment patched and secure. Company protocols and the lot regarding what can be updated/upgraded else risk compat issues etc. can go to hell in this instance. If your CF server is out of date with patching and general lock down concepts, you need to get it patched. Period. If you host valuable/sensitive information then you should be taking this stuff serious. Period. This mindset shouldn't just apply for CF either! I'll note however that I do not expect every sysadmin or whoever maintains a given CF server to be up to date from the usual go-to internet sources for CF info like Twitter and CF Community Blogs (though I'd highly recommend it!). There are a solid number of 9-5ers who do what they do and leave it at work. Which is why the process should still circle back to it being on Adobe to at least _attempt_ to make more "personal" contact with their clients on updates/issues. Cheers!

I think it is a duel responsibility. As someone who recently had a coldfusion server compromised I squarely place the blame on myself for not keeping Coldfusion updated with the most recent patch. I do however think that Adobe has an obligation to notify customers. We are not talking about something here that is open source. The financial investment that is made when choosing Coldfusion should include a certain level of support.

Adobe has the email address of everyone who downloads cf and has purchased a license so I find it hard to think of a reason that adobe can't send a simple email out when a security risk.is identified. After all they have no problem sending emails to promote buying the latest adobe product.

I think it's their "moral duty" to expend whatever effort is required to make sure word about exploits and vulnerabilities gets out to as many people as possible. This requires multiple channels: social media, blog postss, direct emails and for those customers with Gold Support contracts I think that followup phone calls should be considered.

There should be a way to subscribe to bulletins posted, with an obvious way to sign up.

Yes!

Adobe needs to do a far better job at communicating important server updates.

Perhaps not for every single exploit, especially since there is an option to sign up now. However, given the severity and level of exploitation of some of the vulns released last year I would have actively sent out notification if I were them. That being said, I do not personally own any CF server licenses so I cannot confirm whether or not they contacted license holders.

Developers are not in most cases the system administrators. I have been suggesting since the Allaire days that you let people have a place to plug system administrators emails into the server. Maybe even prompt for them to be filled out the first few times you log in. Do not send any marketing whatsoever to these people, just emails notifying them of the need for updates along with clear instructions. Clear enough that someone who knows nothing about ColdFusion could follow. Maybe even grab their cell numbers and send them a text message. Lots of times these same people are constantly lobbying management to replace CF with ASP.Net, don't give them more ammunition. They're not going to say they failed to patch the server in a timely manner - they will blame ColdFusion!!!

Contact people. Make every attempt. Act like you care.

Yes. As a commercial product that we pay big bucks for, I would expect them to email and/or phone us to notify of updates and security issues. But they don't (at least not for CF9).

Absolutely they should let us know

yes

Certainly as soon as they have a fix for a security issue, Adobe should notify ALL ColdFusion customers in their database - including trial account users who have downloaded the affected version(s).

Yes they should attempt contact, but it is also a sys admin's duty to keep informed and updated/patched.

Adobe should use every means they have to let their customers / users / developers know when there is a secuirty hole in their software and that there is a patch / fix to be applied. That being said, it is still up to the admin / developers to be on the lookout for these things as well, thus the reason I subscribe to a number of blogs, follow a number of CF devs on twitter, have them on the FB and everything else I can think of.... One big suggestion, how about a listserv hosted by Adobe just for ColdFusion Security Alerts...........

I think to be an effective administrator you need to be active seeking out issues

I think that a software company should allow for customers/users to sign up for notifications, preferably with some level of granularity. Then that company should send notifications, with the proper urgency, and in the method(s) that the customer chose. This is pretty basic, no? Furthermore, I think that have applications like this should have the ability to have push notifications enabled in the Admin (ala CF 10 and up) and frankly, it wouldn't be a bad idea to back-port, at the very least, the notification part of the CF 10 auto update to prior versions of CF regardless of support level. It's just good PR if nothing else: the fewer such articles as mentioned in this question, the better it is for said software company.

They have the information, so yes they should. Perhaps not for every single update that's pushed out to CF10 via the auto updater (but why not?); but for serious security holes that have been demonstrated to cause financial damage and also damage Adobe's own reputation, it's negligent and foolhardy for them not to.
Thanks for all the great input everyone! It's good to see any insight into how other people thing, and the ideas people have. Now... what do you think about each others' opinions / comments?
--
Adam

Built-in functions, UDFs, methods, and function expressions... with the same name

$
0
0
G'day:
I have no idea what I'm playing at. It's 9pm on Saturday night and I am in a downtown Galway ("Galway, Ireland", for you Americans... there's probably a Galway in TX, MN, CA as well ;-) pub, which was nice and quiet this afternoon and good for a quiet drink and some blog research. Now it's a full on Saturday night and everyone else is just partying and I'm still typing. The alternative is sitting here by myself and drinking, looking like... I'm sitting here by myself and drinking (and it'd not be the first time. Even today!). I guess at least I would not be the only person in the place writing a blog article about CFML.

Right, so today's efforts started with the intention of looking at some new methods in Railo: .some(), .every() (there's no docs for these yet - that I can find - but there is a Jira ticket: Add closure functions ArrayMap, ArrayReduce, ArrayEvery, ArraySome functions), and rounding out at look at any other CFML iteration functions I'd not looked at yet. I've looked at some previously from a ColdFusion perspective: "ColdFusion 11: .map() and .reduce()".

But then an interesting discussion came up on the Railo Google Group and the function map() which has been added to the latest Railo BER, which breaks WireBox: "Wirebox breaking on latest Railo patch 4.2.0.006", and this absorbed me for the rest of the afternoon and evening, thinking about it, wittering on on the forum, and testing some code. There's a few concepts discussed on the thread, and it's worth reading in its entirety.

Anyway, I decided to have a look at formalising my understanding of how much overlap is allowed between built-in functions and user-defined functions (be those stand-alone declared in a CFM, declared within a CFC, or defined as a function expression). And the behaviour varies a bit as it turns out.

I got to the point of having the code written yesterday at the pub, but all bar the first paragraph of this article is written the following evening. I pretty much gave up on the pub at that point (and I was actually pretty drunk by then), so waddled off into the night. Well: it was only mid evening actually... about 9pm. OK... it's now Sunday evening and completely pub-free today, and the only cibarial consideration in play at the moment is that I'm a bit hungry. But this does not usually impact my writing, so off we go.

The problem Alex (who started that thread on Google) was having with WireBox is that Railo have introduced a map() function (a generic handler for mapping structs, arrays, etc). This interferes with WireBox's own map() method, which Alex is using. This means in his CFCs when he calls map(), it now calls Railo's map() BIF, instead of WireBox's method of the same name. And so Alex's app goes splat.

If we were talking just a coupla instance of this (and it was solely Alex's own code), he could just qualify the calls to WireBox's map() method thus: variables.map(). That's enough to allow Railo to disambiguate as to which map() function Alex means to call. However there's a lot of code internal to WireBox that suffers the same problem, so that's a PitA. At the same time I muddied the water slightly by positing that the map() function in Railo was a bad decision to implement anyhow, so perhaps the best solution here was to get rid (bear in mind they also have arrayMap() and structMap() and a map() method on arrays and structs as well!).

Anyway, I knocked together some code to have a look at how BIFs and UDFs collide, like I said. Here it is:

//RailoVersion.cfc
component {

function ucase(s){
echo("ucase() method was called<br>")
return s.toUpperCase()
}

function viaUnscoped(s){
return ucase(s)
}

function viaScoped(s){
return variables.ucase(s)
}

}

<cfscript>
// railoTest.cfm

/*
// will not compile.
function ucase(s){
writeOutput("ucase() UDF was called<br>");
return s.toUpperCase()
}
*/

uCase = function(s){
echo("ucase() function expression was called<br>")
return s.toUpperCase()
}

o = new RailoVersion()
s = "tahi,rua,toru,wha"

echo("Using method<br>")
echo(o.ucase(s) & "<hr>")

echo("Using unscoped reference within CFC<br>")
echo(o.viaUnscoped(s)& "<hr>")

echo("Using scoped reference within CFC<br>")
echo(o.viaScoped(s)& "<hr>")


echo("Using unscoped ucase() within CFM<br>")
echo(ucase(s)& "<hr>")

echo("Using scoped ucase() within CFM<br>")
echo(variables.ucase(s)& "<hr>")
</cfscript>

Here we have three different versions of a ucase() UDF defined:
  • declared via a function statement as a method in a CFC
  • declared via a function statement as a function in a CFM
  • declared via a function expression as a function in a CFM
The first thing to note here is that the function statement version (in the CFM file) won't even compile. Railo prohibits it:

Railo 4.2.0.006 Error (template)
MessageThe name [ucase] is already used by a built in Function

We'll get back to that. Here's the output of the code above (with that second UDF definition commented-out):

Using method
ucase() method was called
TAHI,RUA,TORU,WHA


Using unscoped reference within CFC
TAHI,RUA,TORU,WHA

Using scoped reference within CFC
ucase() method was called
TAHI,RUA,TORU,WHA

Using unscoped ucase() within CFM
TAHI,RUA,TORU,WHA

Using scoped ucase() within CFM
ucase() function expression was called
TAHI,RUA,TORU,WHA


We see a few interesting things here.
  • Using a method call on an object: no problem, and the UDF is called. This is not surprising.
  • Using an unscoped reference within a CFC calls the BIF, not the UDF. This is what Alex was experiencing with map().
  • Using a scoped reference within the CFC calls the UDF. Calling it this way disambiguates between the BIF and the UDF.
  • Using an unscoped ucase() within a CFM unsurprisingly results in the BIF being called.
  • And finally using a scoped reference to ucase() within a CFM also disambiguates between the two, and the UDF is called.
Also interesting is how I can declare a function with the same name as a BIF via a function statement within a CFC just fine, but I cannot within a CFM. I don't think this is very logical. Especially when one considers that there's actually no problem with doing this at runtime with a function expression. To me this seems like having a rule for the sake of it. There is clearly no harm in defining a function thus; it works fine in a CFC. And there's also no problem with calling it: one just needs to scope it first. I'd like to understand the perceived difference here.

Note: ColdFusion works exactly the same here, barring the syntactical differences between the two CFML dialects.

That's all I have to offer on this topic. I've got about half the code written for the some() / every() article, and I have a few hours to kill at the airport tomorrow, so I'll finish it off and write the article then.

Time for me to investigate the burger joint across the road from the hotel (it's not an unknown to me... it'll be the third meal there this weekend).

--
Adam

Railo adds more iteration functions: *some() and *every()

$
0
0
G'day:
Starting with ColdFusion 10 and some-version-or-other of Railo 4 (Railo moves so quickly I can't keep track of when features are added), CFML has been adding collection iteration functions which leverage callback functions to provided the functionality for each iteration. Examples are arraySort(), listFilter(), structEach() (the coverage in each CFML dialect for each collection data type is not fully comprehensive nor do they necessarily both implement the same functions the other does). ColdFusion added in map and reduce functions (detailed in my article "ColdFusion 11: .map() and .reduce()"), now Railo has added those in, and gone one better: now adding some() and every() functionality too. In this article I'll recap each of the sort / filter / each / map / reduce functionalities, and have a look at some() and every().This is a Railo-centric article. The code will not run on ColdFusion without modification.

First: recap.

sort()

A sort() operation is fairly obvious: it sorts the collection. The iterator function takes two arguments, and it returns how those values compare: -1 if the first is less than the second; 0 if they're the same; 1 if the second is higher than the first (so same as how the compare() CFML function works). How the callback arrives at that result is arbitrary; it can do anything it likes. Here's an example using both a function and a method of the array object to sort some Maori numbers:

numbers = [
{maori="wha", digit=4},
{maori="toru", digit=3},
{maori="rua", digit=2},
{maori="tahi", digit=1}
]

arraySort(numbers, function(v1,v2){
return sgn(v1.digit - v2.digit)
})
dump(numbers)
echo("<hr>")

numbers.sort(function(v1,v2){
return sgn(v2.digit - v1.digit)
})
dump(numbers)

This - predictably - sorts the array first by the substructs' digit key value ascending, then by the digit key value descending. Here the formula is the same: taking the sign of the difference of the two digits; the difference between ascending and descending is just whether I subtract v1 from v2 or vice-versa.

I noticed something interesting about the sorting process, but that will be covered in my next article, as I have not yet worked out WTF is going on.

filter()

A filter operation iterates over a collection, and for each element of the collection calls a callback. Here the callback's job is simply to return true or false. If the value is true, the element is kept in the collection; if it's false it's discarded. Here's an example in which some people offer their favourite colours (in English for a change). We then filter so that only people who selected a unique favourite colour is included (so Carol with her selection of "green"). In the second example we filter so that the people that share a favourite colour with someone else is retained:

answers = {
angela = "purple",
bob = "orange",
carol = "green",
dave = "purple",
emma = "orange",
frank = "purple"
}

uniqueAnswers = structFilter(answers, function(key, value, struct){
return arrayLen(structFindValue(struct, value, "all")) == 1
})
dump(uniqueAnswers)
echo("<hr>")

sharedAnswers = answers.filter(function(key, value, struct){
return arrayLen(structFindValue(struct, value, "all")) > 1
})
dump(sharedAnswers)

Output:

Struct
CAROL
stringgreen

Struct
ANGELA
stringpurple
BOB
stringorange
DAVE
stringpurple
EMMA
stringorange
FRANK
stringpurple

Here I am demonstrating that the entire struct is passed into the callback as the third argument (along with first the key, then second the value of that key). This means filter callbacks can base their processing on the whole struct, not just the key/value pair for that iteration.

each()

An each() iteration does not "return a value" from the callback, per se, it just loops over the collection and runs the callback each iteration. The callback can do anything one likes at all. In this sample code I first demonstrate the each() function, and the arguments passed to the callback; then use the arrayEach() function to output each element of the array; then the each() array method to output the array elements in uppercase.

numbers = ["tahi","rua","toru","wha"]

each(["single"],function(value,index,array){
dump(arguments)
})

echo("<hr>")

arrayEach(numbers, function(v){
echo (v & "<br>")
})
echo("<hr>")

numbers.each(function(v){
echo(v.toUpperCase() & "<br>")
})

Scope Arguments
value1
stringsingle
index2
number1
array3
Array
1
stringsingle

tahi
rua
toru
wha


TAHI
RUA
TORU
WHA


The arguments passed in are the element value, its index in the array, and the whole array.


map() and reduce() are new to Railo.

map()

A map operation iterates over the collection, and - as expected - executes a callback for each element of the collection. The callback should return a value which becomes that element's equivalent value for a new collection. An example will make that more clear:

answers = {
angela = "purple",
bob = "orange",
carol = "green",
dave = "purple",
emma = "orange",
frank = "purple"
}

individualsAnswerUniqueness = map(answers, function(key,value,struct){
return {colour=value,factor=arrayLen(structFindValue(struct, value, "all"))}
})
dump(individualsAnswerUniqueness)
echo("<hr>")

Here we have the favourite colours again, but we create a new struct which shows the "uniqueness" of each person's favourite colour within the group (lower is better):

Struct
ANGELA
Struct
COLOUR
stringpurple
FACTOR
number3
BOB
Struct
COLOUR
stringorange
FACTOR
number2
CAROL
Struct
COLOUR
stringgreen
FACTOR
number1
DAVE
Struct
COLOUR
stringpurple
FACTOR
number3
EMMA
Struct
COLOUR
stringorange
FACTOR
number2
FRANK
Struct
COLOUR
stringpurple
FACTOR
number3


The returned collection has the same element keys as the initial one, but new values for each element based on the callback.

Railo implements the controversial generic map() function, as well as type-specific functions like arrayMap() and structMap(), as well as map() methods on the various collection data types. I understand map() is going to be removed, being replaced with collectionMap() ("map()" caused problems in WireBox, as discussed yesterday: "Built-in functions, UDFs, methods, and function expressions... with the same name").

reduce()

This is probably the most complicated of the iteration functions as it builds a new value, based on the iteration process. It can be used to "flatten" a collection, which I've demonstrated before, but it can just be used to generate any completely different data structure, as demonstrated here:

answerTally = answers.reduce(function(previousValue,key,value,struct){
if (!structKeyExists(previousValue, value)){
previousValue[value] = {
tally = 0,
people = []
}
}
previousValue[value].tally++
arrayAppend(previousValue[value].people, key)
return previousValue
}, {})
dump(answerTally)

(This assumes the same colour picks as the earlier examples).

This one outputs this:

Struct
green
Struct
PEOPLE
Array
1
stringCAROL
TALLY
number1
orange
Struct
PEOPLE
Array
1
stringEMMA
2
stringBOB
TALLY
number2
purple
Struct
PEOPLE
Array
1
stringFRANK
2
stringDAVE
3
stringANGELA
TALLY
number3

Basically it loops over the colour-pick collection and creates a summary object which tallies things up from the perspective of colour, not person: it lists who likes the given colour, and gives a tally as well.

The reduction process starts with a base value, and then each iteration takes that value as a starting point, and returns a new value. Here I am building on the passed-in value, but equally the value returned from each iteration could be a completely new value; an example would be summing all the elements in an array:

numbers = [1,2,3,4]
sum = numbers.reduce(
function(prev,current){
return prev+current;
},
0 // this is the starting point
)
dump([numbers,sum])

Array
1
Array
1
number1
2
number2
3
number3
4
number4
2
number10

And now the stuff new to both Railo and CFML in general.

some()

The some iteration function returns a boolean. It returns true if some of the elements in the collection meet the test defined by the callback. Here's an example:

param name="URL.showTelemetry" default=false; // need a semicolon here for some reason

numbers = ["tahi","rua","toru","wha","rima","ono","whitu","waru","iwa","tekau"]

some = numbers.some(function(v,i,a){
if (URL.showTelemetry) echo("Checking: #i# #v#<br>")
return v[1] == URL.letter
})
echo("Does #numbers.toString()# contain any numbers starting with '#URL.letter#'? #some#<hr>")

Here I take some use input, and check to see if any of the numbers (we're back to Maori again) start with that letter. I also have an optional switch to output some telemetry, showing how the some() process iterates. Here's a basic run, just checking for the first letter being "w":

Does [tahi, rua, toru, wha, rima, ono, whitu, waru, iwa, tekau] contain any numbers starting with 'w'? true

No surprises then: Maori's fairly heavy on W usage, so any of wha, whitu or waru (btw, "wh" in Maori is pronounced more like an "f" in Japanese, not like in English. Like half way between an F and an H; it's quite hard for an English-only-speaker like me to voice) fit the bill there.

Let's have a look at what happens here with some telemetry being output:

Checking: 1 tahi
Checking: 2 rua
Checking: 3 toru
Checking: 4 wha
Does [tahi, rua, toru, wha, rima, ono, whitu, waru, iwa, tekau] contain any numbers starting with 'w'? true

So that's good news: it only iterates until it finds the first case that fulfills the requirement, then exits. There's no point continuing after it's found at least one element that matches the "some" criteria.

Also note in here I'm using a cool Railo trick: one can access a string via array notation: here v is the value of the element - eg "wha" - so v[1] is "w". Cool!

every()

every() is the spiritual opposite of some(): it returns true if all the collection elements match the criteria dictated by the callback. Again, this is determined by iterating over the collection, and calling the callback for each element until the first definitive result is returned: in this case a false. Here's an example:

every = numbers.every(function(v,i,a){
if (URL.showTelemetry) echo("Checking: #i# #v#<br>")
return v.length() >= URL.length
})
echo("Are all of #numbers.toString()# at least #URL.length# characters long? #every#<hr>")


This is similar to the previous one, except this time we check to see whether all the values have a length greater-than or equal-to the threshold. If we run this code with a length value of 3, we get this test run:

Checking: 1 tahi
Checking: 2 rua
Checking: 3 toru
Checking: 4 wha
Checking: 5 rima
Checking: 6 ono
Checking: 7 whitu
Checking: 8 waru
Checking: 9 iwa
Checking: 10 tekau
Are all of [tahi, rua, toru, wha, rima, ono, whitu, waru, iwa, tekau] at least 3 characters long? true

Obviously here it needs to iterate over everything to get a true value. On the other hand if we check for 4 instead of three, we exit quite quickly:

Checking: 1 tahi
Checking: 2 rua
Are all of [tahi, rua, toru, wha, rima, ono, whitu, waru, iwa, tekau] at least 4 characters long? false

"rua" doesn't fulfill the requirement of the callback, so that's it: the result is false.

Railo has implemented both a type-specific function for every() (eg: arrayEvery()), as well as a method on the array type, as demonstrated above.


What's next?

Railo still hasn't implemented any iteration functions for query objects (for that matter nor has ColdFusion... I dunno why this wasn't just an obvious requirement from the outset?), and Railo-specifically has not implemented them for lists. I am split on this. The dogmatist in me thinks "like it or not, CFML has a 'collection' data type which is a list, so for the sake of completeness and uniformity of the language, list-oriented iteration functions should be implemented along with the rest". The more pragmatic side of me thinks "yeah, but lists were a shite concept from the outset, so the less time spent 'enhancing' them the better". And it seems the community is split on this, from what I've read.

Micha is currently asking for input into how the query iteration functions should be implemented ('Query "closure" functions'), so go over to the Jira ticket and have a read and put your oar in if you have an opinion. Even if you don't use Railo, odds-on when Adobe get around to implementing these for ColdFusion, they will probably (/hopefully) follow Railo's lead, so go offer your input even if yer not a Railo user.

Sorry there was a lot of recap in this article compared to the earlier CF11 article coverings its new functions (which also had recap of the the older iteration functions). Hopefully it wasn't too dull. I really like these new functions, and can't wait to upgrade our production environment to be able to use them.

Nice work Railo. And, hey, nice work Adobe for their equivalent efforts.

--
Adam

myArray.each()... extending the array whilst iterating..?

$
0
0
G'day:
I dunno what to think about some behaviour I'm seeing when iterating over an array with the .each() method.

Consider (but do not run!) this code:

numbers = ["one","two","three","four"];
numbers.each(function(value,index){
if (value=="one") {
numbers.append("five");
}
writeOutput("Index: #index#; value: #value#<br>");
});
writeDump(var=numbers);


What would you expect the result to be? One might expect four iterations because the array to iterate is only evaluate at the outset of the loop; or one might expect five iterations because by the time we get to the fourth element, there is now a fifth element. Either of these would be legit behaviour to me.

On Railo I get this:

Index: 1; value: one
Index: 2; value: two
Index: 3; value: three
Index: 4; value: four

Array
1
stringone
2
stringtwo
3
stringthree
4
stringfour
5
stringfive

But on ColdFusion I get this:

Index: 1; value: one
Index: 2; value: two
Index: 3; value: three
Index: 4; value: four
Index: 5; value: five

array
1one
2two
3three
4four
5five

Railo takes the former approach: the iterations to be done seem to be calculated at the beginning of the loop, not each iteration; whereas ColdFusion takes the approach of simply iterating over whatever array is there.

Hmmm. What I do know is that both should do the same thing! CFML should be consistent in areas were both vendors implement the same thing!

To add more question marks, I ran this JS version:

numbers = ["one","two","three","four"]
numbers.forEach(function(value,index,array){
if (value=="one") {
numbers.push("five")
}
console.log("Index: "+ (index+1) + "; value: "+ value)
})
console.log(numbers)

And that resulted in this:

Index: 1; value: one
Index: 2; value: two
Index: 3; value: three
Index: 4; value: four
["one", "two", "three", "four", "five"]


And this code on Ruby:

numbers = ["one","two","three","four"]
numbers.each_with_index { |value,index|
if value=="one" then
numbers.concat ["five"]
end

puts "Index: %d; value: %s" % [index+1,value]
}

Which yielded this:

Index: 1; value: one
Index: 2; value: two
Index: 3; value: three
Index: 4; value: four
Index: 5; value: five
["one", "two", "three", "four", "five"]


So JS does the same as Railo; Ruby the same as ColdFusion.

I like the Ruby / ColdFusion way better, I think.

Thoughts?

--
Adam

Using a sort iteration function to implement a custom ranking

$
0
0
G'day:
It occurred to me last night that - in all my examples of using a callback-based sort on a collection - I am always just using the natural ordering of a field; eg I sort an array of structs on a key in the struct which needs either alphabetical or numeric sorting. EG:

numbers = [
{maori="wha", digit=4},
{maori="toru", digit=3},
{maori="rua", digit=2},
{maori="tahi", digit=1}
]

dump(numbers.sort(function(v1,v2){
return sgn(v1.digit - v2.digit)
}))


This is selling the functionality short, somewhat.

Here's an example of using a callback sort to effect sorting on a custom sequence: not numeric or alphabetical, but any custom sequence one might want. I achieve this by using closure to create a custom sort callback, then using the callback in the sort operation:

function customRankings(required array options, required string field, boolean throwOnNoMatch=false){
return function(v1,v2){
var v1Place = options.findNoCase(v1[field])
var v2Place = options.findNoCase(v2[field])

if (v1Place && v2Place){
return sgn(v1Place - v2Place)
}
if (throwOnNoMatch){
throw(type="IllegalValueException", message="Incorrect value in sort field", detail="Sort field must contain only #options.toString()#")
}

if (v1Place){
return -1
}

if (v2Place){
return 1
}

if (isNumeric(v1[field]) && isNumeric(v2[field])) {
return sgn(v1[field] - v2[field])
}

return compareNoCase(v1[field], v2[field])
}
}


medalSorter = customRankings(["gold","silver","bronze"], "place")

raceResults = [
{lane=1, name="Angus", place="silver"},
{lane=2, name="Barbara", place="gold"},
{lane=3, name="Colin", place="5th"},
{lane=4, name="Ethel", place="4th"},
{lane=5, name="Faisal", place="bronze"}
]

dump(raceResults.sort(medalSorter))


There are three sections to this:
  1. a function which returns a sort callback based on the rankings provided;
  2. creating a sorter for the given rankings;
  3. sorting data by the sorter.
Looking closer at the function:

function customRankings(required array options, required string field, boolean throwOnNoMatch=false){
return function(v1,v2){
var v1Place = options.findNoCase(v1[field])
var v2Place = options.findNoCase(v2[field])

if (v1Place && v2Place){
return sgn(v1Place - v2Place)
}
if (throwOnNoMatch){
throw(type="IllegalValueException", message="Incorrect value in sort field", detail="Sort field must contain only #options.toString()#")
}

if (v1Place){
return -1
}

if (v2Place){
return 1
}

if (isNumeric(v1[field]) && isNumeric(v2[field])) {
return sgn(v1[field] - v2[field])
}

return compareNoCase(v1[field], v2[field])
}
}

It takes up to three arguments:
  1. an array of options to sort on. The order in the array represents highest to lowest (eg: ["gold","silver","bronze"]).
  2. A field in each structure to use for sorting. This example only works on an array of structs, obviously.
  3. Optionally whether to allow any value in that field, or to throw an error if there's a different value.
 The logic then is fairly simple:
  • find the values in the array of options
  • if they're both found, sort on those two as per the normal sorting rules
  • if they're not both found, and if we're flagged to raise an error if this happens, do so
  • otherwise if either item contains one of the sort options, it is ranked higher than the other value
  • finally - so neither value is one of the options - either treat them numerically if possible, otherwise as strings, and sort on that.
Once we've done that, it's just a matter of calling that function to create a custom sorter, then using it.

We use closure in customRankings() to bind references to the options and field values when creating medalSorter(). Note that we only need to specify those when we call customRankings()... they're enclosed within medalSort() at that point, so medalSorter()"remembers" what the options and field were. Hence it's specifically a medalSorter() not simply some generic sorter. So it pretty much binds the input data and the code to process the data together in one. Also note that the function returned also takes two arguments: v1 and v2 which are what the callback passed to an array sort will receive.

Let's revisit the actual sort() call:

raceResults.sort(medalSorter)

sort() takes a function. Often we see the function being passed inline as a function expression, eg:

numbers.sort(function(v1,v2){
return sgn(v2.digit - v1.digit)
})

But sort() doesn't take a function expression, per se, it just takes a function. A function expression returns a function, but we can use an already-existing function if we want. In fact we could also have done without creating the medalSorter() function at all, and simply do this:

raceResults.sort(customRankings(["gold","silver","bronze"], "place"))

Here the value passed to sort() is neither an existing function nor a function expression, but we call a function which returns a function. So sort() still receives the function it needs.

One thing that's not immediately clear with my example above is that customRankings() can be reused to provide more custom sorters. We can build on that code above:

raceResults = [
{lane=1, name="Angus", place="silver"},
{lane=2, name="Barbara", place="gold"},
{lane=3, name="Colin", place="5th"},
{lane=4, name="Ethel", place="4th"},
{lane=5, name="Faisal", place="bronze"}
]
medalSorter = customRankings(["gold","silver","bronze"], "place")
dump(raceResults.sort(medalSorter))


rugbyRankings = [
{country="Australia", ranking="3rd"},
{country="England", ranking="4th"},
{country="New Zealand", ranking="1st"},
{country="South Africa", ranking="2nd"}
]
ordinalSorter = customRankings(["1st","2nd","3rd","4th"], "ranking")
dump(rugbyRankings.sort(ordinalSorter))

So here we're using customRankings() a second time to create another custom sorter. The code for customRankings() might be a bit long, but once it's there: using it is a simple one liner. Which is pretty cool, I reckon.

Oh, and the output for all this is:

Array
1
Struct
LANE
number2
NAME
stringBarbara
PLACE
stringgold
2
Struct
LANE
number1
NAME
stringAngus
PLACE
stringsilver
3
Struct
LANE
number5
NAME
stringFaisal
PLACE
stringbronze
4
Struct
LANE
number4
NAME
stringEthel
PLACE
string4th
5
Struct
LANE
number3
NAME
stringColin
PLACE
string5th


Array
1
Struct
COUNTRY
stringNew Zealand
RANKING
string1st
2
Struct
COUNTRY
stringSouth Africa
RANKING
string2nd
3
Struct
COUNTRY
stringAustralia
RANKING
string3rd
4
Struct
COUNTRY
stringEngland
RANKING
string4th

I hope this goes some way to better demonstrate how cool these iteration functions are. And - for a change - actually using closure with them too.

--
Adam

Sharing a conversation about clean code

$
0
0
G'day:
I'm being a bit lazy today. And interesting conversation cropped up on the comm log of one of the side projects I'm working on, and I thought you might be interested to read it, and perhaps add your thoughts.

We were discussing how to handle the requirement for a new API function we're adding to our localisation suite, which works similar to dayOfWeekAsString(), except the immediate requirement it to provide a localised "short version" of the day, eg: "Mon", "Tue", etc. The initial suggestion had come in as dayOfWeekShortAsString(). And the conversation started. I am "Mr Green", incidentally. I have made a few edits to clean up spelling and grammar, but I have not removed anything, or changed the context of what was said. I have added some clarification (for the purposes of this article) part-way down.

Topic:
dayOfWeekAsString() / dayOfWeekShortAsString()


Mr Red
Should we make merge them to one function with a new argument "short:boolean"?


Mr Green
It's bad coding practice to add boolean arguments to a function. I recommend using a mask - as with dateFormat() - instead (as that actually reflects what you want to be doing. A boolean is meaningless)


Mr Red
I disagree. IMO It's a bad idea to use string arguments to a function in an untyped language like CFML. A boolean is very meaningful when there are only two options as long as the argument name is descriptive. Then you know that the value is either true or false, not "milliseconds", "millisec", "millis", "milli", "ms", "m", etc. If you use a strongly typed language where the IDE can type it for you then it's a different story.


Mr Blue
Boolean arguments are a code smell. Mr Green is right. "Robert C. Martin's Clean Code Tip #12: Eliminate Boolean Arguments""avoid boolean parameters" and "Code Smell Metrics" also talks about this.


Mr Red
I still disagree. the examples you cited use a strongly typed language where you can use enums and static variables, which is very different from passing a string value to a function as an argument.

This might be a good rule for Java, but not for CFML and its like (ie: The Application).

[Just to contextualise here, "The Application" has a large and richly featured public API which developers using The Application use to write their own sub-applications. So this conversation is very much from the perspective of adding methods to an API, not simply thinking about stand-alone functions, or an inward-facing system. The code doesn't simply need to make sense to us: it needs to make sense to our client developers too].

Mr Blue
Read the articles again. It's nothing to do with the type system: boolean flags are a code smell in any language.


Mr Red
Of course that it has to do with type system. In a strongly-typed language the IDE and/or compiler will autocomplete/check the values for you so there is no possibility of you to make a mistake like writing getTickCount('nanoseconds') when the correct value should be getTickCount('nanos'), or maybe it is getTickCount('nano')... who knows?

In a typed language you will pass a value that is checked at edit time so that you don't get weird behavior at runtime with months going by before you even find out that there is a bug somewhere in the code.


Mr Blue
I'm sorry Mr Red but you are completely and utterly wrong: this has absolutely nothing to do with types or IDEs and absolutely not "better" than some string. You are totally missing the point here. A Boolean value has absolutely no semantics beyond a binary flag. It doesn't describe anything. That's why it is a code smell. foo(42, true, false) vs foo(42, true, true) vs foo(42, false, false). That's a useless API that tells you nothing about what the calls mean. You have to know the implementation: so Boolean flags are a leaky abstraction.


Mr Red
You are certainly entitled to your strong opinions, Mr Blue, but that does not mean that I am wrong. of course that the words true and false do not have a semantic value. The semantic value is in the argument name.

There is one thing that is much worse than the lack of semantics and that's the passing of strings which are prone to errors and are less efficient in checking.

IMO a function that is declared as:
find(string input, string substring, boolean ignoreCase)
which you would then call as
find("abcde", "Bc", true)
is much better than
find(string input, string substring, string flags)
which will be called as
find("abcde", "Bc", "ignoreCase")
or maybe it was "notCaseSensitive"? or perhaps "caseInsensitive"? or...


Mr Purple
That article "avoid boolean parameters" is
from 2004, at that time I was still using Allaires Kawa Editor (I
think) and for this editor that argument maybe was valid, but in Eclipse or
Netbeans a simple mouse over tells me the function arguments, problems
today in Java is not having boolean arguments, problem is having 100
methods in a class that are slightly different and this is exactly what the
article suggest, to more methods! I always try to reduce methods in my
classes.

Maybe it is handy to read existing code when you have 1000 of slightly

different methods, but it is certainly bad for writing code.

That is the point, Mr Red's way is handy to write code, Mr Green's solution is handy
to read code. Mr Red knows that I hate to at new functions to The Application, I'm
afraid of the "PHP Effect", having 1000 of functions doing slightly the same.


Mr Green
Be it from 2004, is there industry consensus that has decided "clean code" is not still something to strive for? Or that the premises offered somehow are now incorrect? (Not a purposely rhetorical question, but I suspect know the answer)?

Considering the function at hand, a boolean argument - whether or not boolean arguments are poor programming or not - is simply painting one's self into a corner anyhow. It's not a case of "short: yes or no": In English - I cannot vouch for other languages - there are at least three accepted shortenings of a day of the week: "d", "dd", "ddd" (with "dddd" being "long-form"). How does one reflect that with a boolean? If we have one or the other, which are we picking?

Mr Purple has speciously posed that a boolean argument is better than multiple functions. This is tenuous, but irrelevant here: no-one's suggesting that. I suggested having a mask which one can use which offers all the required functionality (a boolean doesn't), has an already-set precedent in the language (date/time format), and also doesn't suffer from Mr Red's concern that invalid strings can cause unexpected errors. If the mask is not one of the supported patterns: the code can simply error.

Mr Red does offer a reasonable suggestion that Java offers enumerations to cover optionality rather than strings. CFML doesn't, and there's a healthy precedent there to invalidate that position, but if we were to decide that using enumerations are better than strings... cool... implement that.

But using a boolean is an invalid suggestion here.


Mr Red
I'm sorry, I should have clarified: my comments were about the general statement against boolean arguments, and not specifically to the Format functions.

Formatting functions and their like should take a mask for an argument -- I wasn't suggesting that we add 17 boolean arguments instead of a mask ;)
So what do you think here? Obviously the conversation (apparently) strayed from the original issue a bit, but I think the positions everyone states are relevant to the function at hand, as well as to the notion of how to write functions in general.

What do you think?

--
Adam

We don't need bigots in this industry

Survey: lists in CFML, and the naming of list member functions

$
0
0
G'day:
There's a conversation brewing on the Railo Google Group regarding string / list member functions ("String member functions and list functions"), and this touches on ColdFusion's approach to these.

TL;DR: the survey is here: "List function support as member functions in CFML"


The question being asked on the Railo forum is whether to bother supporting list member functions in Railo at all. And as an aside to this, some lack of uniformity in how ColdFusion 11 has implemented these has cropped-up.

Firstly, I'll "go on record" as saying that I think having a specific concept as a "list" as a (pseudo) data type in CFML is a bit rubbish. Strings aren't intended for implementing data collections, and aren't very good at doing it, so - IMO - it was a poor idea to facilitate it. If Railo and ColdFusion deprecated the concept of lists entirely, the (CFML) world would be a better place.

That said, they are in the language as a concept, so we have to acknowledge this I guess.

Adobe have added list member functions to CFML in ColdFusion 11.  Here's a table of their implementation ("Supported List member functions"):

Supported List member functions

The following List member functions are supported:
ListAppendsomeVar.append()
ListChangeDelimssomeVar.changeDelims()
ListContainssomeVar.contains()
ListContainsNoCasesomeVar.containsNoCase()
ListDeleteAtsomeVar.deleteAt()
ListFindsomeVar.ListFind ()
ListFindNoCasesomeVar.ListFindNoCase()
ListFirstsomeVar.first()
ListGetAtsomeVar.getAt()
ListInsertAtsomeVar.InsertAt()
ListLastsomeVar.last()
ListLensomeVar.listLen()
ListPrependsomeVar.prepend()
ListQualifysomeVar.qualify
ListRestsomeVar.rest()
ListSetAtsomeVar.setAt()
ListSortsomeVar.sort()
ListToArraysomeVar.toArray()
ListValueCountsomeVar.valueCount()
ListValueCountNoCasesomeVar.valueCountNoCase()
ReplaceListsomeVar.replace()

I've highlighted where I think they've got this wrong, and where they've got it right. And where I dunno how it's supposed to work, as there's already a differently-functioning function of that name already.

The problem here is that these are all string member functions (ie: you call them on a string object), and the fact they're actually designed to work on a special sort of string is lost in the current function naming. eg: what does a rest() function do in the context of a string? It makes no sense. prepend() is understandable (one can prepend to a string), but when one sees that one's string has been prepended with the substring specified, and a comma, it stops making sense. The function names have to "work" in the context they are being called. They should not require special knowledge that they are "list" functions. This should be reflected in the name of the function.

I've also created a survey to get your thoughts on the matter: "List function support as member functions in CFML". Please go and put your oar in.

--
Adam
Viewing all 1332 articles
Browse latest View live