Programming (JavaScript basics) → Lesson 7

Programming Lesson 7: Errors, validation & security mindset

Learn how to think defensively: catch mistakes early, validate inputs, and avoid leaking sensitive data.

Learning goals

  • Understand different kinds of errors and what their messages tell you.
  • Practice defensive programming: validate inputs and fail early with clear messages.
  • Recognize trust boundaries and treat external input as untrusted.
  • Know why secrets must be protected and the conceptual difference between hashing and encryption.

What are errors?

Errors are signs that something unexpected occurred: a thrown error, an exception, or an error message and stack trace produced by the runtime. Stack traces help you find where the error originated (conceptually).

Common categories

  • Syntax errors — code can't be parsed.
  • Runtime errors — code fails while running (e.g., calling a method on undefined).
  • Validation errors / business rules — input doesn't meet expected rules.

Defensive programming

Validate inputs (types, ranges) and fail early with clear messages. Keep checks readable and specific.

function validateNumber(n) {
  if (typeof n !== 'number') throw new Error('Expected number');
  if (!Number.isFinite(n)) throw new Error('Number must be finite');
  return true;
}

try {
  validateNumber('x');
} catch (err) {
  console.error('Validation failed:', err.message);
}

Trust boundaries

Treat any data originating outside your trusted code (user input, files, networks) as untrusted. Sanitize, validate, and avoid implicitly trusting values.

Secrets & safe handling

Never store secrets in plain text. Prefer established safe practices: don't log secrets, and limit who can read them.

Hashing vs encryption (conceptual)

Hashing: one-way transform used for storing password representations — you can't (shouldn't) recover the original from the hash.

Encryption: two-way transform that protects data but allows authorized parties to decrypt and read it.

// pseudocode
// Hashing (store salt + hash(password)) — cannot recover password
// Encryption (encrypt data with key) — can decrypt when key is available

Basic threat thinking

  • Data leaks: avoid exposing sensitive values in logs, error messages, or outputs.
  • Logging secrets accidentally: never log raw tokens or passwords.
  • Unsafe defaults: choose secure defaults and require explicit opt-in for weaker behavior.

Examples

Validating a number input

function safeParseInt(s) {
  const n = Number(s);
  if (!Number.isFinite(n)) throw new Error('Invalid integer');
  return Math.trunc(n);
}

safeParseInt('42'); // 42
// safeParseInt('abc') -> throws Error('Invalid integer')

Throwing an Error with a clear message

function requirePositive(n) {
  if (n <= 0) throw new Error('Expected a positive number');
  return n;
}

Hash vs encrypt (high-level pseudocode)

// hash(password) -> store hash
// encrypt(data, key) -> store ciphertext; can decrypt with key
// Use hashing for passwords; encryption when you need to read data later.

Practice tasks

  1. Write validateAge(value) that returns age if 0 ≤ age ≤ 120, otherwise throws a clear Error.
  2. Write safeParseInt(str) that returns a number or throws; test with '10' → 10, 'x' → Error.
  3. Explain why logging a user-provided token to debug output could be dangerous.

Common mistakes

  • Relying on implicit type coercion for validation.
  • Returning vague error messages that don't help debugging.
  • Logging secrets or including them in error outputs.
  • Using home-grown crypto or rolling your own hash/encryption — prefer vetted libraries and high-level APIs.