mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 08:36:25 +00:00
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:
co-authored by
Claude Fable 5
parent
f220d92495
commit
2c41210019
@@ -13,6 +13,23 @@ per-file diff commands.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [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
|
## [1.1.0] - 2026-07-30
|
||||||
|
|
||||||
### Security & privacy
|
### Security & privacy
|
||||||
|
|||||||
@@ -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}
|
|
||||||
@@ -120,6 +120,39 @@ class DetectColumnTypeTests(unittest.TestCase):
|
|||||||
self.assertEqual(len(companies), 1)
|
self.assertEqual(len(companies), 1)
|
||||||
self.assertEqual(companies[0]["city"], "Aarhus")
|
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):
|
def test_skips_free_text_column(self):
|
||||||
# A free-text "Notes" column must not become a bogus salary category.
|
# A free-text "Notes" column must not become a bogus salary category.
|
||||||
ws = FakeWorksheet([
|
ws = FakeWorksheet([
|
||||||
|
|||||||
@@ -50,7 +50,11 @@ REQUIRED_IGNORE_RULES = [
|
|||||||
"**/job_scraper/seen_jobs.json",
|
"**/job_scraper/seen_jobs.json",
|
||||||
"cv/main_*.*",
|
"cv/main_*.*",
|
||||||
"!cv/main_example.tex",
|
"!cv/main_example.tex",
|
||||||
|
# ATS text extractions (/apply step 5d) carry the CV's full text.
|
||||||
|
"cv/*.txt",
|
||||||
"cover_letters/cover_*.*",
|
"cover_letters/cover_*.*",
|
||||||
|
# /apply also recognizes the uppercase Cover_* naming variant.
|
||||||
|
"cover_letters/Cover_*.*",
|
||||||
"documents/cv/**",
|
"documents/cv/**",
|
||||||
"documents/linkedin/**",
|
"documents/linkedin/**",
|
||||||
"documents/diplomas/**",
|
"documents/diplomas/**",
|
||||||
|
|||||||
Reference in New Issue
Block a user