Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Comrade Gritty
Sep 19, 2011

This Machine Kills Fascists

Internet Janitor posted:

Do they specify a level of granularity? Public methods (easy and a good idea), Lines (arbitrary), Code paths (gently caress)?

If you break up your methods into small logical pieces, it's normally not _that_ hard to test all the code paths for each small piece. I mean that's basically unit testing. If you have to have 100% code coverage on every code path that the app could possibly take then I don't know what the gently caress.

Adbot
ADBOT LOVES YOU

Comrade Gritty
Sep 19, 2011

This Machine Kills Fascists
Contrived Example!

code:
def my_function(a, b, c, d):
    pass
changing to

code:
def my_function(a, b, c, d, e):
    pass
produces this diff:

code:
- def my_function(a, b, c, d):
+ def my_function(a, b, c, d, e):
while

code:
def my_function(
    a,
    b,
    c,
    d,
)
going to

code:
def my_function(
    a,
    b,
    c,
    d,
    e,
)
produces this diff:

code:
+ e,
This is basically why it's allowed for lists, tupes, dictionaries, and the args to a function is just a tuple.

Comrade Gritty
Sep 19, 2011

This Machine Kills Fascists

Frozen-Solid posted:

:monocle:

There are only 2 real reasons to use an ORM.

1) To have further protection against SQL injection

This can be mitigated with good code and sanitizing user input. The ORM just lets a coder be lazy and not give a poo poo, which is a good thing. Even WITH an ORM you should still write good code and sanitize user input.

2) Allows you to easily move from one database language to another.

If you're never going to change your database language, you don't need to worry about this either.


Emphasis mine.

If you're attempting to sanitize input instead of using a prepared statement you are exactly the sort of person who should never be writing SQL manually.

SQL is a lovely DSL that requires me to leave the language I prefer to write in, to write in another language. I use an ORM because I like writing in my preferred language and I like operating on objects.

That being said anyone who thinks "Use the ORM Luke" is the only answer and you never need raw SQL (or that an ORM is always the answer at all) is an idiot. Any ORM worth using has a way of dropping to raw SQL and optionally populating the object(s) from that raw SQL. Because, surprise!, sometimes the ORM writes brain dead SQL and you need to write it your self if you don't want to bog your app down.

Comrade Gritty
Sep 19, 2011

This Machine Kills Fascists

Frozen-Solid posted:

There's more to sanitizing user input than just escaping quotes to prevent injection. Every piece of user input should be checked for validity long before it even sees SQL. The fact that people think SQL injection is the only reason for sanitizing user input is the horror here.

Now you're backpedaling, you explicitly said that you should sanitize input to prevent SQL Injections. There are other, valid, reasons to sanitize input but your statement didn't mention them, it only claimed that the "correct" way to handle App -> DB code was to sanitize input which it patently wrong.

Comrade Gritty
Sep 19, 2011

This Machine Kills Fascists

Janin posted:

https://github.com/rails/rails/issues/5228

Russian programmer discovers massive security vulnerability in Rails. He reports it to the issue tracker, then the issue gets closed by Rails devs.

He uses it to re-open the issue, as a proof-of-concept.

Rails devs close it again.

He files Issue #5239: I'm Bender from Future. from the year 3012, and re-opens the original issue.

Rails devs close it again.

He submits a new file, "hacked", to the main Rails repository and re-opens the original issue.



The best part about this is how this has been known for like 4 years, and time and time again Rails Core has basically said it's not their problem, end developers should know this etc etc. The very same day that Github gets exploited using it they commit a change to Rails so that new projects by default have a whitelist instead of a blacklist.

Comrade Gritty
Sep 19, 2011

This Machine Kills Fascists

tef posted:

I can't wait for 'how secure is my credit card number'

http://ismycreditcardstolen.com/

Comrade Gritty
Sep 19, 2011

This Machine Kills Fascists

Thermopyle posted:

I love how this thread always comes back to PHP.

Ender.uNF posted:

PHP is a great example of mediocrity at work... Some dude created his own templating system, some other dudes hack on it, wanna-be coders everywhere adopt it en-masse. Popularity used as proof that it's great.

For reference I saw all sorts of similar horrors from coders back when I was doing ASP classic/VBScript... Including a whole host of Byzantine logic and "fun" type coercion crap. The difference is that VBScript was designed as a language by people who knew what they were doing so it produces far fewer WTFs from the platform and language itself.

Did you love the mixing of logic and presentation in Classic ASP, but hate all that useless language design? PHP: Party like its 1998!

http://otherwise-than-code.blogspot.com.au/2012/07/complex-made-easy-or-debate-over-php.html

Comrade Gritty
Sep 19, 2011

This Machine Kills Fascists

leterip posted:

The point is that you have to assume that the fopen() call could raise a warning regardless of how much checking you do because by the time you get to actually opening the file the state of the filesystem could be different, so why bother doing the checking? It's a completely worthless function.

There are other reasons to check if a file exists or is readable than to write (or read) from it. Sometimes you just want to know if the file exists and is readable.

Comrade Gritty
Sep 19, 2011

This Machine Kills Fascists

Cocoa Crispies posted:

Where's the horror there?

Global variables are great. Hope you didn't want to run a second copy in the same process.

Comrade Gritty
Sep 19, 2011

This Machine Kills Fascists

dexter posted:

If it's the same process, shouldn't it share the same configuration variables? We don't use environment variables for configuration (outside of random hack day projects that get pushed to Heroku) but it's an idea we've thrown around to simplify the deployment of new applications without pulling operations in. Right now repositories have development-mode only credentials that are overwritten by our deployment processes with real credentials. Can you expand on why environment variables are a horror?

The expanded reason is your application shouldn't rely on environment variables and instead your startup script (or wsgi file, or whatever) should accept those environment variables and feed them into a sane configuration method.

A contrived/simple example would be a flask app that just simply serves a directory and takes the directory to serve as a parameter. If you have in the app the concept of loading config from an environment variable then if you want to serve 2 directories you need to either a) Modify the app to handle a list of directories, b) run 2 separate processes one configured for dir 1, one configured for dir 2, or c) use Application dispatching to run multiple instances of the same flask app in the same process with different configurations. (http://flask.pocoo.org/docs/patterns/appdispatch/). It might not even be 2 instances of the same app but just 2 different apps you want to live in the same process and both have a hardcoded reference to "DATABASE_URL".

Another example of where you'll want to possibly run multiple configurations of the same app in the same process is with unit testing. Anyones whose used Django can attest to how much of a pain in the rear end it is to handle testing different configurations when unit testing a library. The bulk of this problem comes down to the massive amount of global state in Django, starting with DJANGO_SETTINGS_MODULE.

The *right* way to do configuration from an environment variable is to do something like:

Python code:
    import os
    import myapp

    database_url = os.environ.get("DATABASE_URL")

    app = myapp.create_app(config={"DATABASE_URL": database_url})
    app.run()
and then down the road when you add in another app that also wants a DATABASE_URL configured it becomes

Python code:
import os

from werkzeug.serving import run_simple
from werkzeug.wsgi import DispatcherMiddleware

import myapp
import otherapp

myapp_config = {
    "DATABASE_URL": os.environ.get("DATABASE_URL_MYAPP"),
}

otherapp_config = {
    "DATABASE_URL": os.environ.get("DATABASE_URL_OTHERAPP"),
}


application = DispatcherMiddleware(myapp.create_app(config=myapp_config), {
    "/otherapp": otherapp.create_app(config=otherapp_config),
})

run_simple('localhost', 5000, application, use_reloader=True)
The key being that your process global environment variable style configuration is being "injected" into your application instance, the application instance isn't reaching out looking at a process global. Doing that you've gained all the good parts of configuration from the environment while retaining all the benefits of not having configuration being a process global.

Comrade Gritty fucked around with this message at 13:50 on Jan 4, 2013

Comrade Gritty
Sep 19, 2011

This Machine Kills Fascists

dexter posted:

If it needs to connect to another database then it probably shouldn't run in the same process as this application. I think it's better to use a smart HTTP routing layer to handle dispatching requests to disparate applications instead of mounting them within a single application.

The database url was just an example. splitting it into separate processes can make sense. It can also make sense to keep them in the same one. Hardcoding environment variables means you're forced to make them separate processes. Even if you think that there is never a reason to have the same app in the same process there's still the matter of testing and when you use process globals that makes running tests with different configurations involve lots of monkey patching instead of simply being to create another instance of your app with different configuration.

Maybe a better real world example would be the boto library which will (optionally) pull the AWS credentials from an environment variable. This makes it easy to mistakenly use the wrong credentials in the wrong place. Forget to pass in the configuration to your second instance of boto (or stand up a new instance and forget to include the configuration for the second AWS account and boto automatically grabs your config for the primary account out of the environment variables). boto mitigates the inability to connect to a second account by allowing you to pass in the configuration and not pull it from environment variables but the behavior still has potential to turn a bug that would be easily caught and fairly "harmless" (e.g. it would exception because of missing creds) into potentially a very bad bug that has no immediately obvious consequences.

Comrade Gritty
Sep 19, 2011

This Machine Kills Fascists

Doctor w-rw-rw- posted:

Sorry, this just sounds kind of nuts. Why not just make a config.py and do "import config", add it to .gitignore, then copy it sans credentials as config.py.example and add that to source control?

The possibility of very heterogenous configurations because of configurable environment variables strikes me as a bad idea - not the idea of using an environment variable itself. With a file, you control how many configurations you have deployed, and certainly, if you like, you can even use environment variables to select what config file is being imported.

A hardcoded or environment variable driven configuration file has all the same problems. It's a process global configuration source that makes it difficult or hard to run multiple instances of your thing in one process, and to test varying configurations without needing to resort to hacky monkeypatching.

Comrade Gritty
Sep 19, 2011

This Machine Kills Fascists

Doctor w-rw-rw- posted:

What solution would you advocate, though? I think that as far as transparency and refactor-ability go, having a hardcoded file that is the single source of truth beats pulling variables in from the environment. Fully functional configuration management IMO gets in the way at small scale (well, for me at least), however essential it is at large scale.

App class that holds configuration as an instance member. You can configure that class in the script_you_use_to_actually_start_your_app (in my example it was a simple python script that just ran app.run() from Flask). Your configuration can come from a file, or environment variables or what not but when you instiate your application/library/whatever you process that file and then pass the configuration into your app instead of your app itself looking for a file, environment variable, etc.


Python code:
class GoodApp(object):
    def __init__(self, config):
        self.config = config

    def do_something(self):
        if self.config.DEBUG:
            print "Running my do_something method"

import config

class BadApp(object):

    def do_something(self):
        if config.DEBUG:
            print "Running my do_something method"

if __name__ == "__main__":
    import config
    import config2
    app1 = GoodApp(config=config)
    app2 = GoodApp(config=config2)
    app3 = BadApp()

The actual source of configuration isn't the bad part, but how your application gets it's configuration from that source is. If your app reaches out and tries to use a python module (ala Django) or the environment (ala Boto), it makes it more difficult to actually use the app in the same process for whatever reason (testing, hosting, whatever).

Comrade Gritty fucked around with this message at 03:50 on Jan 5, 2013

Comrade Gritty
Sep 19, 2011

This Machine Kills Fascists

dexter posted:

What sort of configuration changes are you making between test runs? Shouldn't those changes be managed within the individual unit or functional test groups?


Yes, but if your application assumes it's configuration is coming from the environment variable, or a particular file on the system then you need to patch the global state in your unit test to test those changes, instead of having your unit test just instantiate a new copy of your app configured a particular way.

Comrade Gritty
Sep 19, 2011

This Machine Kills Fascists
Passlib is excellent for password storage in Python FWIW

Adbot
ADBOT LOVES YOU

Comrade Gritty
Sep 19, 2011

This Machine Kills Fascists

Steve French posted:

Except that sorting an array of integers lexicographically is not sane. Maybe giving not-meaningful results when sorting a heterogeneous array when an explicit comparator is not given, as python does, is not sane either.

In Python3 instead of a not-meaningful result you get a TypeError.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply