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:
- Se inicializa una variable a un valor seguro o valor por defecto.
- 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
.
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.