fix(security-guards): sync gitignore guard with the Cover_*.* and cv/*.txt rules

Two personal-data ignore rules existed in .gitignore but not in
REQUIRED_IGNORE_RULES, so a change weakening either would have passed CI:
cover_letters/Cover_*.* (the uppercase naming variant /apply recognizes)
and cv/*.txt (ATS text extractions of tailored CVs).

Also: regression tests pinning #252's ragged-row bounds fix in
convert_salary_excel.py (mutation-verified), and removal of the vestigial
cover_letters/OpenFonts/cover.cls, which since #252's rename ambiguously
declared the same class as the real cover.cls (zero references; cover
letter re-compiled and page-verified after removal).

Guard-list gap surfaced by CodeRabbit's review on jakob1379's Nix demo
fork PR (jakob1379/ai-job-search#1).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Mads Lorentzen
2026-07-31 12:55:09 +02:00
co-authored by Claude Fable 5
parent f220d92495
commit 2c41210019
4 changed files with 54 additions and 101 deletions
+17
View File
@@ -13,6 +13,23 @@ per-file diff commands.
## [Unreleased]
### Security & privacy
- **The gitignore guard now covers two more personal-data rules** - `security_guards.py`
requires `cover_letters/Cover_*.*` (the uppercase cover-letter naming variant `/apply`
recognizes) and `cv/*.txt` (ATS text extractions of tailored CVs) in `.gitignore`, so a
future change weakening either rule fails CI instead of silently making personal files
trackable. Both rules were already present in `.gitignore`; only the guard lagged.
### Fixed
- Removed the vestigial `cover_letters/OpenFonts/cover.cls` - an unreferenced remnant of
the original font bundle that, since #252's class rename, ambiguously declared the same
`cover` class as the real `cover_letters/cover.cls`.
- Added regression tests pinning #252's ragged-row bounds fix in
`tools/convert_salary_excel.py` (dimension-less workbooks read in `read_only` mode
yield rows shorter than the header).
## [1.1.0] - 2026-07-30
### Security & privacy
-101
View File
@@ -1,101 +0,0 @@
% Intro Options
\ProvidesClass{cover}[2024/04/30 Cover letter class]
\NeedsTeXFormat{LaTeX2e}
\DeclareOption{print}{\def\@cv@print{}}
\DeclareOption*{%
\PassOptionsToClass{\CurrentOption}{article}
}
\ProcessOptions\relax
\LoadClass{article}
% Package Imports
\usepackage[hmargin=2.54cm, vmargin=2.54cm]{geometry}
\usepackage[hidelinks]{hyperref}
\usepackage[usenames,dvipsnames]{xcolor}
\usepackage{titlesec}
\usepackage[absolute]{textpos}
\usepackage{fontspec,xltxtra,xunicode}
% Publications
\usepackage{cite}
\renewcommand\refname{\vskip -1.5cm}
% Color definitions
\usepackage[usenames,dvipsnames]{xcolor}
\definecolor{date}{HTML}{666666}
\definecolor{primary}{HTML}{2b2b2b}
\definecolor{headings}{HTML}{6A6A6A}
\definecolor{subheadings}{HTML}{333333}
% Set main fonts
\usepackage{fontspec}
\setmainfont[Color=primary, Path = OpenFonts/fonts/lato/,BoldItalicFont=Lato-RegIta,BoldFont=Lato-Reg,ItalicFont=Lato-LigIta]{Lato-Lig}
\setsansfont[Scale=MatchLowercase,Mapping=tex-text, Path = OpenFonts/fonts/raleway/]{Raleway-ExtraLight}
% Date command
\usepackage[absolute]{textpos}
% \usepackage[UKenglish]{isodate}
\setlength{\TPHorizModule}{1mm}
\setlength{\TPVertModule}{1mm}
\newcommand{\lastupdated}{\begin{textblock}{60}(155,5)
\color{date}\fontspec[Path = fonts/raleway/]{Raleway-ExtraLight}\fontsize{8pt}{10pt}\selectfont
Last Updated on \today
\end{textblock}}
% Name command
\newcommand{\namesection}[3]{
\centering{
\fontsize{40pt}{60pt}
\fontspec[Path = fonts/lato/]{Lato-Hai}\selectfont #1
\fontspec[Path = fonts/lato/]{Lato-Lig}\selectfont #2
} \\[5pt]
\centering{
\color{headings}
\fontspec[Path = fonts/raleway/]{Raleway-Medium}\fontsize{11pt}{14pt}\selectfont #3}
\noindent\makebox[\linewidth]{\color{headings}\rule{\paperwidth}{0.0pt}}
\vspace{0pt}
}
% Section seperators
\usepackage{titlesec}
\titlespacing{\section}{0pt}{0pt}{0pt}
\titlespacing{\subsection}{0pt}{0pt}{0pt}
\newcommand{\sectionsep}{\vspace{8pt}}
% Headings command
\titleformat{\section}{\color{headings}
\scshape\fontspec[Path = fonts/lato/]{Lato-Lig}\fontsize{16pt}{24pt}\selectfont \raggedright\uppercase}{}{0em}{}
% Subeadings command
\titleformat{\subsection}{
\color{subheadings}\fontspec[Path = fonts/lato/]{Lato-Bol}\fontsize{12pt}{12pt}\selectfont\bfseries\uppercase}{}{0em}{}
\newcommand{\runsubsection}[1]{
\color{subheadings}\fontspec[Path = fonts/lato/]{Lato-Bol}\fontsize{12pt}{12pt}\selectfont\bfseries\uppercase {#1} \normalfont}
% Descriptors command
\newcommand{\descript}[1]{
\color{subheadings}\raggedright\scshape\fontspec[Path = fonts/raleway/]{Raleway-Medium}\fontsize{11pt}{13pt}\selectfont {#1 \\} \normalfont}
% Location command
\newcommand{\location}[1]{
\color{headings}\raggedright\fontspec[Path = fonts/raleway/]{Raleway-Medium}\fontsize{10pt}{12pt}\selectfont {#1\\} \normalfont}
% Bullet Lists with fewer gaps command
\newenvironment{tightemize}{
\vspace{-\topsep}\begin{itemize}\itemsep1pt \parskip0pt \parsep0pt}
{\end{itemize}\vspace{-\topsep}}
% Cover Letter
\newcommand{\companyname}[1]{\raggedright\fontspec[Path = fonts/lato/]{Lato-Bol}\fontsize{12pt}{14pt}\selectfont {#1 \\} \normalfont}
\newcommand{\companyaddress}[1]{\raggedright\fontspec[Path = fonts/raleway/]{Raleway-Medium}\fontsize{11pt}{13pt}\selectfont {#1 \\}\mbox{}\\\mbox{}\\ \normalfont}
\newcommand{\currentdate}[1]{\raggedleft\fontspec[Path = fonts/raleway/]{Raleway-Medium}\fontsize{11pt}{13pt}\selectfont {#1 \\} \normalfont}
% Letter content command
\newcommand{\lettercontent}[1]{\raggedright\fontspec[Path = fonts/raleway/]{Raleway-Medium}\fontsize{11pt}{13pt}\selectfont {#1 \\}\mbox{}\\ \normalfont}
\newcommand{\closing}[1]{\raggedright\fontspec[Path = fonts/raleway/]{Raleway-Medium}\fontsize{11pt}{13pt}\selectfont {#1 \\}\mbox{}\\\mbox{}\\ \normalfont}
\newcommand{\signature}[1]{\raggedright\fontspec[Path = fonts/raleway/]{Raleway-Medium}\fontsize{11pt}{13pt}\selectfont {#1 \\} \normalfont}
+33
View File
@@ -120,6 +120,39 @@ class DetectColumnTypeTests(unittest.TestCase):
self.assertEqual(len(companies), 1)
self.assertEqual(companies[0]["city"], "Aarhus")
def test_parse_sheet_handles_ragged_rows(self):
# openpyxl's read_only mode yields ragged tuples for dimension-less
# workbooks: a row can be shorter than the header. A company row that
# omits its city and category cells must parse without an IndexError,
# be retained, and get an empty city.
ws = FakeWorksheet([
("Company", "City", "Engineering Count", "Engineering Index"),
("Example Corp",),
("Other Corp", "Aarhus", 12, 105.5),
])
companies = parse_sheet(ws)
self.assertEqual(len(companies), 2)
self.assertEqual(companies[0]["company"], "Example Corp")
self.assertEqual(companies[0]["city"], "")
self.assertEqual(companies[0]["categories"], {})
self.assertEqual(companies[1]["categories"]["engineering"], {"count": 12, "index": 105.5})
def test_parse_sheet_skips_row_shorter_than_company_column(self):
# A ragged row that ends before the company column has no company cell
# at all; it must be skipped, not crash the parse.
ws = FakeWorksheet([
("Notes", "Company", "Salary Index"),
("stray",),
("", "Example Corp", 105.5),
])
companies = parse_sheet(ws)
self.assertEqual(len(companies), 1)
self.assertEqual(companies[0]["company"], "Example Corp")
def test_skips_free_text_column(self):
# A free-text "Notes" column must not become a bogus salary category.
ws = FakeWorksheet([
+4
View File
@@ -50,7 +50,11 @@ REQUIRED_IGNORE_RULES = [
"**/job_scraper/seen_jobs.json",
"cv/main_*.*",
"!cv/main_example.tex",
# ATS text extractions (/apply step 5d) carry the CV's full text.
"cv/*.txt",
"cover_letters/cover_*.*",
# /apply also recognizes the uppercase Cover_* naming variant.
"cover_letters/Cover_*.*",
"documents/cv/**",
"documents/linkedin/**",
"documents/diplomas/**",