A Military Virus |
The virus threats we worried about in the late 90s are quite different than the one we're worrying about in 2020, because someone looked at word processors and said, "You know what that needs? That needs a full fledged programming language I can embed in documents."
Alex was a sailor for the US Navy, fresh out of boot, and working for the Defense Information School. The school taught sailors to be journalists, which meant really learning how to create press releases and other public affairs documents. The IT department was far from mature, and they had just deployed the latest version of Microsoft Word, which created the perfect breeding ground for macro viruses.
Alex wasn't in the IT department- not yet- and was actually learning to be a journalist. At the end of classes one day, he saved a bunch of Word documents to a shared computer. The next day, he came in and found they were all destroyed- as were everyone else's.
The chiefs in the school sent out stern warnings about viruses, and not running any unfamiliar executables. Lots of work was lost, but this was the late 90s: many of the documents got printed out for review anyway, so there were hard copies of much of the classwork. After a week, things were basically back to normal.
And then the virus hit again.
Cue another round of stern warnings, and NCOs chewing out anybody who even looked at the computers wrong, or thought about running an unauthorized executable. One Seaman got caught with a floppy containing WOLF3D.EXE
, and while no one in the barracks knew exactly what happened to him, everyone agreed it must have been pretty dire.
After the third time, Alex decided to offer his services. He wasn't an IT professional, but he was pretty technical. Maybe he could take a look?
Chief Donaldson thought it over for a few moments. "Well, kid, it's already jacked up. You probably can't make it any worse. Take a look."
Anti-virus protection was provided by Norton. It had been configured to use a whitelist to decide which files to scan. That whitelist didn't include *.doc
files. Alex made that fix on his computer, and showed Chief Donaldson what he'd done.
The IT department was notified, but had no interest in listening to some kid fresh out of boot camp. They'd been in the service for years, so there was no need for them to change their policies or procedures.
Alex shared his knowledge with his peers. It quickly percolated out through the Navy barracks at the Defense Information School, and without the IT department doing anything, very quickly nearly all of the computers the Navy used were protected against macro viruses. The same couldn't be said about the other branches of military service, which each had their own barracks at the Defense Information School.
Still, the virus activity was severely curtailed. For months, nobody thought about anything, and the IT department patted themselves on the back for making a really good show of stopping the virus. And then came the "field trip": the budding military journalists would go to another military base to participate in a war game, simulating a "real world" scenario which would let them hone their journalistic skills under fire.
There was one problem. The only computers that had been configured to defend against macro viruses were the ones Alex and his peers had personally protected. Those were back at the Defense Information School Navy barracks. Here, they were using computers that were tagged for field deployments.
This meant they were about 15 minutes into the wargame when a Word virus started forwarding itself through every newsroom computer on the simulated battlefield. Since this was a number of branches at the same time, it was an even more varied collection: some raced to delete files, others opened as many porn sites as they possibly could, and yet others simply played an animation claiming that they were formatting your hard drive (they weren't).
You might think that makes this more of an IT education than a journalistic experience. Certainly, Alex eventually moved on to become an IT professional in the civilian sector. But there was a very valuable lesson on military journalism to be had, in the form of the after action report on the exercise. It managed to bury the entire IT disaster with a brief blurb: "Computer viruses and malfunctions were not part of the exercise parameters. These should be part of the plan for future exercises."
Метки: Feature Articles |
Error'd: Let's Dazzle Them with Errors! |
"Hmmm...Somehow, I can't seem to remember the password for this particular AppleID," writes Thomas G.
"The video will be finised processing in roughly 585 million years. (That's later than the lifetime of some of the stars in the Orion constellation)," David K. wrote.
Brian A. writes, "AWS Canary only speaks English, huh? Sure about that?"
"Every now and then, I change the language of my computer and here is an example of the result," wrote Beatrix W., "Not only does Apple mix German with English but the translations for the languages get confused too."
Bryan writes, "Apparently, you can be fooled again and again by the wrong lyrics for this popular song from The Who."
"When it comes down to it, captchas aren't about proving that you're a human, but rather proving that you're able to think like a computer. If you give the computer what it expects, then you've passed! (Image: verified!)" Daniel S. wrote.
https://thedailywtf.com/articles/let-s-dazzle-them-with-errors
Метки: Error'd |
CodeSOD: Deep VB |
Thomas had an application which was timing out. The code which he sent us has nothing to do with why it was timing out, but it provides a nice illustration of why timeouts and other bugs are a common “feature” of the application.
The codebase contains 9000+ line classes, functions with hundreds of lines, and no concept of separation of function. So, when someone needed to look at an account number and decide if that account needs special handling, this is what they did:
For i As Integer = 0 To R - 1
If DataSet.Tables(0).Rows(i)("Code") = Code Then
If Not IsDBNull(DataSet.Tables(0).Rows(i)("Estimate")) Then
If DataSet.Tables(0).Rows(i)("Estimate") <> "E" Then
If Not (IsDBNull(DataSet.Tables(0).Rows(i)("Code2"))) Then
If Not (DataSet.Tables(0).Rows(i)("Code2").contains("XX")) Then 'does not perform the test on XX accounts
If Not (DataSet.Tables(0).Rows(i)("Code2").contains("YYY")) Then
If Not (DataSet.Tables(0).Rows(i)("Code2") = "ZZZZZZ") Then
If Not (DataSet.Tables(0).Rows(i)("Code2").contains("ZZZ")) Then
If (Not (DataSet.Tables(0).Rows(i)("Code2").Contains("CC"))) Or (Not (DataSet.Tables(0).Rows(i)("Code2").Contains("cc"))) Then
If Not (DataSet.Tables(0).Rows(i)("Code2") = "AAAAAA") Then
If Not (DataSet.Tables(0).Rows(i)("Code2").StartsWith("BB")) Then
If Not (DataSet.Tables(0).Rows(i)("Code2").Contains("BBBB")) Then
If Not (IsDBNull(DataSet.Tables(0).Rows(i)("Location"))) Then
Dim tempLocation As String = DataSet.Tables(0).Rows(i)("Location")
If Not (tempLocation.Contains("XX")) Then 'does not perform the test on XX accounts
If Not (tempLocation.Contains("YYY")) Then
If Not (tempLocation = "ZZZZZZ") Then
If Not (DataSet.Tables(0).Rows(i)("Location").contains("ZZZ")) Then
If tempLocation = "CCCCCC" Or tempLocation = "CCCCCC" Then
GoTo Skip
Else
If Not (DataSet.Tables(0).Rows(i)("Location") = "AAAAAA") Then
If Not (DataSet.Tables(0).Rows(i)("Location").StartsWith("BB")) Then
If Not (DataSet.Tables(0).Rows(i)("Location").Contains("BBBB")) Then
If DataSet.Tables(0).Rows(i)("Date") > LastMonth Then
LastMonth = DataSet.Tables(0).Rows(i)("Date")
If Not IsDBNull(DataSet.Tables(0).Rows(i)("Present")) Then
LastReading = DataSet.Tables(0).Rows(i)("Present")
Else
LastReading = 0
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
skip:
Next
I don’t know what this code does. I don’t really want to. It’s a stack of if statements so deep that it provides a habitat for monothalameans.
The real treat, though, is that hidden goto
, which sort circuits the loop. Thomas didn’t know that labels, in VB.Net, were scoped to functions, so you can re-use the same label, skip
as often as you like, and don’t worry, the developer who wrote this did. skip:
s all over the place.
Метки: CodeSOD |
CodeSOD: GUID Enough, I Guess |
Philemon Eichin has a depressing problem: his codebase is full of WTFs and git blame
always returns his name. It's not his fault! Before he joined the team, "source control" was "a zip file named Project.Final.Real.Finished.CurrentRelease.zip
.
Periodically, he'll trawl through the codebase, tracking down ugly, bad code and fixing it, as a way to cut down on how many WTFs are attached to his name.
For some reason, and Philemon isn't sure, he didn't fix these methods. He just slapped an Obsolete
annotation on them, which explains what he should have done. Maybe he didn't want to fight with regressions, since there weren't any unit tests. Maybe he just liked the logging output. Regardless, this remains in his codebase:
// Konvertierung einer z.B. im Oracle-Server als
// VARCHAR2(32) gespeicherten Guid in ein f"ur den
// System.Guid Datentyp (z.B. im SQL-Server) g"ultiges
// Beispiel:
// SQL-Server: {ADF60E63-0D32-4423-B044-631E08093E3C}
// Oracle-Server: ADF60E630D324423B044631E08093E3C
[Obsolete("Replace with Guid.Parse")]
public static Guid GuidFromString(string value, ILog log = null)
{
Guid trgValue = Guid.Empty;
try
{
string newId =
$"{value.Substring(0, 8)}-{value.Substring(8, 4)}-{value.Substring(12, 4)}-{value.Substring(16, 4)}-{value.Substring(20)}";
trgValue = new Guid(newId);
log.DebugFormat("GuidFromString() In:<{0}> Out:<{1}>", value, trgValue);
}
catch (Exception ex)
{
log.Error("String => Guid Conversion failed!", ex);
}
return trgValue;
}
// Konvertierung des System.Guid Datentyps (z.B. im SQL-Server)
// in einen (z.B. im Oracle-Server verwendbaren) String mit
// fester L"ange von 32-Zeichen, z.B. als (VARCHAR2(32)
// Beispiel:
// IN: {ADF60E63-0D32-4423-B044-631E08093E3C} [SQL-Server]
// OUT: ADF60E630D324423B044631E08093E3C [Oracle-Server]
[Obsolete("Replace with guid.ToString(N).ToUpper()")]
public static string GuidToString(Guid value, ILog log)
{
string s = value.ToString();
s = s.Replace("{", "").Replace("-", "").Replace("}", "").ToUpper();
string trgValue = s;
log.DebugFormat("GuidToString() In:<{0}> Out:<{1}>", value, trgValue);
return trgValue;
}
Now, if you skim through this code, it's not an utter disaster. As the Obsolete
annotation points out, you could replace the entire body with a built-in method, but they don't take any obviously terrible approaches to reinvent a wheel which already exists. They didn't blunder onto the absolute worst way to format/parse GUIDs, they just… wrote pointless code.
So, what's the WTF?
Well, I'm no German expert, but if you read the comments it's pretty clear that this code exists because SQL Server and Oracle use two different formats for GUIDs/UUIDs. Now, sure, Guid.Parse
and Guid.ToString
could handle both formats just fine, so this code remains unnecessary- but doubly so.
Because this product doesn't talk to an Oracle database. It never has. It never will. Much of the business logic lives in stored procedures. It absolutely never will talk to Oracle. So the original developer solved a problem they'll never have, by re-implementing built-in methods.
Compared to that, just marking them as Obsolete
and not fixing the bad code is a very minor sin. It's good enough, it just shouldn't exist is all.
Метки: CodeSOD |
Representative Line: hnjbbbbynbhhhhhhhhhhhh |
Five years ago, someone at Adam’s company made a commit. Like all good commits, it touched 200 individual files and 3,500 lines of code, and the commit message was simply: “Fixed”.
One of those 200 files was a .h
header file, declaring a long pile of function prototypes. One of them is this one. It has no implementation, and isn’t used anywhere:
void eXosip_suhnjbbbbynbhhhhhhhhhhhhbscribe_free (eXosip_subscribe_t * js);
One of the most famous survival horror games ever implemented, which combines lo-fi, character-oriented “graphics” with escape room puzzles is VIM. Endlessly customizable, it offers a huge amount of replay value, if you ever successfully exit it.
Adam’s suspicion is that the developer was unwittingly in VIM’s edit mode, and accidentally mangled this. Either that, or a cat decided to do a softshoe routine on his keyboard while he wasn’t looking.
Regardless, that commit never got reviewed, as you can imagine, and thus this little “treat” has been sitting in the code base for five years. The real question is: what else got mashed into the code that no one knows about?
Метки: Representative Line |
The Renegade Datacenter |
The bank Edward worked for had a datacenter problem. Said datacenter resided in the basement of their headquarters. Over a twenty-year period, it had been expanded twice, and now covered the entire floor. There was simply no place left to go. The datacenter contained everything from state-of-the-art racks to $10 Ethernet hubs that no one had touched in a decade, and many of these mission-critical components were situated directly upon the floor. Every other week or so, some technician would trip on a cable and knock out a server or switch.
While the bank had plans for a new headquarters and datacenter, construction had been postponed twice. The most optimistic estimate was that work would not begin for another 2 years. In the meantime, Edward and his IT team were tasked with figuring out how to squeeze more space out of the existing datacenter.
The team's first move was to set up virtual Windows servers, so they could throw away some of the older computers cluttering the floor. Spreadsheets were compiled to track server names, business group owners, locations (ex. "DC room #2, third gray desktop on the floor from the left"), IPs, applications, and when virtualization would occur. Unsurprisingly, there were hiccups in tracking down owners, figuring out what applications ran on which machines, and whether those applications would work properly in a virtual environment. Overall, though, the virtualization push was successful, allowing much precious basement real estate to be reclaimed.
But, wouldn't you know it? There was a problem with "DC room #2, third gray desktop on the floor from the left." It'd been successfully virtualized, and the application running in the virtual environment had not yet exhibited any problems. Just in case, the old server remained temporarily in the datacenter, with a "DECOMMISSIONED" sign taped to it. Everything seemed fine—until Edward received an IP collision alert on the beginning of the next month. Someone had fired up the old server, which began butting heads with its virtualized clone.
It was easy enough to shut down "DC room #2, third gray desktop on the floor from the left." Less easy was figuring out who had turned it on. Over 100 people had unrestricted access to the datacenter. Server logs were of no help, either; whoever had logged in had used one of the general maintenance accounts that half the IT department knew the credentials for. Edward emailed everyone remotely related to that server, asking who might have turned it back on. To his complete lack of surprise, he received no reply.
The beginning of the next month, there was another IP collision alert, along with several angry phone calls from users. Edward went down to the datacenter and found the server back on again. Someone had carefully un-taped the "DECOMMISSIONED" sign and moved it a few inches over to access the server's power switch.
Edward appealed to his manager: there was no trouble with the virtual environment. Surely it was time to get rid of "DC room #2, third gray desktop on the floor from the left?" Alas, no. His manager explained that, due to how many departments relied on this server for reporting, they couldn't afford to get rid of it if there were ever an issue with the virtual environment. But he did allow Edward to do the next best thing: unplug the server and hide it behind a cardboard box full of power cables.
The beginning of the next month, there was no IP collision. However, there was a frantic call from one of the 24/7 datacenter monitoring personnel, claiming that one of the servers had been stolen. When Edward and his manager inquired further, the details came out. This person had mapped a drive for one of his monthly reports to "DC room #2, third gray desktop on the floor from the left." Edward's cloning script had somehow missed this share. Whenever this person had to access his report, he would receive an error. Since datacenter hardware issues were common, he would think something had happened to the server, and would simply fire it back up. Once Edward had hidden the server, he'd reached the only logical conclusion.
"You saw the 'DECOMMISSIONED' sign," Edward pointed out. "Didn't that tell you anything?"
"Half the servers in the datacenter have had stuff like that on them for years!" he dismissed.
"What about all the emails we've sent out?" Edward asked.
"I don't read emails with too many recipients," he replied. "If anything really important happens, I'll get a call about it sooner or later anyway."
"Why didn't you ever file a support ticket?"
He wrinkled his nose. "You never know who'll pick it up! Some of the people who work here are idiots."
Метки: Feature Articles |
Error'd: Luck of the Error |
Drew W. writes, "I'm looking forward to the next month's episode, Localized/Consumer-Facing Title where the kids all learn about the pitfalls experienced when attempting to display localized strings. And it's the St. Patrick's Day episode."
"Photoshop doesn't always have problems with the display driver, but when it does," Morgan wrote.
"Modern math is so hard. I just don't know the answer!" writes Allan.
Vivia wrote, "Of course, ticket kiosk systems are separate from the trains' systems! Right?"
"I, for one, enjoy an occasional fish taco, but I don't think this will go over very well for Taco Bell," Chris N. wrote.
Iddo S. wrote, "I guess even Google acknowledges the popularity and necessity of WhatsApp, especially if they’re trying to show us what the world would look like without it."
Метки: Error'd |
Simple Class |
Sometimes, you just know one of your coworkers isn't pulling his or her weight on the team. Sometimes it's the slacker co-worker, the one you always see browsing Facebook or getting coffee and never on pull requests or in architecture meetings. Sometimes it's the absent one, the one always seeming to be on sick leave or "working from home." And sometimes it's the guy who you wish would slack off just so you could stop reviewing his inane, poorly-executed code.
Danny was one of the latter types. He worked at a Fortune 50 company on a team of four; he had, in theory, 14 years of professional experience working for many huge companies as a software architect. Not only were his communication skills non-existent, his code was never tested and rarely worked. And yet somehow, he kept missing lower and lower bars of competence as our submitter watched in horror.
One of his tasks was to create a simple class that could hold the config for the new functionality. "That's simple," he said, before spending two whole days working on it. Our submitter came in the third day, grabbed some coffee, and began reviewing the PR Danny had submitted the evening prior. In order to hold a config like this:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Config",
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description":"xxx"
},
"keyId": {
"type": "string",
"description": "xxx"
}
}
He created a class like so:
public class Config {
@SerializedName("$schema")
@Expose
private String $schema;
@SerializedName("title")
@Expose
private String title;
@SerializedName("type")
@Expose
private String type;
@SerializedName("properties")
@Expose
private Properties properties;
// ... getters & setters
}
public class Properties {
@SerializedName("enabled")
@Expose
private Enabled enabled;
@SerializedName("keyId")
@Expose
private KeyId keyId;
// ... getters & setters
}
public class KeyId {
@SerializedName("type")
@Expose
private String type;
@SerializedName("description")
@Expose
private String description;
// ... getters & setters
}
public class Enabled {
@SerializedName("type")
@Expose
private String type;
@SerializedName("description")
@Expose
private String description;
// ... getters & setters
}
That's right: every single field in the JSON, including the schema definition, had a field in the class, with a getter and setter to match. It's as though he'd never seen JSON before in all his 14 years in the industry. Furthermore, even if this were the correct way to approach the topic, it should take about five minutes in IntelliJ to do it correctly, and yet Danny had spent two days doing ... what? Not testing, that's for sure.
He was fired the next week.
Метки: Feature Articles |
CodeSOD: Dating for Three Months |
Nathaniel P writes: “We have long agreed never to ask ‘why’ for any code in this project. We can only speculate, and therein lies madness.”
There’s a lot to speculate about in this block. Like so much bad code, it touches upon that favorite source of bad code: date handling. And, at its core, this code is actually fine, but it still puzzles us:
$theFirst = date("Y-m", strtotime("-1 months"));
list($theFirstYear, $theFirstMonth) = explode('-', $theFirst);
$theSecond = date("Y-m", strtotime("-2 months"));
list($theSecondYear, $theSecondMonth) = explode('-', $theSecond);
$theThird = date("Y-m", strtotime("-3 months"));
list($theThirdYear, $theThirdMonth) = explode('-', $theThird);
//this is a fix for weird feb issue
$date = new DateTime();
$theFirstMonth = $date->format('m')-1;
if(strlen($theFirstMonth) < 2){
$theFirstMonth = '0'.$theFirstMonth;
}
$theSecondMonth = $date->format('m')-2;
if(strlen($theSecondMonth) < 2){
$theSecondMonth = '0'.$theSecondMonth;
}
$theThirdMonth = $date->format('m')-3;
if(strlen($theThirdMonth) < 2){
$theThirdMonth = '0'.$theThirdMonth;
}
$dateArray = array($theThirdYear.'-'.$theThirdMonth,$theSecondYear.'-'.$theSecondMonth,$theFirstYear.'-'.$theFirstMonth);
The goal of this code is to output “Y-m”- year and month- for the past three months. And the core approach- pull the date
with a format string for strtotime
with dates like -1 months
does the job. And that could be all of the code, but there was a weird feb issue
. No one knows what this weird issue was, or what the fix was trying to do to fix it. The “fix” is to prepend a “0” to the front of the dates, but the logic is a mystery. Just a few lines before, they showed they knew how to do date arithmetic, but now they get the month and simply subtract a number, meaning in January and February, they’ll prepend zeros to the wrong month.
Hey, I think I figured out what the “weird feb issue” was.
This code doesn’t fix the issue, and quite to the contrary, creates the bug. This bug doesn’t need to exist, either. If we check the docs on PHP’s date format strings, we see that the “m” is: “Numeric representation of a month, with leading zeros”.
Nathaniel didn’t ask “why”, and leaves that to us. He just fixed it.
Метки: CodeSOD |
CodeSOD: Request for Quote |
Once upon a time, a client needed Initech to develop a custom tool for them. It would be mildly complex to develop, in terms of developer hours, and in respect of that, the client offered a budget of zero dollars. “But,” they added, “we are looking to do a major upgrade later this year.”
The hint came through loud and clear. Since the development teams charged billable hours, the boss didn’t want to run the project through the usual channels. Besides, there were perfectly good internal development resources, who maintained the internal SharePoint site.
So Terry and Terri, the two SharePoint developers, got their first client-facing project. If you haven’t done any SharePoint development, 90% of it isn’t development at all: it’s about dragging and dropping widgets onto the screen and configuring them correctly. The remaining 10% is a hellscape of absolutely forsaken APIs, infernal coding conventions, and gibbering madness.
Terry and Terri did the boss’s off the books project, the results were handed off to the client, and nobody thought about it until two years later, when, after a rash of bugs and crashes cropped up, someone asked, “Hey, was this ever code reviewed?”
Which is how this code ended up on Erica’s computer.
The goal of this block of code is to sanitize a string. The string contains double quotes, which need to be removed. There also might be a space, in which case only the content before the space should be taken.
Terry and Terri produced this:
str = (str.Remove(str.LastIndexOf("\"")) + " \"");
if (str.Contains(" ") )
{
var s = str.Remove(str.IndexOf(" ")).Trim("\"");
}
This code removes the last "
, then adds a space… followed by a double quote. Once we definitely have a space in the string, we check to see if the string contains a space, and if it does, we remove the first space in the string, and then trim off the double quotes.
When Erica asked the Terri(y)s how this happened, they explained a little bit about their development process: when they encountered an error, they added more code until the error went away.
Erica raised this issue up with her boss. The boss, to Erica’s eternal joy, said, “Great, this sounds like a training opportunity. Teach Terry and Terri how to improve this code, and they’ll be better developers.”
Now, Erica wasn’t exactly skilled in training, but neither Terri nor Terry were skilled in development, so this was doomed to failure from the start. Erica spent three days trying to explain to the Terry(i)s that this code was wrong, and needed to be changed. Terry replied, “I’ve looked at it, and I think this code is ingenious. We shouldn’t change it, because that might break something.”
In the end, Erica couldn’t provide the training that the Terr(i)ys needed, but she certainly had some more suggestions for some other changes- staffing changes.
Метки: CodeSOD |
Representative Line: Three's a Constant |
Bryant E was debugging a hardware system with some embedded C code. Due to form factor considerations, there weren't a lot of physical inputs to this device, which meant what controls it had needed to be reused cleverly.
For example, there was a pushbutton. Just pushing it performed one function. Press-and-hold did another. Double click and triple click did yet more functions.
Or at least, they were supposed to. Skimming through the code, it looked correct. Bryant saw things like if (clickCount == THREE_CLICKS)
. Could it be any more clear?
While trying to understand why the triple click wasn't working, that constant nagged at him. It's one thing to remove magic numbers, it's something completely different to define a constant explicitly named THREE
_anything. When Bryant repeatedly tapped the button as quickly as he could, and saw the triple click event fire after far more than three clicks, he started to get a sense of what might be going on.
This, as it turned out, was the constant definition:
// a click is a press and release event
#define THREE_CLICKS 6
Perhaps, at one point, this constant was meant to track something different than it was actually used for. If there are six press and release events, that is three clicks, so maybe the original developer meant it to be a count of events. That would almost make sense except that:
#define TWO_CLICKS 2
The moral of the story is never trust a constant that contains a number in its name. It's often lying to you.
Метки: Representative Line |
Error'd: If Everything's on Sale, Nothing is |
"When it comes to sending discount notifications, eBay knows that it often times just the thought that counts," Clayton D. wrote.
Stephane writes, "Hopefully, the rest of their tax math will not be as wrong."
"What is this? 'Free' survey with the purchase of an nVidia Shield?" wrote Joe.
Jamie S. writes, "I've seen my fair share of errors from Altium Designer, but this is the first one I've seen that seems to fail at basic math."
"With the level of personalization in this email, I shudder to think of what horrors will be revealed after clicking on MYAfterPay," wrote Hannes.
Karl V. writes, "Are you curious to find out what secrets BMW's configurator is trying to hide from you? For about 300 USD, you can."
https://thedailywtf.com/articles/if-everything-s-on-sale-nothing-is
Метки: Error'd |
CodeSOD: Unique Subscription |
Today’s anonymous submission starts with “I inherited an old wordpress site that uses an external corporate authentication server for logins.”
As one might expect, the result is a hodgepodge of reinvented wheels and anti-patterns. Deep in the authentication code, there’s a method to add an email subscription. You might ask yourself, “What does adding an email subscription have to do with authentication?” and you’ve already put more thought into the design than the original developer.
In any case, you want to assign each subscription a unique identifier. You’re not planning to use this as a key, you’ve just decided it’d be nice to have, I suppose. So what is the best way to generate a unique ID? Well, as a hint, the variable that holds the unique ID is called $guid
, so obviously, we’re going to… do this:
function add_subscription_user($user_id, $user_name, $user_email, $group) {
$guid = rand();
$length = 6;
global $wpdb;
$db_prefix = $wpdb->prefix;
//$rand1 = substr(str_shuffle("abcdefghijklmnopqrstuvwxyz"), 0, $length);
$rand2 = substr(str_shuffle("abcdefghijklmnopqrstuvwxyz"), 0, $length);
$rand3 = substr(str_shuffle("abcdefghijklmnopqrstuvwxyz"), 0, $length);
$rand4 = substr(str_shuffle("abcdefghijklmnopqrstuvwxyz"), 0, $length);
$rand5 = substr(str_shuffle("abcdefghijklmnopqrstuvwxyz"), 0, $length);
$rand6 = substr(str_shuffle("abcdefghijklmnopqrstuvwxyz"), 0, $length);
$guid = $user_id."-".$rand2."-".$rand3."-".$rand4."-".$rand5."-".$rand6;
$CurrentDate = date('Y-m-d G:i:s');
$sql = $wpdb->prepare("INSERT INTO `".$db_prefix."es_emaillist`
(`es_email_name`,`es_email_mail`, `es_email_status`, `es_email_created`, `es_email_viewcount`, `es_email_group`, `es_email_guid`)
VALUES(%s, %s, %s, %s, %d, %s, %s)", array($user_name, $user_email,
'Confirmed', $CurrentDate, 0, $group, $guid));
$wpdb->query($sql);
//return $wpdb->insert_id;
}
That’s not a GUID or a UUID. That’s random nonsense. Take every letter in the alphabet, shuffle it, and then grab the first six characters. Repeat that five times. Slap the user’s ID on the front, and call that a GUID, or es_email_guid
, if you’re speaking database.
Our submitter adds:
Sadly, this is one of the better snippets of code I’ve found. The plan now is to recommend we nuke this from orbit and start over with either a fresh Wordpress installation or a totally different platform.
Метки: CodeSOD |
Humble Origins |
Way back in 2002, I was a fresh-faced 16 year-old kid with a driver's license and a car to pay for. That meant seeking employment anywhere I could get it. My first job at a sub sandwich shop lasted long enough to learn that I didn't want to deal with disgusting food/dishes every day. My next job at a "lackluster" video store taught me that I couldn't deal with customers, especially when they were upset about late fees. I decided to set my sights on something I actually had a knack for - working on computers.
I managed to land a low-paying IT internship with the internal application development team at a plastics manufacturing company. They made the software the company used to do everything from track materials and orders to real-time monitoring of the molding machines. They were looking for someone young to do all the grunt work around the office and I was just the guy.
The development team was full of a bunch of quirky, overgrown children with incredible potty mouths. When they weren't busy coding, they were slinging around some of the most vulgar insults I've heard. Usually that was followed by brutal physical assaults like nipple twisters or forceful punches in the ass. Then there were the rubber band fights that could break out at any time. I once came into the office and was immediately nailed in the eye by a double-knotted 4-inch long rubber band - by my boss. Instead of apologizing like a normal human, he and everyone laughed their asses off. It was all part of the playful, laid back environment they created and I loved it.
There was actual work for me to do in between the hijinks and I learned many essential computer skills. A lot of what I did amounted to help desk work. I would assist users with their random computer problems that the others lacked the desire, and tact, to do. I learned about hardware by ripping apart towers and putting new components in as well as formatting hard drives and re-imaging them. I even learned how to start up a computer by touching a screwdriver on 2 pins on the mother board. That came in handy when I built my own Frankenstein home computer and didn't have a proper power switch.
I soon got a reputation around the office as a PC problem-solver, so people started bringing in their virus-riddled home PCs to have me clean them up - on the clock. I would neglect to mention all the pornography I found on their computers, which was the most likely virus source. As an extra service, I was instructed by my supervisor to install some of the cracked PC games we had stored on an isolated server. For quality-assurance purposes, I was supposed play the first level/mission of the games to "make sure they worked".
Eventually, the guys wanted to mold me into a computer programmer like they were. In the early days of .NET development, they threw a Beginning C# book at me and told me to read it and learn. I spent about six months going through the book and coding the sample projects, including a crappy command-based card game that I played when I was bored. Once I got towards the end of my book, I was assigned real development projects to do. At that point I realized that reading a book and regurgitating its code didn't mean I knew how to program yet.
With the help of the more veteran developers, and a whole lot of banging my head on the wall, the major concepts of object-oriented programming actually sunk in. I started enjoying it and produced small useful applications used by the business. I'm sure my code was riddled with WTFs, but it worked. Around that time the US was in the midst of a recession that hit the manufacturing sector particularly hard. The 800 employee plastics company I worked at started to struggle, and was about to have its workforce cut in half.
An initial wave of layoffs happened around the company and our development team wasn't able to escape it. The middle tier of the group was gutted and our team of 11 was cut to 7. As people were let go, HR would send emails saying "Please be advised that the following people are no longer with the company." Eventually the emails stopped, not because they were done firing people, but because it was too damn depressing to get all those emails.
A second wave of cutbacks slashed many more jobs, but I still managed to weather the storm. After all, dumping a lowly-paid intern wasn't going to save the company. Our group was now down to a four person bare-bones staff - The manager/senior developer, another good developer, a lady who did design/documentation, and me. We didn't have nearly the staff to keep all of the programs running properly, let alone touch the mountain of fixes/enhancements they needed.
As we struggled along as a skeleton crew, all the life had been sucked out of the office. We mostly kept to ourselves, didn't joke around much, and the rubber bands were no longer flying. Everyone that remained in the company feared that their days were numbered. It was a horrible situation that I've fortunately never had to endure since.
One day in the late spring of 2003, I came in and sat down at my computer like I did every day. My boss told me not to bother logging in and to follow him to a conference room to meet with HR. I immediately knew what was happening and I was crushed. This was the job I wanted, and the only job I was good at. It was about to be taken away from me due to circumstances beyond my control. On my way out, he told me that he fought to keep me around and it obviously wasn't due to money, it was a head count situation. He also encouraged me to stay in touch and that when things improved, they would be happy to have me back.
I wasn't overly optimistic, but I kept what he said in the back of my mind. In the meantime, I needed a new job. IT jobs that would hire a high school kid were few and far between, so I spent my whole summer delivering pizzas. That job was full of its own class of WTFs, and to this day is still the most stressful job I've had. On a good tipping day though, I actually made more doing that than I did computer programming.
Around August, I heard back from my former supervisor. The layoffs had ended and the company was starting to turn itself around. They wanted to bring me back and I would even be making $2 an hour more than I was before. For someone about to enter his junior year of high school, that seemed like fat cash. So in the end, being laid off literally paid off in the end.
I spent two more years working there, including switching to full-time after I graduated high school. The team stayed small, only bringing in two more new developers after the cutbacks. It was never close to the fun, highly unprofessional environment we had when I started. While I never got laid off again, I never got a raise again. I eventually left the company, and my programming days, behind for a better paying testing job at a software company. But I never forgot my humble origins that started what is now my 18 year career in IT.
Метки: Feature Articles |
The 4000 Characters |
Containers and cloud deployments are made for each other. Using say, a Docker configuration file, you can pass that off to a cloud host, and get an environment whipped up in a declarative fashion without having to worry about all the ugly details of exactly how that happens. With tools like Docker’s Compose, you can spin up many components all in one big, declarative YAML block.
Or can you? Ricardo P was running into problems with deploying to the cloud. Specifically, when deploying to Azure, he got this error message: Linux Version is too long. It cannot be more than 4000 characters.
That’s such an odd message, and when Ricardo started digging around, it wasn’t really easy to see what it was referring to when it was talking about “Linux Version”.
But Ricardo wasn’t the only one with this problem, but the root cause is… surprising.
The Azure configuration stores a Base–64 encoded version of the docker-compose.yml
file in a field called linuxFxVersion
. An “artifact” of their storage system caps the size of that data to 4,000 bytes. Why is that field name holding that data? It’s unclear, and certainly leads to unclear error messages.
But there are other odd quirks. The Base–64 encoded version isn’t content aware- it encodes all of the text. Did you include comments in your compose file? Well, be ready, to count those against your filesize limit. Plus the Base–64 encoding makes the content even larger- folks would cut down their filesizes only to discover that they still blew out the upper limit.
This is clearly a situation where an existing field was adapted to a new purpose, but isn’t actually fit for that purpose. An ugly hack that got out into the wild, and probably can’t be reined back in, because every change breaks someone’s workflow. So now it lives on: breaking deployments, unable to be deprecated, the fix tied up in internal teams that have higher priorities, and making sure that one single forum thread gets lots and lots of hits.
Метки: Feature Articles |
The Document Cursor |
IniCAD was one of the world's largest purveyors of CAD software. In the earlier days of this industry, they used more "industrial" sales models: rarely did you buy a license from IniCAD directly, but instead bought through a dealer who was technically a third party, but essentially was IniCAD's representative.
If you build designs in CAD, at some point you need to turn these into drawings. In the industry, the drawings are 2D, static images that represent a canonical representation of your picture of the object/building/machine being designed. They're one of the primary tools for making sure that all of the various teams working on a large scale construction or fabrication project can all communicate accurately and precisely. Nothing is worse than having a building's electrician working from one drawing as they plan their wiring, and having the framers working from another drawing, and putting their walls in different places than the engineer expects.
So IniCAD tried to solve this problem, by implementing their own document management solution, IniDOC. They implemented it in house, called it "good enough", and then shoved it off to the dealers to sell.
Judi worked for one of those dealers as a TSSR, and was expected to demo this product. There was just one problem: it didn't work.
IniDOC was a pile of VB code (IniCAD's flagship product was scriptable in VBA) that somehow managed to contain its own little copy of DLL Hell in its own dependencies. The first time Judi tried to install it, it borked her computer so badly she needed to do a fresh install of Windows. The second time, she got it to install, but couldn't store documents in it. With each glitch or problem, she went up the chain to complain to IniCAD, and each time IniCAD escalated her issue.
By the time Judi was ready to start doing demos for clients, she had a team of IniCAD developers on call for the entire demo window. The code she was running for her demo was a hodgepodge of non-version-controlled quick hacks and duct-tape. Certainly, it wasn't the "gold master" or "release candidate" or what the customer would actually get.
The demos went terribly, and Judi spent about six months touring Europe looking like an idiot shilling a dumpster-fire of a product. It was soul crushing, and at the end of that period, the sales team had one sale for the entire region to her credit. A few months after that, her one sale demanded a refund: a glitch caused the system to crash and destroy all the documents they were storing in their document management system.
IniCAD realized that they had made a terrible mistake, trying to branch away from their specialty and into a new product niche. They quietly withdrew their IniDOC product, and then did what all companies with a decent cash reserve do: they bought a third party company that had a product better than theirs.
IniCAD purchased "Docutrode", the "best in class" document management tool for architects, engineers, and fabricators. They relaunched it as IniDOC V2, and Judi was once again doing a round of demos.
It went better, but not great. The new document management product was an attempt to clone Visual Source Safe, and the install process was fraught with unnecessary complexity. While IniDOC V2 actually made sales, most of the profit was eaten up by the high volume of support calls, and those calls were almost entirely configuration and install issues.
IniCAD had a choice: they could improve their documentation, streamline their support path, redesign the install process to cut down on errors, and maybe do some business analysis to get a better sense of what the "right most of the time" defaults should be, or they could do a ground up rewrite using the team responsible for IniDOC V1.
They did a ground up rewrite. Judi, working at the dealer, got to see lots and lots of marketing copy about it, lots of promises, lots of "Web 2.0!" branding. Release dates came and went, without a product. For awhile, Judi wondered if IniDOC V3 had died, but no software product dies if you're willing to throw good money after bad, so it eventually released.
What Judi received was a software package that ran an IIS web server and a SQL Server database. It could support, in theory, hundreds of simultaneous users, but in practice more than five meant requests started timing out.
Judi didn't write the software and didn't have access to most of the source code, but she wanted to understand why it was so slow. So she started poking around in the database to see the structure.
The core of the database were three tables: one which actually stored the documents and their data (documents
), one which lists off all the possible attribute metadata for any document (attributes
), and one which links documents, attributes, and their values (document_attribute_values
). Because metadata could be anything- numeric, text, even a thumbnail image- the only workable datatype was to store everything in BLOB columns.
On the UI side, as the user navigated the application, they saw logical folders, and the files in that folder were listed with metadata columns. The user could set global preferences for which metadata columns they wanted to see.
Judi poked around in the database, and found that almost all the business logic was implemented in stored procedures. And that's when she pulled up the procedure which handled that browsing.
The logic went basically like this:
Create a temporary table for the folder you're about to display. It has one column, document_id
, and has one row for every document in the folder. Then, it opens a cursor on the attributes
table. For every possible metadata field, it alter
s the query table to add column
for the metadata field. Then, inside of that cursor for loop, the code opens a second cursor looking at the document_attribute_values
table. There, it pulls the values (if there are any), and populates them into the column on the temporary table.
Once every document in the folder had gotten that treatment, the entire table was sent to the client side, where the users' display settings got applied and the metadata columns they didn't want to see were excluded.
Metadata fields were billed as being customizable and flexible. The application encouraged users to create metadata fields with abandon. A single deployment might have hundreds of possible metadata fields, but a single document might have, at most, thirty. In practice, most users only ever needed to see 5-8 columns.
Each time an engineer changed folders, a cursor-driven pivot table was created with hundreds of mostly empty columns, of which only a handful actually mattered. All that data went back to the client side, where the filtering happened.
IniDOC V3 sold slightly better than its previous versions, more due to increased demand for a document management solution than because of any merits to the product. Judi left IniCAD's dealer before IniDOC V4 had a chance to come out, but based on conversations with former co-workers, Judi believes it still more or less works this way.
Which should be a lesson to all software developers: all performance problems can be solved by simply not changing any code and waiting for hardware improvements to make it run faster.
Метки: Feature Articles |
Error'd: Just Following Out of Order Orders |
"Instructables alphabetic sorting by each country's name in its own language (i.e. Spain == Espana) is a great idea, but it kind of makes for a hard to navigate list," writes Peter L.
"It's very thoughtful of my company to *try* to autofill my network username," Joe writes.
Noah wrote, "Some newsbot is really worried about bumblebees...not to say it isn't entirely unfounded concern though."
"While alt-tabbing my apps, I found what one might think of as an attempt at a hidden form,"
"I imagine that somewhere behind the scenes at H&R Block, some design said that x must be set to a randomvalue, and, well, here we are," writes Darrel H.
Brett wrote, "Seems that AxleHire does their deliveries using their fleet of DeLoreans."
https://thedailywtf.com/articles/just-following-out-of-order-orders
Метки: Error'd |
CodeSOD: A Blacklisted Senior |
Damien has the “pleasure” of supporting JavaScript code which runs, not in a browser or Node, but inside of a proprietary runtime specialized in handling high-performance collection datatypes. You can write extremely fast code, so long as you’re using the collection types correctly. This is good, because a lot of those JavaScript blocks have to be executed for every single request. Milliseconds of execution time add up faster than you think.
One of Damien’s senior peers needed to add some code that would filter fields out of a response. Data fetched from the database would be compared against a blacklist of fields to exclude- those fields should be removed from the collection.
This is a relatively simple task, and one that has, in other variations, already been implemented in the codebase. Some of those proprietary enhancements are faster implementations of types like Set
, so the basic approach would be:
removeAt
The Set
is fast. By iterating in reverse order, you can change the length of the list without disrupting the for loop. removeAt
is a direct access, which is also fast. Since performance is a concern, this is a pretty good solution.
The senior developer came up with this:
function getFilteredItems() {
var items = getItems();
var blacklist = config.getBlacklist();
Object.keys(blacklist).forEach(function (blacklistElement) {
if (blacklist[blacklistElement]) {
items.toArray().forEach(function (item) {
if (item.tag === blacklistElement) {
items.remove(item);
}
});
}
});
return items;
}
getBlacklist
was implemented by this developer, and thus they could have done anything they wanted. What they chose to do was return an object where keys were the field names, and values were booleans. The blacklist could contain hundreds of items.
So we iterate across each key, keeping in mind that some of these keys may potentially be false
, and thus should be skipped anyway. Then, for each key in the blacklist, we iterate across each item in our items
array, turning something that should have been a linear operation into a quadratic operation. The items
array could have hundreds or hundreds of thousands of items, depending on the operation.
But we don’t just iterate across the items
array. We iterate across items.toArray()
, a copy of the items
array. So that bloats the runtime and the memory footprint, especially since we need to do that for each blacklist item. But then that’s necessary, since we use items.remove
to remove the item in place- something that we couldn’t do from inside of a forEach
because changing the size of a collection while iterating across the collection can be tricky (if you don’t use the solution we mentioned above).
But items.remove
is what makes this even worse. Because items.remove
does a linear search through the array to find the target reference. So, we iterate across the blacklist. For each item in the blacklist, we iterate across the entire list of items, then for each item we need to remove, we iterate across the items again. And all this assumes that the toArray
cast is cleverly implemented using a memcopy
approach and doesn’t have to actually iterate the array, which is probably true.
Even if performance doesn’t matter, that’s a pretty WTF way to implement that, even if you’re a “senior developer”.
Метки: CodeSOD |
CodeSOD: Producing Self Joins |
Wesley considers himself an “intermediate SQL-er”. The previous “database expert”, though, has moved on to greener pastures, and now Wesley is “the database person”. This means that they need to pick through a bunch of stored procedures and triggers and try and understand the undocumented, unversion-controlled database code.
The upshot, is that Wesley is starting to feel like his intermediate skills might be more “expert” than the previous expert.
For example, Wesley found this query. The goal of this query is, for a single point-of-sale terminal, in a date range, how many of each product did they sell, as a raw count. It should be able to say, there were 5 apples, 10 oranges, etc.
Now, you or I or Wesley are probably already reaching for the GROUP BY
and maybe a CASE
. It’s the right choice, obviously. But the “expert” has a different solution:
SELECT
a.Name,
b.RegisterId,
COUNT(c.ProduceId) AS Apples,
COUNT(d.ProduceId) AS Oranges,
-- ...
COUNT(ac.ProduceId) AS Zucchini,
COUNT(ad.ProduceId) AS Other,
COUNT(ae.ProduceId) AS GrandTotal
FROM Cashier a
INNER JOIN Sale b ON a.RegisterId = b.RegisterId
AND (CONVERT(DATE,b.SaleDate) BETWEEN @startDate AND @endDate
LEFT OUTER JOIN ProduceType c ON b.ProduceId = c.ProduceId AND c.ProduceName = 'Apples'
LEFT OUTER JOIN ProduceType d ON b.ProduceId = d.ProduceId AND d.ProduceName = 'Oranges'
-- ...
LEFT OUTER JOIN ProduceType ac ON b.ProduceId = x.ProduceId AND x.ProduceName = 'Zucchini'
LEFT OUTER JOIN ProduceType ad ON b.ProduceId = y.ProduceId AND y.ProduceName = 'Other'
LEFT OUTER JOIN ProduceType ae ON b.ProduceId = z.ProduceId AND z.ProduceName IS NOT NULL
GROUP BY a.Name, b.RegisterId
ORDER BY a.Name, b.RegisterId
This… is a massive self-join. Each join trips back to ProduceType
, but filters it for one specific type of produce. For every kind of produce they track. Well, presumably every kind- at some point the list of produce in the ProduceType
table might change, in which case this query breaks.
This isn’t the simplest query to write in SQL, given that SQL really doesn’t like it when you dynamically project an arbitrary number of columns, so pretty much any solution is going to be at least a little bit ugly. Still, with a smart use of CASE
statements or possibly sub-queries, you can certainly get there. Given all the possible ways to write a query like this, it’s impressive to see that the original developer hit upon what may be the worst possible one.
Wesley adds:
I can’t stop asking myself: how does someone end up writing a join into a N-way self-join instead of counting cases over a single join? HOW?! Were they challenged to write a loop-switch sequence in SQL? Was CASE broken somehow?
Since there’s no revision control for these scripts, I may never know.
Метки: CodeSOD |
CodeSOD: Break your Labels |
Nedra writes “I discovered this code while cleaning up and refactoring some label printing methods in a home-grown ERP that I maintain.”
The code in question “works most of the time”, which means it’s crossed a line of code quality. Speaking of crossing lines, this particular block of code needs to take information about how a product is formulated and print it on a label. These sorts of ERP functions are “mission critical”, in that correct and accurate formulations- whether the ingredients list on a foodstuff or the ingredients in a can of paint, or an industrial solvent- are required for regulatory compliance.
Labels are also physical objects, and have a defined physical size. This means that you can only fit so much information on them, and you’ll need to make sure the layout of what you’re printing is readable on the label.
Nedra’s co-worker had… a solution for this.
Dim result As String = ""
Dim f As String() = Formula.Split(New String() {Environment.NewLine}, StringSplitOptions.None)
If f.Count > 8 Then
For Each line In f
If line.Length > 80 Then
Dim break As Integer = line.Substring(0, 80).LastIndexOf(" ") + 1
line = line.Insert(break, Environment.NewLine)
If line.Length > 161 Then
break = line.Substring(0, 161).LastIndexOf(" ") + 1
line = line.Insert(break, Environment.NewLine)
If line.Length > 242 Then
break = line.Substring(0, 242).LastIndexOf(" ") + 1
line = line.Insert(break, Environment.NewLine)
If line.Length > 323 Then
break = line.Substring(0, 343).LastIndexOf(" ") + 1
line = line.Insert(break, Environment.NewLine)
End If
End If
End If
End If
If String.IsNullOrEmpty(result) Then
result = line
Else
result += Environment.NewLine + line
End If
Next
Else
For Each line In f
If line.Length > 65 Then
Dim break As Integer = line.Substring(0, 65).LastIndexOf(" ") + 1
line = line.Insert(break, Environment.NewLine)
If line.Length > 131 Then
break = line.Substring(0, 131).LastIndexOf(" ") + 1
line = line.Insert(break, Environment.NewLine)
If line.Length > 197 Then
break = line.Substring(0, 197).LastIndexOf(" ") + 1
line = line.Insert(break, Environment.NewLine)
If line.Length > 263 Then
break = line.Substring(0, 263).LastIndexOf(" ") + 1
line = line.Insert(break, Environment.NewLine)
End If
End If
End If
End If
If String.IsNullOrEmpty(result) Then
result = line
Else
result += Environment.NewLine + line
End If
Next
End If
After writing a block like this, you definitely need to take a break. At its core, this code injects linebreaks at specific positions in a string depending on how many total original lines there were. For a long moment, I was trying to figure out the off-by-one errors that it looked like it had- 80 characters, then 161?- but that’s specifically because it’s inserting characters.
It’s not efficient, it’s not easy to read, it’s not easy to skim, but it does work. Mostly. Some formulas have a lot of content, which means this break pattern doesn’t always actually fit the content correctly.
Метки: CodeSOD |