PROGRAMMING × CYBERSECURITY × INACAP

FERR FERNANDA

Programming and cybersecurity student. I build web apps that are secure by default, with tests, CI and security analysis in every repo. Always learning.

Fernanda (Ferr)'s GitHub avatar

`> whoami`

A NEW ANGLE OF CODE

name     →  Ferr (Fernanda)
studies  →  Programming + Cybersecurity @ INACAP
focus    →  secure web apps: tests, CI, CodeQL
into     →  CTF, ethical hacking, AppSec, DevSecOps
status   →  always learning...

I'm Fernanda, but everyone calls me Ferr. I study programming and cybersecurity at INACAP (Chile) and spend my time between CTFs, code and music.

  • APIs with Java + Spring Boot and apps with Flask
  • Frontends with React and vanilla JavaScript
  • Passionate about ethical hacking
  • Digital art & pixel-art lover
PROGRAMMING CYBERSECURITY CTF PLAYER PIXEL ART

`> stack`

EVERYDAY
TOOLS

The combo I use to build, break and learn.


`> currently learning`

ALWAYS
LEARNING

digital art · gaming · music 🎧


`> projects`

A NEW ANGLE
OF CODE

For each project: the problem it solves, how I used each technology and the security decisions I made.

Swagger UI of the clinic API showing the authentication, medical history and patient endpoints
JAVA · REST API

Clinic: patient records API

The problem
Keeping patients and medical histories in spreadsheets exposes sensitive data and gives no control over who can read or change each record.
The solution
A REST API with authentication, three roles (admin, doctor and reception) and Chilean RUT validation: reception registers patients, doctors write the medical history and only admins manage users.

Stack:

  • Java 21
  • Spring Boot
  • PostgreSQL
  • Docker
How I built it

How I used each technology

Spring Boot 3
Layered structure (controller → service → repository), DTOs and a global exception handler with consistent responses.
Spring Security + JWT
Login returns a token and every endpoint requires a role through @PreAuthorize; passwords are stored with BCrypt.
PostgreSQL + Flyway
The schema lives in versioned migrations (tables, indexes and development data), so the database is rebuilt identically in any environment.
Spring Data JPA
Paginated patient search by RUT, name or email without hand-written SQL.
Docker Compose
Starts the API and PostgreSQL with one command; multi-stage image with a non-root user, and Compose stops if a secret is missing.
JUnit 5 + Mockito
22 tests: RUT validator, patient service and a security regression test.
SpringDoc OpenAPI
Swagger UI to document and try every endpoint.

Decisions & security

  • I found that user registration was public and let anyone create an ADMIN account: now only an admin can register users, backed by a test.
  • No default credentials or JWT secret: everything is read from environment variables (.env.example).
  • The initial admin is created from an environment variable and its password is no longer printed in the logs.

CI with mvn verify, Docker build and CodeQL.

MediForm new clinical visit form, patient data section
PYTHON · WEB

MediForm

The problem
On a paper visit form, fields are left blank, abnormal vital signs go unnoticed and finding a previous visit takes time.
The solution
A sectioned clinical form validated on both client and server, with alerts when a vital sign is out of range, automatic age and BMI calculation, and search by name or ID number.

Stack:

  • Python
  • Flask
  • SQLite
  • pytest
How I built it

How I used each technology

Flask + Jinja2
Routes, templates and custom filters (age, BMI and vital-sign status) used directly in the HTML.
SQLite
Serverless persistence; every query is parameterized with ?.
Flask-WTF
CSRF protection on every form that submits data.
Vanilla JavaScript
Live validation and delete confirmation without inline scripts, compatible with a strict CSP.
pytest
19 tests: creating visits, required fields, rejecting requests without a CSRF token, security headers and configuration.
gunicorn
Production server for deployment (Procfile).

Decisions & security

  • Debug off by default and server bound to 127.0.0.1: debug=True on 0.0.0.0 exposes the Werkzeug console to the network.
  • The app refuses to start in production without a SECRET_KEY.
  • CSP, X-Frame-Options, X-Content-Type-Options and Referrer-Policy headers on every response.
  • Educational project: not intended for real clinical data.

CI with ruff and pytest on Python 3.10 and 3.12, plus CodeQL.

Cover of the Banco de Chile cyberattack infographic with the incident figures
REACT · CYBERSEC

Infographic: the 2018 Banco de Chile attack

The problem
Real incidents are studied through long texts and, often, with figures nobody verified.
The solution
An interactive infographic that explains the attack through the CIA triad and labels each claim as documented, hypothesis or unconfirmed, with its sources.

Stack:

  • React
  • Vite
  • CSS
  • GitHub Pages
How I built it

How I used each technology

React
Reusable components: collapsible timeline, CIA triad tabs and a vulnerability → mitigations filter.
JSON + node:test
All content lives in a JSON file and tests check that every source reference and every vulnerability → mitigation link exists.
Vite + GitHub Actions
Production build and automatic deployment to GitHub Pages on every push.
WAI-ARIA
Arrow-key tabs, an accordion with aria-expanded and filters with aria-pressed and aria-live.
Custom CSS
Terminal look, print styles for PDF export and prefers-reduced-motion.

Decisions & security

  • Cross-checking sources fixed the original version: the malware was a wiper (KillDisk), not WannaCry, and unsupported figures were removed.
  • Checked with axe-core: 0 WCAG 2.1 AA violations.

CI with ESLint, data tests and build, plus CodeQL.

Audit simulator showing the three CIA principles and role-based access control
CYBERSEC · CIA

CIA Audit

The problem
How do you stop a student from seeing other students' sensitive data, changing their own grades, or losing exam week to a system outage?
The solution
A simulation of controls for each CIA principle applied to INACAP's systems, in Python and as an interactive HTML simulator.

Stack:

  • Python
  • hashlib
  • HTML
  • pytest
How I built it

How I used each technology

Python (standard library)
One class per principle: role-based access control with an audit log, grade integrity, and the impact of a DDoS with a continuity plan.
hashlib (SHA-256)
A hash per academic record detects any change made outside the system; unauthorized changes are rolled back.
HTML + JavaScript
Interactive version to try roles and resources from the browser.
pytest + ruff
16 tests and linting with security rules (bandit) in CI.

Decisions & security

  • Tests revealed that access control allowed unknown resources (fail-open); it now denies by default.
  • An unkeyed hash detects accidental changes, but against an attacker you need an HMAC: documented as a next step.

CI with ruff, pytest and a full run of the simulation.

Home page of the SQL injection wiki with its learning sections
REACT · CYBERSEC

SQL Injection Wiki

The problem
SQL injection is usually explained with code that intimidates beginners, without making it clear how to prevent it.
The solution
A wiki with analogies, a simulation that executes nothing and a prevention checklist, designed for students and class presentations.

Stack:

  • React
  • Vite
  • CSS
How I built it

How I used each technology

React + Vite
Seven navigable sections, global search with Ctrl/Cmd + K, persistent light/dark mode and PDF export.
Custom simulator
Shows token by token how six classic payloads alter a query, without connecting to any database.
Responsive CSS
Layout for mobile, tablet and desktop without UI libraries.

Decisions & security

  • Teaching the concept without publishing working offensive material.
  • Prevention focuses on parameterized queries; validation and escaping are extra layers.

CI with ESLint and build, plus CodeQL.

Turismo Extremo Sur home page with the Cuernos del Paine
HTML · WEB

Turismo Extremo Sur

The problem
An adventure tourism agency in Punta Arenas had nowhere to show its routes and prices or to receive booking inquiries.
The solution
A fast, dependency-free static site with services, a location map and a validated contact form.

Stack:

  • HTML
  • CSS
  • JavaScript
  • Vercel
How I built it

How I used each technology

Semantic HTML + CSS
Responsive layout with custom properties and a hamburger menu on mobile.
Vanilla JavaScript
Service cards are generated from a data array, the form is validated on the client and animations respect prefers-reduced-motion.
Vercel
Deployment with security headers: CSP, X-Frame-Options, Referrer-Policy and Permissions-Policy.

Decisions & security

  • So the CSP could forbid inline styles, image framing moved from style="..." to the CSSOM.
  • The translucent navbar over the animated aurora failed AA contrast: it now has a dark background.

CI with ESLint, html-validate and asset checks; axe-core with no violations.

TechStore Manager dashboard with the product form and inventory metrics
FULL STACK · REACT

TechStore Manager

The problem
A tech store managed its inventory by hand, with no quick view of stock, total value or products to restock.
The solution
A full-stack system with a REST API for product CRUD and a React dashboard with real-time inventory metrics.

Stack:

  • React
  • Express
  • Axios
  • Vercel
How I built it

How I used each technology

Express
REST API split into routes, controllers and middleware; server-side validation (name, category, price > 0, stock ≥ 0) is the source of truth.
Custom middleware
A logger records the method, path and time of every request.
React
Components (form, table, stats, notifications) with useState, useMemo for the metrics and useCallback for handlers.
Axios
A services/api.js layer centralizes requests and turns errors into readable messages for the user.
Vercel
Frontend and API deployed together: rewrites route /productos to a serverless function that reuses the Express app.
node:test
6 API tests with no extra dependencies: full CRUD, validation, malformed JSON and headers.

Decisions & security

  • CORS can be restricted with the FRONTEND_URL variable, and the X-Powered-By header is gone.
  • The error handler no longer returns err.message to the client in production.
  • listen() only runs when the server is started directly, so the app can be tested.

CI with backend tests, frontend ESLint and build, plus CodeQL.

Medicine catalog with inventory metrics and expiration status
PYTHON · DJANGO

Pharmacy Catalog

The problem
In a pharmacy, expired or soon-to-expire products are caught too late when the inventory lives in a spreadsheet.
The solution
A Django CRUD that shows the inventory value, flags products expiring within 30 days or already expired, and rejects products with a past expiration date.

Stack:

  • Python
  • Django
  • SQLite / PostgreSQL
  • Vercel
How I built it

How I used each technology

Django (generic views)
ListView, CreateView, UpdateView and DeleteView cover the CRUD; the list view computes the inventory metrics.
Django Forms
clean_fecha_vencimiento rejects past dates before saving.
ORM + migrations
SQLite locally and PostgreSQL through DATABASE_URL in production, with no code changes.
WhiteNoise
Serves static files (including the admin) in the serverless environment.
Vercel
On startup, migrations run and the superuser is created from environment variables.
Django tests
Listing, creation, deletion and rejection of expired dates.

Decisions & security

  • SECRET_KEY is required in production: before, if it was missing, an insecure key was used.
  • Secure cookies, HSTS and Referrer-Policy when DEBUG is off; CI runs manage.py check --deploy.

CI with ruff, Django tests and deployment checks, plus CodeQL.

See all my repositories on GitHub ↗


`> stats`

BY THE NUMBERS

Stats are updated automatically from the public GitHub API. If they don't load, you can see them directly on my GitHub profile ↗.


`> contact`

LET'S TALK

Internship, a team CTF, feedback or just saying hi? Here's where to find me.

listening to whatever keeps me in focus mode 🎧