Regression Testing

I recently had a discussion with a delightful individual regarding my current side project.

I mentioned that I was using Jest for my regression testing, and she asked something along the lines of “If this is a side project and nobody is forcing you to write unit tests, then why are you writing unit tests?”

For context, I’m pretty sure that she writes unit tests on everything that she writes regardless of whether or not someone is ‘forcing’ her to write them. You could say that she was testing me. (See what I did there?)

She wanted to know my reason for writing them and see how it matched up with her reasons.

I told her that I’d heard a quote attributed to Robert Martin (Uncle Bob) that went something along the lines of “Anyone who thinks that they can move faster by not writing unit tests is smoking some pretty crazy stuff”.

That’s not an exact reproduction of what I heard, and I haven’t been able to find the original quote, but I think the sentiment is correct.

It can feel like you’re ‘wasting’ time writing unit tests when you could instead be creating something new, but the fact is that automated tests save a ton of time when it comes to debugging things down the road, and if your unit tests help you avoid introducing a bug into production that prevents the loss of hundreds of thousands (or more) of dollars, then you’ve likely just paid for the unit tests many times over.

So, I’m still going to keep on building unit tests even when nobody ‘makes’ me do it.

Minimum Viable Product (MVP)

My accounting background, combined with working for a startup previously means that I’m familiar with at least some of the advice given to tech founders.

Generally, the advice is to get your minimum viable product out the door as soon as possible. Make sure that people are actually willing to pay money for your service, and then you can start worrying about cleaning things up and worrying about how your product needs architected in order to be able to scale.

I can see the value to the suggestion. You don’t want to spend 10 years building something and then find out that you were solving a problem that nobody else feels like is a problem.

It’s far better to spend 6 months building a MVP, and then find out that your product isn’t going to be a go.

For certain personality types, it’s really hard to move forward on anything until it’s perfect. I don’t normally consider myself to be one of those people. I tend to think that perfect is the enemy of good enough, but I’m running across situations lately that give me a bit more sympathy for that mindset than I used to have.

It can be hard to know when you’re reinventing the wheel, vs. when you’re trying to gather enough foundational knowledge in an area to avoid tripping over something that is otherwise going to torpedo your entire idea.

It’s a tricky judgement call, made all the more tricky when it’s your first rodeo and you’re not 100% sure that you can build the application in the first place.

ServiceNow UI Policies

I really thought that I’d posted about this previously, but I couldn’t find the post, and it’s something that’s tripped me up a couple of times.

When building a UI policy in ServiceNow, if you want to be able to clear the variable value, then you need to build it to hide the variable.

Or to put it another way, never build a UI Policy Action that has the ‘Clear the Variable Value’ box checked and which also has ‘Visible’ set to false.

If you do that, then when the variable is shown, the system will clear the value of the variable, which is never what you want to happen. You want the system to clear the value of the variable when the variable is hidden.

Pattern for Pushing API Call to Database

I’m working on a side project where I accept a post call into an end point and then push that data into a user record.

My starting point was based on a Udemy class where the instructor walked through a project that tied an Express app into Mongo DB. Here’s the user schema (../models/user.js):

const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
    firstName: {
        type: String,
        required: true,
        trim: true
    },
    lastName: {
        type: String,
        required: true,
        trim: true
    },
    entitlements: {
        type: String,
    },
}, {
    timestamps: true
});

So, a first name, a last name, and some kind of entitlements field that we don’t want to have filled out by the user–we want to apply some kind of business logic and fill that in ourselves.

Here is the user router (../routers/user.js):

const express = require('express');

const router = new express.Router();

// Import Model
const User = require('../models/user');

// Create a new user
router.post('/user', async (req, res) => {
    try {
        //Create the user
        const user = new User(req.body);
        await user.save();
        res.status(201).send(user);
    } catch (error) {
        res.status(400).send({Error: error.message});
    }
})

Here is my app.js (which is called by index.js):

const express = require('express');

require('./db/mongoose');

// Routers
const userRouter = require('./routers/user');
const app = express();


// Options
app.use(express.json());
app.use(userRouter);

module.exports = app;

Here is my ./db/mongoose.js file:

const mongoose = require('mongoose');

mongoose.connect(process.env.MONGODB_URL, {
    useNewUrlParser: true,
    useCreateIndex: true,
    useFindAndModify: false,
    useUnifiedTopology: true
});

Here is my index.js:

'use strict';

const app = require('./app');

const port = process.env.PORT;

app.get('', (req, res) => {
    res.send("Nothing here, but it's up and working...");
});

app.listen(port, () => {
    logger.logInfo("Server is up on port " + port);
})

As you’ve no-doubt guessed, this is a very trimmed down version of my actual app. I believe that I’ve got all of the relevant pieces such that this would run, but if I’ve missed something I apologize–the point of this post should still come through.

The issue I noticed is that with what I’ve got above, it’s actually possible for the user to pass an ‘entitlements’ property on the request and it will push that through to the database. Obviously, that requires that the person trying to exploit the loophole figures out that you’ve got an entitlements field on your user record. Then, they have to figure out how you’re representing elevated access in that string field (or however you’re storing the thing that you don’t want users setting themselves).

That’s all very unlikely, but it would be foolish to depend on it not happening.

As far as addressing the gap, my first instinct was to go through and delete off the attributes on the request that I don’t want the user to be able to set. Something like this:

const express = require('express');

const router = new express.Router();

// Import Model
const User = require('../models/user');

// Create a new user
router.post('/user', async (req, res) => {
    try {
        //Remove any protected fields
        delete req.body.entitlements;

        //Create the user
        const user = new User(req.body);
        await user.save();
        res.status(201).send(user);
    } catch (error) {
        res.status(400).send({Error: error.message});
    }
})

That does the trick, but is asking for problems down the road. Each time I add a new ‘protected’ field that I don’t want users to be able to set, I’ve got to remember to come back here and remember to delete it off of the request body. I can virtually guarantee that I’ll forget to do that at some point.

The better option is to make sure that I only submit ‘non-protected’ fields to the database. Something like this:

const express = require('express');

const router = new express.Router();

// Import Model
const User = require('../models/user');

// Create a new user
router.post('/user', async (req, res) => {
    try {
        //Copy over acceptable attributes so that call can't populate protected fields
        const userObject = {
            firstName: req.body.firstName,
            lastName: req.body.lastName
        }

        //Create the user
        const user = new User(userObject);
        await user.save();
        res.status(201).send(user);
    } catch (error) {
        // console.log(error.message);
        res.status(400).send({Error: error.message});
    }
})

Obviously, that’s not perfect. If I add a new ‘non-protected’ field, I’ve got to remember to come back here and add it to the list of the fields that are being copied over.

The plus side is that failing to remember to do that will cause things to break things right away, which will cause me to come back and fix the bug. Failing to remember won’t result in a vulnerability.