-Поиск по дневнику

Поиск сообщений в rss_thedaily_wtf

 -Подписка по e-mail

 

 -Постоянные читатели

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 06.04.2008
Записей:
Комментариев:
Написано: 0

The Daily WTF





Curious Perversions in Information Technology


Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://thedailywtf.com/.
Данный дневник сформирован из открытого RSS-источника по адресу http://syndication.thedailywtf.com/thedailywtf, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

CodeSOD: Flip to a Blank Page

Четверг, 20 Сентября 2018 г. 13:30 + в цитатник

You have a web application, written in Spring. Some pages live at endpoints where they’re accessible to the world. Other pages require authentication, and yet others require users belong to specific roles. Fortunately for you, Spring has features and mechanisms to handle all of those details, down to making it extremely easy to return the appropriate HTTP error.

Unfortunately for you, one of the developers on your team is a Rockstar™ who is Officially Very Smart and absolutely refuses to use the tools your platform provides. When that Certified Super Genius leaves the organization, you inherit their code.

That’s what happened to Emmer. And that’s how they found this:

List typeList = getTypeList (loginName);
if(CollectionUtils.size(typeList) > 0){
    return viewRepository.findBySubmsnTypeList(typeList, pr);
}
else{
      return viewRepository.findEmptyPage(pr);
}

This doesn’t look too bad, does it? It’s not great- why are roles called “types”, why are we representing them with strings, why are we checking if a user is logged in by checking which roles they have, and not whether or not they’re logged in… and why on Earth would you send the user an empty page if they’re not authenticated?

The question is: how do you generate an empty page? If “just return an empty view object” is what you thought you’d do, you’re obviously not a Rockstar™.

	@Query(value = "from viewTable where 1=0", nativeQuery = false)
	public Page findEmptyPage(Pageable pr);

If you want to return an empty page, you run a query against the database which is guaranteed to return absolutely no results. That guarantees that you’ll send a blank page back, because there’s no data to put on the page. Genius! This way, returning nothing requires a hop across the network and a call to the database, instead of just, y’know, returning nothing (or even better, returning an error page).

Suffice to say, when this master programmer gave their two weeks notice, Emmer and the rest of the team suggested that this programmer should spend their last two weeks on vacation.

[Advertisement] Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.

https://thedailywtf.com/articles/flip-to-a-blank-page


Метки:  

Westward Ho!

Среда, 19 Сентября 2018 г. 13:30 + в цитатник

Buoy in the ocean

Roman K. once helped to maintain a company website that served a large customer base mainly within the United Kingdom. Each customer was itself a business offering a range of services. The website displayed these businesses on a map so that potential customers could find them. This was done by geocoding the business' addresses to get their longitude and latitude coordinates, then creating points on the map at those locations.

Simple enough—except that over time, some of the businesses began creeping west through the Atlantic Ocean, toward the east coast of North America.

Roman had no idea where to start with troubleshooting. It was only happening with a subset of businesses, and only intermittently. He was certain the initial geocoded coordinates were correct. Those longitude and latitude values were stored in a database table of customer data with strict permissions in place. Even if he wanted to change them himself, he couldn't. Whatever the problem was, it was powerful, and oddly selective: it only ever changed longitude values. Latitude values were never touched.

Were they being hacked by their competitors? Were their customers migrating west en masse? Were goblins messing with the database every night when no one was looking?

Roman dug through reams of code and log files, searching desperately for any whiff of "longitude." He questioned his fellow developers. He blamed his fellow developers. It was all for naught, for the problem was no bug or hack. The problem was a "feature" of the database access layer. Roman discovered that the user class had a simple destructor method that saved all the currently loaded data back to the database:

function __destruct() {
     if ($this->_changed) {
          $this->database->update('customer_table', $this->_user_data, array('customer_id' => $this->_user_data['customer_id']));
     }
}

The legwork was handled by a method called update(). And just what did update() do?

public function update($table, $record, $where = '') {
     // snip
     foreach ($record as $field => $value) {
          if (isset($value[0]) && is_numeric($number) && ($value[0] == '+' || $value[0] == '-')) {
               $set[] = "`$field` = `$field` {$value[0]} ".$number;
          }
     }
}

Each time a customer logged into their account via the website and changed their data: if any of their data began with either a plus or minus sign, those values would have some mysterious value (contained in the variable $number) either added to or subtracted from them. Say a customer's business happened to be located west of the prime meridian. Their longitude would therefore be stored as a negative value, like -3. The next time that customer logged in and changed anything, the update() method would subtract $number from -3, relocating the customer to prime oceanic property. Latitude was never affected because latitude coordinates above the equator are positive. These coordinate values were simply stored as-is, with no sign in front of them.

There was no documentation for the database access layer. The developer responsible for it was long gone. As such, Roman never did learn whether there were some legitimate business reason for this "feature" to exist. He added a flag to the update() method so that customers could disable the behavior upon request. Ever since, the affected companies have remained safely anchored upon UK soil.

[Advertisement] Ensure your software is built only once and then deployed consistently across environments, by packaging your applications and components. Learn how today!

https://thedailywtf.com/articles/westward-ho


Метки:  

CodeSOD: Switch On Suppression

Вторник, 18 Сентября 2018 г. 13:30 + в цитатник

Krista noticed our article explaining that switches were replacements for ifs. She sent in a version she found in her codebase, around the same idea:

	@SuppressWarnings("incomplete-switch")
	@Transactional
	public void removeAssetFromPackage(Package pkg, Asset assetToRemove) {
		pkg.getAssets().remove(assetToRemove);
		// Delete from DB and asset store.
		removeAsset(pkg, assetToRemove);

		// If we're removing LIVE asset, also delete AsyncJobs.
		switch (assetToRemove.getType()) {
			case LIVE:
				asyncJobService.removeAsyncJobsForPresentation(pkg);
				break;
		}

		// Flush package cache.
		cacheInvalidationService.invalidatePresenationCache(pkg);
	}

Once again, we use a switch instead of an if. Perhaps this was premature flexibility- there are obviously other states the getType method could return. Maybe, someday in the future, they’ll need to do other things inside that switch. Until then, it’s just the sort of thing the compiler will throw warnings about, since there’s no default case.

Oh, except, of course, they suppressed the warning up top.

A quick search in Krista’s codebase for @SuppressWarnings("incomplete-switch") finds dozens of usages of this pattern.

[Advertisement] Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.

https://thedailywtf.com/articles/switch-on-suppression


Метки:  

CodeSOD: The Secure Cloud API

Понедельник, 17 Сентября 2018 г. 13:30 + в цитатник

Melinda's organization has purchased a cloud-based storage system. Like any such system, it has a lovely API which lets you manage quotas and login tokens. It also had a lovely CLI, which was helpful for administrators to modify the cloud environment. Melinda's team built a PHP front-end that could not only manage files, but also allowed administrators to manage those quotas.

Melinda was managing those quotas, and when she clicked the link to view the quotas, she noticed the URL contained ?token=RO-cmV1c2luZyBrZXlzIGlzIFRSV1RG. When she went to modify the quota, the URL parameter became ?token=RW-cmV1c2luZyBrZXlzIGlzIFRSV1RG. That looked like a security key for their cloud API, transmitted in the open. The RW and RO looked like they had something to do with readwrite and readonly, but that wasn't the security model their storage provider used. When Melinda had another co-worker log in, they saw the same tokens. What was going on?

Melinda took a look at the authorization code.

function authorised($token, $verb) { // check if token can do verb // TO DO - turn this in to a database lookup $rights = array( 'RW-cmV1c2luZyBrZXlzIGlzIFRSV1RG' => array('getquota' => 0, 'setquota' => 1), 'RO-cmV1c2luZyBrZXlzIGlzIFRSV1RG' => array('getquota' => 1, 'setquota' => 0) ); return ((isset($rights[$token]) && isset($rights[$token][$verb]) && ($rights[$token][$verb] == 1))); }

The developer behind this wrote their own security model, instead of using the one their storage provider offered. The tokens here were hard-coded secret keys for the API. Essentially this meant no matter who logged in to manage quotas on the application side, the storage system saw them all as a single user- a single user with pretty much unlimited permissions that had root access on every VM in their cloud environment.

"Oh, boy, they released their secret key to essentially a root account, that's bad," you say. It gets worse. The code doesn't, but the logic does.

You see, the culprit here didn't want to learn the API. So they did everything via shell commands. They had one machine set up in the cloud environment with a bunch of machine-based permissions that allowed it to control anything in the cloud. When someone wanted to change quotas, the PHP code would shell out and use SSH to log into that cloud machine as root, and then run the cloud vendor's proprietary CLI tool from within their own environment.

[Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!

https://thedailywtf.com/articles/the-secure-cloud-api


Метки:  

Error'd: Stay Away From California

Пятница, 14 Сентября 2018 г. 13:30 + в цитатник

"Deep down, I knew this was one of the most honest labels I've ever seen," wrote Bob E.

 

"Some people struggle to get and keep a high credit score. I, on the other hand, appear to have gotten a high score," writes Pawel.

 

Steve M. wrote, "I'm trying to register our travel insurance with ROL Cruises, but our travel policy has been rejected because it's under age."

 

"This happens every time my mom tries to place a call in her car," writes Dylan S., "Strangely enough, the call still goes through."

 

"While I appreciate the one year warranty, I don't think I will be using these tea cakes to replace my HP battery," J.R. wrote.

 

Neil D. writes, "So, am I supposed to enter -1 and then I can buy one?"

 

[Advertisement] Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.

https://thedailywtf.com/articles/stay-away-from-california


Метки:  

Crazy Like a Fox(Pro)

Четверг, 13 Сентября 2018 г. 13:30 + в цитатник

“Database portability” is one of the key things that modern data access frameworks try and ensure for your application. If you’re using an RDBMS, the same data access layer can hopefully work across any RDBMS. Of course, since every RDBMS has its own slightly different idiom of SQL, and since you might depend on stored procedures, triggers, or views, you’re often tied to a specific database vendor, and sometimes a version.

Keulemans Chama fox.png

And really, for your enterprise applications, how often do you really change out your underlying database layer?

Well, for Eion Robb, it’s a pretty common occurrence. Their software, even their SaaS offering of it, allows their customers a great deal of flexibility in choosing a database. As a result, their PHP-based data access layer tries to abstract out the ugly details, they restrict themselves to a subset of SQL, and have a lot of late nights fighting through the surprising bugs.

The databases they support are the big ones- Oracle, SQL Server, MySQL, and FoxPro. Oh, there are others that Eion’s team supports, but it’s FoxPro that’s the big one. Visual FoxPro’s last version was released in 2004, and the last service pack it received was in 2007. Not many vendors support FoxPro, and that’s one of Eion’s company’s selling points to their customers.

The system worked, mostly. Until one day, when it absolutely didn’t. Their hosted SaaS offering crashed hard. So hard that the webserver spinlocked and nothing got logged. Eion had another late night, trying to trace through and figure out: which customer was causing the crash, and what were they doing?

Many hours of debugging and crying later, Eion tracked down the problem to some code which tracked sales or exchanges of product- transactions which might not have a price when they occur.

$query .= odbc_iif("SUM(price) = 0", 0, "SUM(priceact)/SUM(" . odbc_iif("price != 0", 1, 0) . ")") . " AS price_avg ";

odbc_iif was one of their abstractions- an iif function, aka a ternary. In this case, if the SUM(price) isn’t zero, then divide the SUM(priceact) by the number of non-zero prices in the price column. This ensures that there is at least one non-zero price entry. Then they can average out the actual price across all those non-zero price entries, ignoring all the “free” exchanges.

This line wasn’t failing all the time, which added to Eion’s frustration. It failed when two very specific things were true. The first factor was the database- it only failed in FoxPro. The second factor was the data- it only failed when the first product in the resultset had no entries with a price greater than zero.

Why? Well, we have to think about where FoxPro comes from. FoxPro’s design goal was to be a data-driven programming environment for non-programmers. Like a lot of those environments, it tries its best not to yell at you about types. In fact, if you’re feeding data into a table, you don’t even have to specify the type of the column- it will pick the “correct” type by looking at the first row.

So, look at the iif again. If the SUM(price) = 0 we output 0 in our resultset. Guess what FoxPro decides the datatype must be? A single digit number. If the second row has an average price of, say, 9.99, that’s not a single digit number, and FoxPro explodes and takes down everything else with it.

Eion needed to fix this in a way that didn’t break their “database agnostic” code, and thus would continue to work in FoxPro and all the other databases, with at least predictable errors (that don’t crash the whole system). In the moment, suffering through the emergency, Eion changed the code to this:

$query .= "SUM(priceact)/SUM(" . odbc_iif("price != 0", 1, 0) . ")") . " AS price_avg ";

Without the zero check, any products which had no sales would trigger a divide-by-zero error. This was a catchable, trappable error, even in FoxPro. Eion made the change in production, got the system back up and their customers happy, and then actually put the change in source control with a very apologetic commit message.

[Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!

https://thedailywtf.com/articles/crazy-like-a-fox-pro


Метки:  

CodeSOD: Padding Your Time

Среда, 12 Сентября 2018 г. 13:30 + в цитатник

Today will be a simple one, and it’s arguably low-hanging fruit, because once again, it’s date handling code. But it’s not handling dates where it falls down. It falls down on something much more advanced: conditionals. Supplied by “_ek1n”.

if ($min == 0) {
    if ($hours == 12) {
        $hours = 12;
        $min   = '00';
    } else {
        $hours = $hours;
        $min   = '00';
    }
}

My favorite part is the type-conversion/padding: turning 0 into '00', especially the fact that the same padding doesn’t happen if $min is 1, or 3, or anything else less than 10. Or, it probably does happen, and it probably happens in a general fashion which would also pad out the 0- or maybe it doesn’t, and TRWTF is that their padding method can’t zero-pad a zero.

The most likely situation, though, is that this is just a brain fart.

[Advertisement] Otter - Provision your servers automatically without ever needing to log-in to a command prompt. Get started today!

https://thedailywtf.com/articles/padding-your-time


Метки:  

CodeSOD: Wear a Dunder Cap

Вторник, 11 Сентября 2018 г. 13:30 + в цитатник

In the Python community, one buzzword you’ll find thrown around is whether or not an approach is “pythonic”. It’s a flexible term, and something you can just throw out in code reviews, even if you’ve never written a line of Python in your life: “Is that Pythonic?”

The general rubric for what truly is “pythonic” is generally code that is simple and code that operates explicitly. There shouldn’t be any “magic”. But Python doesn’t force you to write “pythonic” code, and it provides loads of tools like decorators and metaclasses that let you get as complex and implicit as you like.

One bit of magic is the “dunder” methods, which are Python’s vague approach to operator overloading. If you want your class to support the [] operator, implement __getitem__. If you want your class to support the + operator, implement __add__. If you want your class to support access to any arbitrary property, implement __getattr__.

Yes, __getattr__ allows you to execute code on any property access, so a simple statement like this…

if self.connected:
  print(self.coffee)

…could involve twenty database calls and invoke a web service connected to a live coffee machine and literally make you a cup of coffee. At no point does the invoker ever see that they’ve called a method, it just happens by “magic”. And, for extra fun, there’s also a global method getattr, lets you wrap property access with a default, e.g., getattr(some_object, "property", False) will return the value of some_object.property, if it exists, or False if it doesn’t.

Whew, that’s a lot of Python internals, but that brings us to today’s anonymous submission.

Someone wrote some classes which might contain other classes, and wanted them to delegate property access to those, which means there are a number of classes containing this method:

def __getattr__(self, item):
    return self.nested.item

So, for example, you could call some_object.log_level, and this overridden __getattr__ would walk through the whole chain of objects to find where the log_level exists.

That’s fine, if the chain doesn’t contain any loops, where the same object appears in the chain multiple times. If, on the other hand, it does, you get a RecursionError when the loop finally exceeds the system recursion limit.

Our submitter found this a bit of a problem. When the access to log_level failed, it might take a long time before hitting the RecursionError- which, by the way, isn’t triggered by a stack overflow. Python tries to throw a RecursionError before you overflow the stack.

You can, but very much shouldn’t control that value. But our submitter didn’t want to wait for the thousands of calls it might take to get the RecursionError, so they wrapped their accesses to log_level in code that would tweak the recursion limit:

    # Protect against origin having overwritten __getattr__
    old_recursion_limit = sys.getrecursionlimit()
    n = 2
    while True:
        try:
            sys.setrecursionlimit(n)
        except RecursionError as e:
            n += 1
        break
    try:
        log_level = getattr(origin, "log_level", None)
    except RecursionError as e:
        object.__setattr__(origin, "log_level", None)
        log_level = getattr(origin, "log_level", None)
    sys.setrecursionlimit(old_recursion_limit)

So, first, we check the current recursion limit. Then, we try setting the recursion limit to two. If the current recursive depth is greater than two, then when we try to change the recursion limit it throws a RecursionError. So catch that, and then try again with one higher recursion limit.

Once we’ve set the recursion limit to one greater than our current recursion limit, we then try and fetch the log level. If we fail, we’ll just default to None, and finally return back to our original recursion limit.

This is an impressive amount of effort into constructing a footgun. From the use of __getattr__ in the first place, to the misplaced effort of short-circuiting recursion instead of preventing cycles in the first place, and finally to the abuse of setrecursionlimit and error handling to build… this. Absolutely nothing here should have happened. None of it.

Our submitter has confessed their sins. As penance, they’ll say twenty Hail Guidos, and fix this code. Remember, you can’t be absolved of your sins if you just keep on sinning.

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!

https://thedailywtf.com/articles/wear-a-dunder-cap


Метки:  

CodeSOD: Legacy Switchout

Понедельник, 10 Сентября 2018 г. 13:30 + в цитатник

About a decade ago, I attended a talk. The speaker made the argument that "legacy code" may have many possible interpretations, but the practical view was to simply think of legacy code as "code without unit tests". Thus, the solution to modernizing your legacy code was to simply write unit tests. Refactoring the code to make it testable would have the side effect of modernizing the code base, and writing tests would act as documentation. It's that easy.

Andrew is struggling with some legacy code right now. Worse, they're trying to integrate a large mountain of legacy code into a custom, in-house CI/CD pipeline. This particular pile of legacy code dates back to the mid-2000s, so everything in it is glued together via XML. It was some of that XML code which started failing when Andrew threw some unit tests at it.

It doesn't start that bad:

static bool IsNamespace(XName name) { return name.LocalName == "xmlns" || name.NamespaceName == "http://www.w3.org/2000/xmlns/"; }

Now, this is a silly method, and even .Net 1.0 had an XmlNamespaceManager for handling interacting with namespaces. Without knowing more of the overall context, maybe this method was actually useful? At the very least, this method isn't a WTF.

One thing I want you to note, however, is that this method does the sane thing, and simply returns the result of a boolean comparison. That method was written by the same developer, and checked in on the same day as this method:

static bool IsNewtonsoftNamespace(XAttribute attribute) { switch (attribute.Value) { case "http://james.newtonking.com/projects/json": return true; } switch (attribute.Name.NamespaceName) { case "http://james.newtonking.com/projects/json": return true; } return false; }

Between writing IsNamespace and IsNewtonsoftNamespace, the developer apparently forgot that they could just return boolean expressions. They also apparently forgot- or perhaps never knew- what an if statement is supposed to do.

Andrew at least has the code building in their CI solution, but can look forward to many more weeks of fixing test failures by going through code like this.

[Advertisement] Ensure your software is built only once and then deployed consistently across environments, by packaging your applications and components. Learn how today!

https://thedailywtf.com/articles/legacy-switchout


Метки:  

Error'd: An Unfortunate Sign

Пятница, 07 Сентября 2018 г. 13:30 + в цитатник

"Found this in the School of IT. 404: Women not found. Fairly accurate," wrote Maddie J.

 

"In true Disney fashion, even their servers bid us farewell after 'It's a Small World After All' had wrapped up," writes Daniel H.

 

Lorens wrote, "I thought the GNOME bugs in the new 'Bionic Beaver' Ubuntu were going to drain my battery, but turns out there's a lot left."

 

"You know, I never click on ads, and this one certainally doesn't 'speak' to my needs, but I have to know - what is it advertising?" writes Aaron K.

 

John A. wrote, "We got a new super 'secure' expense reporting tool at work."

 

"I guess so long as 'undefined' doesn't get hacked, I'm ok," James writes.

 

[Advertisement] ProGet can centralize your organization's software applications and components to provide uniform access to developers and servers. Check it out!

https://thedailywtf.com/articles/an-unfortunate-sign


Метки:  

CodeSOD: Not a Not Bad Approach

Четверг, 06 Сентября 2018 г. 13:30 + в цитатник

In terms of elegance, I think the bitmask has a unique beauty. The compactness of your expression, the simple power of bitwise operators, and the way you can see the underlying implementation of numbers laid bare just speaks to me. Of course, bitmasks can be a bit opaque, and you may have to spend some time thinking about what foo &= 0xFF0000 is actually doing, but there’s also something alluring about it.

Of course, bitmasks are surprisingly hard. For example, let’s look at some code submitted anonymously. This code is meant to run on a line of coin-operated dryers. Depending on the particular install, how many coins a patron puts in, what modes have been enabled by the owner, and so on, different “extra” features might be disabled or not disabled.

Since bitmasks are hard, we’re not going to use one. Instead, let’s have a pile of constants, like so:

#define DRYER_EXTRA_1    1
#define DRYER_EXTRA_2    2
#define DRYER_EXTRA_3    3
#define DRYER_EXTRA_4    4
#define DRYER_NOT_EXTRA_1    6
#define DRYER_NOT_EXTRA_2    7
#define DRYER_NOT_EXTRA_3    8
#define DRYER_NOT_EXTRA_4    9

Now, how might we use those constants? Why, we’re going to use constructs like this:

if(supportedExtra == DRYER_EXTRA_1)
{
    notSupportedExtrasList[extra1] = DRYER_NOT_EXTRA_1;
}
else
{
    notSupportedExtrasList[extra1] = DRYER_EXTRA_1;
}

A double negative is not a not bad idea. If this dryer is configured to support DRYER_EXTRA_1, then we’ll add DRYER_NOT_EXTRA_1 to the list of unsupported extras. There’s a variation on this code for every possible extra, and they all use the double-negative pattern.

As extras are being selected, however, we need to check against things like if there’s enough money in the machine. Thus, we see patterns like:

if(isEnoughMoney == FALSE)
{
    selectedExtra &= ~DRYER_EXTRA_1;
    //so this is a bit mask now?
}

//...some more lines later
if(selectedExtra == DRYER_EXTRA_1)
{
    //we're back to treating it as integer?
}

//...some more lines later
if((selectedExtra & DRYER_EXTRA_1) == DRYER_EXTRA_1)

Our developer here gets confused about the nature of the constants, swinging from manipulating it as bitmasks (which won’t work, as the constants aren’t powers of two), then back as a regular number (which could work, but maybe isn’t the best way to design this), and back to bitmasks again.

You may be shocked to learn that this entire module doesn’t work, and our submitter has been called in to junk it and rewrite it from scratch. They’ll be using bitmasks.

[Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!

https://thedailywtf.com/articles/not-a-not-bad-approach


Метки:  

CodeSOD: Caught Up in the Captcha

Среда, 05 Сентября 2018 г. 13:30 + в цитатник

Gregor needed to download a network driver. Upon clicking the link, a "captcha" appeared, presumably to prevent hotlinking to the driver files. It wasn't a real, image-based captcha, but a simple "here's some characters, type them into the box".

The code which popped up was "S i u x q F b j NaN 4". He hit the "new code" button, and got "T o A 0 J V s L NaN a". In fact, "NaN" showed up in the penultimate position in every code.

Curious, Gregor pulled up the debugger to see how the captcha was generated.

function Captcha(){ var alpha = new Array('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', '0','1','2','3','4','5','6','7','8','9'); var i; for (i=0;i<20;i++){ var a = alpha[Math.floor(Math.random() * alpha.length)]; var b = alpha[Math.floor(Math.random() * alpha.length)]; var c = alpha[Math.floor(Math.random() * alpha.length)]; var d = alpha[Math.floor(Math.random() * alpha.length)]; var e = alpha[Math.floor(Math.random() * alpha.length)]; var f = alpha[Math.floor(Math.random() * alpha.length)]; var g = alpha[Math.floor(Math.random() * alpha.length)]; var h = alpha[Math.floor(Math.random() * alpha.length)]; var i = alpha[Math.floor(Math.random() * alpha.length)]; var j = alpha[Math.floor(Math.random() * alpha.length)]; } var code = a + ' ' + b + ' ' + ' ' + c + ' ' + d + ' ' + e + ' '+ f + ' ' + g + ' ' + h + ' ' + i + ' ' + j; document.getElementById("mainCaptcha").innerHTML = code document.getElementById("mainCaptcha").value = code }

The first thing that you might notice is that they wanted to be really random, so they generate the random code inside of a for loop, which claims to execute twenty times. Twenty random numbers are better than one, right?

Well, obviously not. The loop almost always executes only once. The variable i is not simply the loop variable, but also one of the variables holding the captcha characters. If it's set to a letter, i<20 is false, and the loop breaks. If it's set to a number, the loop executes again (meaning that if you're really unlucky, the loop could possibly execute for a very long time).

It's a bit embarrassing to admit, but I had to think for a minute to figure out why i was NaN, and not just whatever letter it got assigned. Then I remembered- a for loop is just a while loop, and the increment step is executed at the end of the loop. Calling ++ on a string assigns NaN to the variable.

In Gregor's case, typing the captcha, NaN and all, into the input box worked just fine, and he was able to download his drivers.

[Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!

https://thedailywtf.com/articles/caught-up-in-the-captcha


Метки:  

SLA-p the Salesman

Вторник, 04 Сентября 2018 г. 13:30 + в цитатник

A Service-Level Agreement (SLA) is meant to ensure customer issues receive the attention they deserve based on severity. It also protects the support company from having customers breathing down their neck for frivolous issues. All of the parameters are agreed upon in writing ahead of time and both sides know the expectations. That is, until a salesman starts to meddle and mess things up, as happened at the place Dominick worked for.

Dominick was a simple remote support tech who fixed things for clients well ahead of the SLA. On the rare occasion there was a priority 1 issue - something stopping anyone in the company from doing work - they had 24 hours to fix it before large monetary penalties would start to rack up. One Friday a priority 4 issue (5 business day SLA) came in from the CFO of a new client. The ticket was assigned to Dominick, who had higher priority work to do for other clients, so he decided it could wait until the following week.

Canon ir2270

Dominick came in Monday morning to find Benjamin, a senior salesman who happened to be a personal friend of the CFO, sitting on his desk with his huge arms crossed. Benjamin glanced at his watch to see it was 7:59 AM. "About time you showed up, Dom. I found out you didn't do the ticket that came in Friday and I want an explanation!"

Still in a pre-coffee Monday morning haze, Dominick had to think for a second to figure out what he was talking about. "Oh... that thing about ordering a new printer? That was only priority 4 and it literally said 'no rush' in it. I have 4 more days to get it done."

Benjamin sprang up off Dom's desk and used his beefy arms to forcefully shove an index finger into his chest. "You don't get it do you, bro?? When I made this deal with them, I assured them anything would be treated with the highest priority!" Ben shouted while spraying an unsanitary amount of saliva droplets. "I don't care what your silly numbering system says, it needs to get done today!

"Ok... well let me sit down and look at it," Dominick said timidly while rubbing the spot on his chest that received a mean poking. Benjamin stormed off to presumably consume another protein shake. He pulled up the ticket about ordering a new printer for the CFO's office. It seemed he'd read about this top of the line printer in some tech magazine and really wanted it. The problem was the printer wasn't even on the market yet - it would be released at the end of the month. Since there was literally nothing Dominick could do to get the printer, he closed the ticket and asked that a new one be submitted when the printer was available.

Later that afternoon, Dominick heard stomping behind him and before he could turn around, Benjamin spun him around in his chair and got in his face. "Hey there, bro. Where is my guy's printer?? He told me you closed his submission without ordering it!"

Dominick stood up to defend himself and weakly poked Ben in the chest. "Listen, bro! He wants a printer that isn't out yet. The best I can do is pre-order it and have it shipped to him in a couple weeks. I closed the ticket so we don't get dinged on the SLA to get this done in 5 days."

Benjamin furrowed his brow and got back within saliva-spraying distance, "You'll have to do better than that, Dom! While you were screwing around not resolving this I made an addendum to their SLA. Any ticket submitted by a CxO level executive will be treated as priority 1 by us. So you better pull whatever techie nerd strings you have to get that printer ordered in the next 24 hours!"

After Benjamin stormed off yet again, the reality of what he had done set in. Since the SLA for the printer was now 24 hours, they would start getting charged penalties by tomorrow. Dominick quickly began crafting an email to senior management to explain the situation and how the request wasn't able to be met. He wasn't sure what sort of "techie nerd" resources Benjamin thought he had, but it wasn't going to happen.

Predictably, the situation didn't end well. The financial penalties started adding up the following day, and the next day, and so on. It became so expensive that it was more cost-effective to pay the client to modify the addendum to the SLA that Benjamin made (they couldn't be compelled to do so otherwise) than to continue to rack up fines.

The end of the month came and the world's most expensive printer finally shipped, which was a relief to everyone. But that also meant the end-of-month financial statements showed the huge deficit caused by it. To compensate, the company decided to lay off 20% of the support staff including Dominick. Benjamin, of course, got to keep his job where he always put customer needs first.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!

https://thedailywtf.com/articles/sla-p-the-salesman


Метки:  

Classic WTF: Security By Letterhead

Понедельник, 03 Сентября 2018 г. 13:30 + в цитатник
It's a holiday in the US, so we're turning back the clock a bit.
How do you make sure nobody issues an unauthorized request for a domain transfer? This registrar has serious security to prevent just that kind of event. You know this must be a classic, because it involves fax machines. Original -- Remy

Security through obscurity is something we've all probably complained about. We've covered security by insanity and security by oblivity. And today, joining their ranks, we have security by letterhead.

John O'Rourke wrote in to tell us that as a part of his job, he often has to help clients transfer domain names. He's had to jump through all kinds of crazy hoops to transfer domain names in the past; including just about everything except literally jumping through hoops. After faxing in a transfer request and receiving a rejection fax an hour later, he knew he was in for a fight.

John called the number on the rejection letter to sort things out.

John: Yes, I'm calling to find out why request number 48931258 to transfer somedomain.com was rejected.
ISP: Oh, it was rejected because the request wasn't submitted on company letterhead.
John: Oh... sure... but... uh, just so we're on the same page, can you define exactly what you mean by 'company letterhead?'
ISP: Well, you know, it has the company's logo, maybe a phone number and web site address... that sort of thing. I mean, your fax looks like it could've been typed by anyone!
John: So you know what my company letterhead looks like?
ISP: Ye... no. Not specifically. But, like, we'd know it if we saw it.
John: And what if we don't have letterhead? What if we're a startup? What if we're redesigning our logo?
ISP: Well, you'd have to speak to customer-
John (clicking and typing): I could probably just pick out a semi-professional-looking MS Word template and paste my request in that and resubmit it, right?
ISP: Look, our policy-
John: Oh, it's ok, I just sent the request back in on letterhead.

The transfer was approved. John smiled, having successfully circumvented the ISP's security armed with sophisticated hacking tools like MS Word templates and a crappy LaserJet printer.

[Advertisement] Ensure your software is built only once and then deployed consistently across environments, by packaging your applications and components. Learn how today!

https://thedailywtf.com/articles/classic-wtf-security-by-letterhead


Метки:  

CodeSOD: Why I Hate Conference Swag (and What can be Done About it)

Пятница, 31 Августа 2018 г. 13:30 + в цитатник

Hey everyone - I'm at a conference this week I'd like to cover a WTF that I've been seeing this week - VENDOR SWAG.

IGC Show trade show floor Aug 2016

Ok, if you are one of those poor souls who are always heads down in code and never attend workshops or conferences, this won't make much sense to you, but here's the deal - companies will set up a booth or a table and will pass out swag in exchange for your contact info and (possibly) a lead. To me, this is easily the dirtiest transaction one can make and rife with inequalities. Your information is super valuable - as in possibly generating thousands or millions of sales dollars. The 'gift' will be SUPER cheap by comparison.

How cheap? WELL, here are some examples:

  1. Pens - My bag runneth over with pens. Really, I'm fine, I don't nee any more. Also, if you must make that your swag, don't make me 'work' for a pen. Really? I'm not going to sit through a session about hybridized lizards in the cloud for your crummy mass ordered pens.

  2. Buttons - If the button is made of money, and doesn't have a pin in the back, and it's actually just a $5 bill, then yeah, sure. Other than that? Big nope.

  3. Poor quality shirts or outright badly designed shirts - I know what I'm talking about here. Give me a shirt that YOU'D like to wear, and I don't mean just because you work there. Also - when you hand me a shirt that's made out of tissue paper thin material, it's like "Hey, let's not kid each other - you're just going to turn this thing into a rag."

  4. Four words - Fun Size Candy Bars - If you do this, I hate you on sight. You didn't plan ahead and ended up hitting Target on your way from the airport. Just because vendor halls are like Halloween trick-or-treating doesn't mean you have to handle it that way. Shame on you and your company for dialing that one in.

True - I can, and certainly do, just say "no thanks" to that miniature Butterfinger swag ought be good. An equal exchange that's not more like selling your soul for a pen.

So, with that said...the solution? Enter Virtual Swag or vSwag.

This year, I participated in my first Hackathon ever and our team's theme was Blockchain. After registering we were given a while to prepare which I took advantage of to research the topic and for me, that was pretty fun (but actually we only just made a team after arriving).

The way we planned it, conference attendees can collect vSwag tokens from vendors and redeem them for swag items.

Below is the Solidity file I fudged together based heavily on the Ethereum Crowdsale example, not knowing anything before the Hackathon (hence my 'horrible' comment). Now, if you actually know what you're doing (not me) and want to contribute, the project is out on Github or if you want to skip to the good stuff (and like memes) check out the PowerPoint (*chef kiss*).


pragma solidity ^0.4.18;

/* This is horrible code. */

contract vSwag {
    address public vendorName;
    uint public swagGoal;
    uint public amountRaised;
    uint public deadline;
    uint public price;
    //token public tokenReward;
    mapping(address => uint256) public balanceOf;
    bool swagGoalReached = false;
    bool vSwagClosed = false;

    event GoalReached(address recipient, uint totalAmountRaised);
    event SwagswagToken(address backer, uint amount, bool isContribution);

	 /* This creates an array with all balances */
   // mapping (address => uint256) public balanceOf;
	
    /**
     * Constructor function
     *
     * Setup the owner
     */
    function vSwagConstructor(
        address ifSuccessfulSendTo,
        uint swagGoalInEthers,
        uint durationInMinutes,
        uint etherCostOfEachToken,
        address addressOfTokenUsedAsReward
    ) public {
        vendorName = ifSuccessfulSendTo;
        swagGoal = swagGoalInEthers * 1 ether;
        deadline = now + durationInMinutes * 1 minutes;
        price = etherCostOfEachToken * 1 ether;
		balanceOf[msg.sender] = 1000;              // How much swag coin you got, captain?

    }

	/* Send swag */
    function transfer(address _to, uint256 _value) public returns (bool success) {
        require(balanceOf[msg.sender] >= _value);           // Check if the sender has enough
        require(balanceOf[_to] + _value >= balanceOf[_to]); // Check for overflows
        balanceOf[msg.sender] -= _value;                    // Subtract from the sender
        balanceOf[_to] += _value;                           // Add the same to the recipient
        return true;
    }
	
    /**
     * Fallback function
     *
     * The function without name is the default function that is called whenever anyone sends funds to a contract
     */
    function () payable public {
        uint amount = msg.value;
        balanceOf[msg.sender] += amount;
        amountRaised += amount;
        transfer(msg.sender, amount / price);
       emit SwagswagToken(msg.sender, amount, true);
    }

    modifier afterDeadline() { if (now >= deadline) _; }

    /**
     * Check if goal was reached
     *
     * Checks if the goal or time limit has been reached and ends the campaign
     */
    function checkGoalReached() public afterDeadline {
        if (amountRaised >= swagGoal){
            swagGoalReached = true;
            emit GoalReached(vendorName, amountRaised);
        }
    }


    /**
     * Decrement Swag
     *
     */
    function safeWithdrawal() public afterDeadline {
        if (!swagGoalReached) {
            uint amount = balanceOf[msg.sender];
            balanceOf[msg.sender] = 0;
            if (amount > 0) {
                if (msg.sender.send(amount)) {
                   emit SwagswagToken(msg.sender, amount, false);
                } else {
                    balanceOf[msg.sender] = amount;
                }
            }
        }

        if (swagGoalReached && vendorName == msg.sender) {
            if (vendorName.send(amountRaised)) {
               emit SwagswagToken(vendorName, amountRaised, false);
			} else {
				//something something...give back swag coin
                swagGoalReached = false;
            }
		
        }
    }
}

So, vendors, hear me now - steal this idea!

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!

https://thedailywtf.com/articles/why-i-hate-conference-swag-and-what-can-be-done-about-it


Метки:  

Keeping Up Appearances

Четверг, 30 Августа 2018 г. 13:30 + в цитатник

Just because a tool is available doesn't mean people will use it correctly. People have abused booleans, dates, enums, databases, Go-To's, PHP, reinventing the wheel and even Excel to the point that this forum will never run out of material!

Bug and issue trackers are Good Things™. They let you keep track of multiple projects, feature requests, and open and closed problems. They let you classify the issues by severity/urgency. They let you specify which items are going into which release. They even let you track who did the work, as well as all sorts of additional information.

An optical illusion in which two squares that are actually the same color appear to be different colors

Every project, no matter how big or small, should make use of them.

Ideally, they would be used correctly.

Ideally.

Matt had just released the project that he'd been working on for the past few years. As always happens, some "issues" cropped up. Some were genuine defects. Some were sort-of-enhancements based on the fact that a particular input screen was unwieldy and needed to be improved. At least one was a major restructuring of a part of the project that did not flow too well. In all, Matt had seven issues that needed to be addressed. Before he could deliver them, he needed defect tickets in the bug tracking system. He wasn't authorized to raise such tickets; only the Test team could do that.

It took a while, but he finally received a ticket to do the work to... but everything had been bundled up into one ticket. This made it very difficult to work with, because now he couldn't just clear each issue as he went. Instead, he had to package them up in abeyance, so to speak, and only release them when they were all complete. This also meant that the test documentation, providing instructions to the Test team as to how to ensure that the fix was working as required, all had to be bundled up into one big messy document, making it more difficult for Testers to do their job as well.

So he questioned them: If we can raise a single defect ticket, then what stops us from raising all 7 needed tickets so that the issues can be addressed separately? The answer was: Because these defects appear post-release, it is clear that they weren't caught at the pre-release stage, which means that Somebody Wasn't Doing Their Job Properly in the Test team, which makes them Look Bad; it brings their failure to catch the issues to the attention of Management.

In other words, in order to keep the Test team from looking bad, they would only ever raise a single ticket (encompassing all detected issues) for any given release.

Imagine if all of the bugs from your last major release were assigned to you personally, in a single ticket. Good luck estimating, scheduling, coding, debugging and documenting how to test the single logical bug!

Matt raised this bad practice with management, and explained that while the reason for why they do this is to hide their inadequacy, it also prevents any meaningful way to control work distribution, changes and subsequent testing. It also obscures the actual number of issues (Why is it taking you seven weeks to fix one issue?).

Management was not amused at having been misled.

[Advertisement] Otter - Provision your servers automatically without ever needing to log-in to a command prompt. Get started today!

https://thedailywtf.com/articles/keeping-up-appearances


Метки:  

CodeSOD: Rectangle Marks The Spot

Среда, 29 Августа 2018 г. 13:30 + в цитатник

World Map flat Mercator

If you need your user's country of origin, there are many ways you can go about obtaining it programmatically. Some may opt for a simple drop-down that prompts the user to specify his/her country. If you don't want to burden your user this way, you might look at their session data and return their country of origin, time zone, or some other useful information. If you have fancy enough APIs at your disposal, you could even reverse geocode the user's longitude/latitude position and obtain an address.

Or, you can start with their location and perform the code equivalent of throwing a dart at a map littered with creepy-looking post-it notes:

def country(latitude, longitude):
  if -130 < longitude < -60:
    return 'US'
  elif 110 < longitude < 155 and -40 < latitude < -10:
    return 'AU'
  elif -8 < longitude < 2 and 50 < latitude < 60:
    return 'GB'
  elif 60 < longitude < 100:
    return 'IN'
  return None

def timezone(longitude):
  return int(longitude * 0.0655737704918)

Submitter Zekka writes: "When we decided to internationalize our app, it became necessary to determine what country the user was in. That's not even the correct constant to convert longitude to approximate timezone. I have no clue where they got it. It should be 0.06666 ..."

[Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!

https://thedailywtf.com/articles/rectangle-marks-the-spot


Метки:  

CodeSOD: Look Ahead. Look Out!

Вторник, 28 Августа 2018 г. 13:30 + в цитатник

I'm an old person. It's the sort of thing that happens when you aren't looking. All the kids these days are writing Slack and Discord bots in JavaScript, and I remember writing my first chatbots in Perl and hooking them into IRC. Fortunately, all the WTFs in my Perl chatbots have been lost to time.

"P" has a peer who wants to scrape all the image URLs out of a Discord chat channel. Those URLs will be fetched, then passed through an image processing pipeline to organize and catalog frequently used images, regardless of their origin.

Our intrepid scraper, however, doesn't want to run the risk of trying to request a URL that might be invalid. So they need a way to accurately validate every URL.

Now, the trick to URLs, and URIs in general, is that they have a grammar that seems simple but is deceptively complex and doesn't lend itself to precise validation via regular expressions. If you were a sane person, you'd generally just ballpark it into the neighborhood and handle exceptions, or maybe copy/paste from StackOverflow and call it a day.

This developer spent 7 hours developing their own regular expression to validate a URL. They tested it with every URL they could think of, and it passed with 100% accuracy, which sounds like the kind of robust testing we'd expect from the person who wrote this:

const regex = /((?:(http|https|Http|Https|rtsp|Rtsp):\/\/(?:(?: [a-zA-Z0-9\$\-\_\.\+\!\*'\(\)\,\;\?\&\=]|(?:\%[a-fA-F0-9]{2})){1,64}(?:\: (?:[a-zA-Z0-9\$\-\_\.\+\!\*'\(\)\,\;\?\&\=]|(?:\%[a-fA-F0-9]{2})){1,25})? \@)?)?((?:(?:[a-zA-Z0-9][a-zA-Z0-9\-]{0,64}\.)+(?:(?:aero|arpa|asia|a [cdefgilmnoqrstuwxz])|(?:biz|b[abdefghijmnorstvwyz])|(?:cat|com|coop|c [acdfghiklmnoruvxyz])|d[ejkmoz]|(?:edu|e[cegrstu])|f[ijkmor]|(?:gov|g [abdefghilmnpqrstuwy])|h[kmnrtu]|(?:info|int|i[delmnoqrst])|(?:jobs|j[emop] )|k[eghimnrwyz]|l[abcikrstuvy]|(?:mil|mobi|museum|m[acdghklmnopqrstuvwxyz]) |(?:name|net|n[acefgilopruz])|(?:org|om)|(?:pro|p[aefghklmnrstwy])|qa|r [eouw]|s[abcdeghijklmnortuvyz]|(?:tel|travel|t[cdfghjklmnoprtvwz])|u [agkmsyz]|v[aceginu]|w[fs]|y[etu]|z[amw]))|(?:(?:25[0-5]|2[0-4][0-9]|[0-1] [0-9]{2}|[1-9][0-9]|[1-9])\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9] |[1-9]|0)\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\.(?:25 [0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])))(?:\:\d{1,5})?)(\/(?:(?: [a-zA-Z0-9\;\/\?\:\@\&\=\#\~\-\.\+\!\*'\(\)\,\_])|(?:\%[a-fA-F0-9]{2}))*)? (?:\b|$)/gi

Note each use of (?:). These are "look ahead" matches, which will execute depending on what comes after them. Using one or two of these in a regex makes its massively more complicated. This regex uses 32 look ahead expressions, taking "unreadability" to a new height, and flirting with the lesser demons which serve Zalgo. Bonus points for making the comparison case insensitive, but also checking for both http and Http, just in case.

"There's no module on NPM that can do all of this!" the developer proudly proclaimed. They then presumably uploaded it to NPM as a "microframework", used it within a few of their own modules, and then those modules got used by some other modules, and now 75% of the web depends on this regex.

[Advertisement] ProGet can centralize your organization's software applications and components to provide uniform access to developers and servers. Check it out!

https://thedailywtf.com/articles/look-ahead-look-out


Метки:  

CodeSOD: Assertive Programming

Понедельник, 27 Августа 2018 г. 13:30 + в цитатник

Defensive programming is an important tool in any developer's toolbox. In strictly typed languages, types themselves provide a natural defense against certain classes of bugs, but in loosely typed languages, you may have to be more clear about your assumptions.

For example, in Python, you might choose to use the assert keyword to, well, assert that something is true. It's often used in debugging, but it's also a good way to ensure that the state of the parameters passed to a function, or some other state of your system is correct before doing anything else. If it's not, the code raises an exception.

Dima R found this "interesting" riff on that concept.

try: assert r.status_code == 201 except: print(r.text) pass

Here, the assertion is confirming that we get the expected HTTP status code from a request, but if we didn't, it doesn't bubble up the exception- it just prints the body of the response.

This code has been in production for some time. When Dima tracked down the culprit and asked what they were thinking, the developer admitted:

it was originally an assert just for sanity, but then I wanted to print the result if the assert failed and I wasn't paying very much attention lol

[Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!

https://thedailywtf.com/articles/assertive-programming


Метки:  

Error'd: Exponential Customer Service

Пятница, 24 Августа 2018 г. 13:30 + в цитатник

"I think I'm missing some precision in my Dell customer number," writes Steve B.

 

Tief K. wrote, "Looking for culture in Vienna? You might try the theatre for a surprise."

 

"Funny thing happened while reading Raymond Chen's blog https://blogs.msdn.microsoft.com/oldnewthing/20180803-01/ where he referenced a page on Alaskan ferries which then showed me the attached ad," Sam L. writes.

 

Alicia wrote, "Is there anything Amazon doesn't sell?"

 

"I was having Internet connection problems, so decided to relax a bit and play some Diablo 3. Maybe it's a sign that I should go read a book instead," Radek K. writes.

 

Steve L. wrote, "Looks like mini symposium 215 has been updated to discuss the topic of HTML pasting worst practices."

 

[Advertisement] Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.

https://thedailywtf.com/articles/exponential-customer-service


Метки:  

Поиск сообщений в rss_thedaily_wtf
Страницы: 124 ... 69 68 [67] 66 65 ..
.. 1 Календарь