Codificación defensiva (era Re: Pleonasmos booleanos)

Cuidado con las vulnerabilidades.

En Pleonasmos Booleanos, publicación del excelente blog Developeando, del cual me considero un ávido seguidor, Mario nos sensibiliza sobre la legibilidad de código. Nos dice que al escribir código debemos tomar en cuenta que no solo escribimos para la maquina. Concuerdo con él.

Sin embargo, hay que considerar qué pasa cuando lo que parece que es código redundante, realmente no lo es. Qué pasa si tenemos un código donde sólo debemos lanzar un misil cuando explícitamente no está prohibido:

function deboLanzarMisil(statusProhibicion) {
    result = false

    if (statusProhibicion == false)
        result = true;

    return result;
}

y lo que parece código redundante se legibiliza así:

function deboLanzarMisil(statusProhibicion) {
    return !statusProhibicion;
}

o incluso así:

function deboLanzarMisil(statusProhibicion) {
    if (!statusProhibicion)
        return true;

    return false;
}

En ambos casos habríamos introducido una vulnerabilidad (o bueno, de bug para arriba) que se presenta cuando statusProhibicion es null. En el primer código:

> deboLanzarMisil(true)
false
> deboLanzarMisil(false)
true
> deboLanzarMisil(null)
false

En ambos ejemplos de código legibilizado:

> deboLanzarMisil(true)
false
> deboLanzarMisil(false)
true
> deboLanzarMisil(null)
true

El código que estoy usando como ejemplo de lo que es supuestamente ilegible en realidad sigue un patrón de programación defensivo. En esta técnica:

  1. Se inicializa una variable a un valor seguro o valor por defecto.
  2. Sólo se modifica el valor bajo validaciones estrictas.

Claro que, aún cuando el siguiente código sigue esta técnica, habría sido inseguro, pero eso es porque se violó la regla #2:

function deboLanzarMisil(statusProhibicion) {
    result = false

    if (!statusProhibicion)
        result = true;

    return result;
}

El punto es que la única manera de corregir un potencialmente vulnerable return !x manteniendo la legibilidad hubiera sido usar más de una línea de código o invertir la lógica de la función.

El código que usa Mario para ejemplificar el caso de ilegibilidad resulta ser funcionalmente equivalente al código legibilizado y no son vulnerables ante una entrada null. Sin embargo consideremos los dos siguientes códigos, que no son funcionalmente equivalentes.

Este código es seguro:

function deboLanzarMisil(statusProhibicion) {
    if (statusProhibicion == false)
        return true;
    else
        return false;
}

> deboLanzarMisil(true)
false
> deboLanzarMisil(false)
true
> deboLanzarMisil(null)
false

Este otro código, aparentemente equivalente, resulta inseguro ante una entrada null:

function deboLanzarMisil(statusProhibicion) {
    if (statusProhibicion == true)
        return false;
    else
        return true;
}

> deboLanzarMisil(true)
false
> deboLanzarMisil(false)
true
> deboLanzarMisil(null)
true

Al final todo depende del lenguaje de programación. En SQL, por ejemplo, la lógica es distinta: NULL <> NULL da NULL y NULL == NULL da NULL.

En resumen, sí, hay que considerar que nuestro código será leído por otras personas o por nosotros mismos en el futuro (o por un violento psicópata que sabe dónde vives), pero recuerda que, en Javascript, x == false no es funcionalmente equivalente a !x.


Comentarios

Codificación defensiva (era Re: Pleonasmos booleanos) — 1 comentario

  1. Aunque ya mencionas acertadamente que “Al final todo depende del lenguaje de programación”, quiero ahondar un poco más al respecto.

    El problema de fondo aquí es que JavaScript tiene severos problemas de diseño y uno de los más graves es que su sistema de tipos de datos es débil (al estilo de Perl y PHP), deficiencia suficiente para rechazar su uso.

    En su lugar, podríamos optar por lenguajes de programación que tenga un sistema de tipos de datos fuerte, que protegería al programador de este clase de errores.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *