Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec63906cb2 | ||
|
|
2d7e67e315 | ||
|
|
539b42dde7 | ||
|
|
73ce30c5b0 | ||
|
|
4915a090a3 | ||
|
|
6d1b114b78 | ||
|
|
a9b6203dec | ||
|
|
36f49b216f | ||
|
|
796245fefe | ||
|
|
0bbb166b32 | ||
|
|
fe6b94aa7f |
@@ -335,40 +335,6 @@ dan is het antwoord `no configuration file provided: not found`. Met
|
|||||||
Pullen zelf werkt anoniem zolang de organisatie public is — zie
|
Pullen zelf werkt anoniem zolang de organisatie public is — zie
|
||||||
[Pullen op een deploy-machine](#pullen-op-een-deploy-machine).
|
[Pullen op een deploy-machine](#pullen-op-een-deploy-machine).
|
||||||
|
|
||||||
### De scan meldt 46 CVE's op postgres:18-alpine
|
|
||||||
|
|
||||||
Dat klopt, en het blijft zo. Ze zitten allemaal in één bestand:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Alle bevindingen komen uit /usr/local/bin/gosu, niets uit de Alpine-pakketten
|
|
||||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
|
|
||||||
aquasec/trivy image --scanners vuln postgres:18-alpine
|
|
||||||
```
|
|
||||||
|
|
||||||
`gosu` is een klein Go-programma dat het postgres-entrypoint gebruikt om van
|
|
||||||
root naar de `postgres`-gebruiker te schakelen. Upstream compileert het met Go
|
|
||||||
1.24.6, en alle meldingen zijn CVE's in die Go-standaardbibliotheek. Je kunt er
|
|
||||||
niets aan verhelpen met een `pull`: dit ís de nieuwste versie.
|
|
||||||
|
|
||||||
Wat wél is gedaan: beide compose-bestanden starten de container met
|
|
||||||
`user: "postgres"`. Het entrypoint roept gosu alleen aan als het als root start
|
|
||||||
(`if [ "$(id -u)" = '0' ]`), dus die tak wordt overgeslagen en de binary draait
|
|
||||||
nooit. De CVE's zijn daarmee onbereikbaar — de scanner kijkt naar wat aanwezig
|
|
||||||
is, niet naar wat uitgevoerd wordt.
|
|
||||||
|
|
||||||
> Dit werkt ook op een volume dat ooit als root is aangemaakt: de
|
|
||||||
> data-directory is dan al eigendom van postgres. Getest met een bestaande
|
|
||||||
> database; die start en de data blijft intact.
|
|
||||||
|
|
||||||
**Waarom geen eigen image zonder gosu.** Dat kan (`FROM postgres:18-alpine` +
|
|
||||||
`RUN rm -f /usr/local/bin/gosu`) en zet de scan op nul, maar je ruilt een
|
|
||||||
onbereikbaar risico voor een echt risico: je moet dat image dan zelf herbouwen
|
|
||||||
bij elke postgres-patch. Precies die achterstand was het probleem toen het image
|
|
||||||
vier maanden op 18.4 stond met 143 bevindingen. Met het officiële image haalt
|
|
||||||
`docker compose pull` de patches vanzelf op, en zodra upstream gosu met een
|
|
||||||
nieuwere Go herbouwt is het gratis opgelost. `test.ps1` waarschuwt daarom bij
|
|
||||||
stap 4 als `postgres:18-alpine` in de registry nieuwer is dan wat je lokaal hebt.
|
|
||||||
|
|
||||||
### PostgreSQL versie-upgrade
|
### PostgreSQL versie-upgrade
|
||||||
|
|
||||||
Twee soorten, met een belangrijk verschil. Kijk eerst welke je voor je hebt:
|
Twee soorten, met een belangrijk verschil. Kijk eerst welke je voor je hebt:
|
||||||
|
|||||||
@@ -2,53 +2,6 @@
|
|||||||
|
|
||||||
## [Niet vrijgegeven]
|
## [Niet vrijgegeven]
|
||||||
|
|
||||||
### Opgelost
|
|
||||||
- **De kaart "Rendement Eigen Vermogen" op Voortgang stond leeg.** Bedrag en
|
|
||||||
percentage waren sinds de eerste commit hardcoded als `—` in de JSX; er zat
|
|
||||||
geen berekening achter en `evRows` werd niet eens uit de context gehaald. Het
|
|
||||||
oogde als een datafout — lege database, mislukte API-call — maar de kaart was
|
|
||||||
nooit afgebouwd. Hij rekent nu met dezelfde formule als het blok "Rendement"
|
|
||||||
op Eigen vermogen: huidige waarde min geïnvesteerde fiat, gedeeld door die
|
|
||||||
inleg. Groen bij winst, rood bij verlies, en `—` zolang er niets is ingelegd,
|
|
||||||
zodat er niet door nul wordt gedeeld
|
|
||||||
- Beide kaarten tonen daarmee hetzelfde totaal. Dat is bewust: twee plekken die
|
|
||||||
"rendement" noemen horen niet twee verschillende getallen te geven
|
|
||||||
|
|
||||||
### Gewijzigd
|
|
||||||
- De achtergrond achter een popup is lichter: `rgba(0,0,0,0.75)` naar `0.45` in
|
|
||||||
de gedeelde `Overlay`. Op 0.75 was van het scherm eronder vrijwel niets meer
|
|
||||||
te zien, terwijl je bij het invullen van een modal juist wilt kunnen nakijken
|
|
||||||
waar je vandaan kwam. Gedimd genoeg om de modal de aandacht te laten houden
|
|
||||||
- De popups van NavBar, Instellingen en Gebruikersbeheer gebruiken de gedeelde
|
|
||||||
`Overlay` niet maar hadden hun eigen backdrop op `rgba(0,0,0,0.55)` met
|
|
||||||
`backdropFilter: blur(4px)`. Alle drie staan nu op dezelfde 0.45, en de blur
|
|
||||||
is eruit: die maakte de achtergrond onscherp in plaats van zichtbaar, en
|
|
||||||
zonder dat weg te halen bleven ze alsnog afwijken van de rest. Vier
|
|
||||||
popup-backdrops, één waarde
|
|
||||||
|
|
||||||
## [1.0.12] - 2026-09-18
|
|
||||||
|
|
||||||
### Beveiliging
|
|
||||||
- **De postgres-container draait niet meer als root.** Beide compose-bestanden
|
|
||||||
starten hem met `user: "postgres"`. Alle 46 bevindingen op `postgres:18-alpine`
|
|
||||||
zitten in één bestand — `/usr/local/bin/gosu`, dat upstream met Go 1.24.6
|
|
||||||
compileert — en het entrypoint roept gosu alleen aan als het als root start.
|
|
||||||
Non-root starten slaat die tak over, dus de binary draait nooit meer. De
|
|
||||||
Alpine-pakketten in dat image zijn zelf schoon
|
|
||||||
- Getest op een bestaande database die ooit als root was aangemaakt: die start
|
|
||||||
als non-root en de data blijft intact. Ook een verse database initialiseert
|
|
||||||
gewoon
|
|
||||||
- Bewust géén eigen postgres-image gebouwd om die binary te verwijderen. Dat zou
|
|
||||||
de scan op nul zetten maar je verplichten het image zelf te herbouwen bij elke
|
|
||||||
postgres-patch — precies de achterstand die het image vier maanden op 18.4 met
|
|
||||||
143 bevindingen hield. De afweging staat in `BEHEER.md`, zodat de volgende
|
|
||||||
scan niet opnieuw tot dezelfde vraag leidt
|
|
||||||
- `test.ps1` waarschuwt bij stap 4 wanneer `postgres:18-alpine` in de registry
|
|
||||||
nieuwer is dan wat er lokaal staat. Die achterstand is het werkelijke risico;
|
|
||||||
het getal in de scan is dat niet
|
|
||||||
|
|
||||||
## [1.0.11] - 2026-09-18
|
|
||||||
|
|
||||||
### Beveiliging
|
### Beveiliging
|
||||||
- **PostgreSQL 18.4 → 18.6.** Het `postgres:18-alpine` image dat in gebruik was
|
- **PostgreSQL 18.4 → 18.6.** Het `postgres:18-alpine` image dat in gebruik was
|
||||||
dateerde van vier maanden geleden. Dat is een patch binnen dezelfde major, dus
|
dateerde van vier maanden geleden. Dat is een patch binnen dezelfde major, dus
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Eigen vermogen · Schulden · Voortgang · Gebruikersbeheer
|
|||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
<img src="docs/screenshot-dashboard.png" alt="VibeFinance dashboard" width="820">
|
<img src="docs/screenshot-dashboard.png" alt="VibeFinance dashboard" width="820">
|
||||||
@@ -190,7 +190,7 @@ build — anders zou de release met een placeholder als notes online komen.
|
|||||||
| 📊 **Dashboard** | KPI's over inleg, vermogen, winst en ROI, de voortgang naar je doel, en de grafiek met vermogen tegen inleg |
|
| 📊 **Dashboard** | KPI's over inleg, vermogen, winst en ROI, de voortgang naar je doel, en de grafiek met vermogen tegen inleg |
|
||||||
| 💰 **Eigen vermogen** | Vermogensposten per categorie beheren + geïnvesteerde fiat historiek |
|
| 💰 **Eigen vermogen** | Vermogensposten per categorie beheren + geïnvesteerde fiat historiek |
|
||||||
| 📉 **Schuld** | Schulden bijhouden per blok met afbetalingsoverzicht |
|
| 📉 **Schuld** | Schulden bijhouden per blok met afbetalingsoverzicht |
|
||||||
| 📈 **Voortgang Schuld & EV** | KPI-kaarten met de verdeling van schuld en EV, het rendement en de netto waarde, plus grafieken en trends van EV en schuld over tijd (apart bij te houden) |
|
| 📈 **Voortgang Schuld & EV** | Grafieken en trends van EV en schuld over tijd (apart bij te houden) |
|
||||||
| 👥 **Gebruikers** | Accounts aanmaken, bewerken en beheren (Admin-rol vereist) |
|
| 👥 **Gebruikers** | Accounts aanmaken, bewerken en beheren (Admin-rol vereist) |
|
||||||
| ⚙️ **Instellingen** | Kleurthema, licht/donker, backup downloaden, backup terugzetten en export naar HTML |
|
| ⚙️ **Instellingen** | Kleurthema, licht/donker, backup downloaden, backup terugzetten en export naar HTML |
|
||||||
|
|
||||||
@@ -202,13 +202,6 @@ inleglijn is per definitie gelijk aan de KPI "Totaal geïnvesteerde fiat" — be
|
|||||||
rekenen het oudste voortgangsmoment als startinleg en tellen daar de stortingen
|
rekenen het oudste voortgangsmoment als startinleg en tellen daar de stortingen
|
||||||
per rij bij op.
|
per rij bij op.
|
||||||
|
|
||||||
Rendement betekent overal hetzelfde: huidige waarde min geïnvesteerde fiat,
|
|
||||||
gedeeld door die inleg. De kaart "Rendement Eigen Vermogen" op Voortgang en het
|
|
||||||
blok "Rendement" op Eigen vermogen tonen daarom hetzelfde totaal — dat laatste
|
|
||||||
splitst het alleen nog uit per categorie. Zolang er geen inleg is vastgelegd
|
|
||||||
staat er `—` in plaats van een percentage, want zonder noemer is er geen
|
|
||||||
rendement te berekenen.
|
|
||||||
|
|
||||||
De x-as noemt maanden (`sep '26`), niet losse datums: de grafiek rekent uit
|
De x-as noemt maanden (`sep '26`), niet losse datums: de grafiek rekent uit
|
||||||
hoeveel labels er naast elkaar passen en toont er zoveel, terugtellend vanaf het
|
hoeveel labels er naast elkaar passen en toont er zoveel, terugtellend vanaf het
|
||||||
laatste punt zodat dat er altijd bij staat. Bij een langere reeks dunt hij
|
laatste punt zodat dat er altijd bij staat. Bij een langere reeks dunt hij
|
||||||
|
|||||||
@@ -15,9 +15,6 @@ services:
|
|||||||
postgres:
|
postgres:
|
||||||
image: postgres:18-alpine
|
image: postgres:18-alpine
|
||||||
container_name: vibefinance-postgres-dev
|
container_name: vibefinance-postgres-dev
|
||||||
# Zie docker-compose.yml: non-root starten omzeilt gosu, waar alle CVE's
|
|
||||||
# van dit image in zitten.
|
|
||||||
user: "postgres"
|
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: ${POSTGRES_DB:-vibefinance}
|
POSTGRES_DB: ${POSTGRES_DB:-vibefinance}
|
||||||
POSTGRES_USER: ${POSTGRES_USER:-vibefinance}
|
POSTGRES_USER: ${POSTGRES_USER:-vibefinance}
|
||||||
|
|||||||
@@ -16,13 +16,6 @@ services:
|
|||||||
image: postgres:18-alpine
|
image: postgres:18-alpine
|
||||||
container_name: vibefinance_postgres
|
container_name: vibefinance_postgres
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
# Meteen als postgres draaien in plaats van als root. Het entrypoint schakelt
|
|
||||||
# anders zelf terug met `gosu`, en juist die binary bevat alle 46 CVE's van
|
|
||||||
# dit image (Go stdlib 1.24.6, meegecompileerd door upstream). Start de
|
|
||||||
# container al als postgres, dan slaat het entrypoint die tak over en wordt
|
|
||||||
# gosu nooit uitgevoerd. Werkt ook op een volume dat ooit als root is
|
|
||||||
# aangemaakt — de data-directory is dan al van postgres.
|
|
||||||
user: "postgres"
|
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: ${POSTGRES_DB:-vibefinance}
|
POSTGRES_DB: ${POSTGRES_DB:-vibefinance}
|
||||||
POSTGRES_USER: ${POSTGRES_USER:-vibefinance}
|
POSTGRES_USER: ${POSTGRES_USER:-vibefinance}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vibefinance-frontend",
|
"name": "vibefinance-frontend",
|
||||||
"version": "1.0.13-dev",
|
"version": "1.0.11-dev",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ export default function GebruikersBeheer({ onClose }) {
|
|||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
style={{
|
style={{
|
||||||
position: "fixed", inset: 0, zIndex: 400,
|
position: "fixed", inset: 0, zIndex: 400,
|
||||||
background: "rgba(0,0,0,0.45)",
|
background: "rgba(0,0,0,0.55)", backdropFilter: "blur(4px)",
|
||||||
display: "flex", alignItems: "center", justifyContent: "center",
|
display: "flex", alignItems: "center", justifyContent: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export default function InstellingenModal({ onClose }) {
|
|||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
style={{
|
style={{
|
||||||
position: "fixed", inset: 0, zIndex: 400,
|
position: "fixed", inset: 0, zIndex: 400,
|
||||||
background: "rgba(0,0,0,0.45)",
|
background: "rgba(0,0,0,0.55)", backdropFilter: "blur(4px)",
|
||||||
display: "flex", alignItems: "center", justifyContent: "center",
|
display: "flex", alignItems: "center", justifyContent: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ function OverPopup({ onClose, nieuweVersie, T }) {
|
|||||||
{/* Overlay */}
|
{/* Overlay */}
|
||||||
<div onClick={onClose} style={{
|
<div onClick={onClose} style={{
|
||||||
position: "fixed", inset: 0, zIndex: 490,
|
position: "fixed", inset: 0, zIndex: 490,
|
||||||
background: "rgba(0,0,0,0.45)",
|
background: "rgba(0,0,0,0.55)", backdropFilter: "blur(4px)",
|
||||||
display: "flex", alignItems: "center", justifyContent: "center",
|
display: "flex", alignItems: "center", justifyContent: "center",
|
||||||
}}>
|
}}>
|
||||||
{/* Modal */}
|
{/* Modal */}
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ export function Overlay({ children, onClose, zIndex = 400 }) {
|
|||||||
onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}
|
onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}
|
||||||
style={{
|
style={{
|
||||||
position: "fixed", inset: 0,
|
position: "fixed", inset: 0,
|
||||||
background: "rgba(0,0,0,0.45)",
|
background: "rgba(0,0,0,0.75)",
|
||||||
zIndex,
|
zIndex,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -237,22 +237,13 @@ export default function VoortgangTab() {
|
|||||||
const [showAdd, setShowAdd] = useState(false);
|
const [showAdd, setShowAdd] = useState(false);
|
||||||
const [editRow, setEditRow] = useState(null);
|
const [editRow, setEditRow] = useState(null);
|
||||||
const {
|
const {
|
||||||
totEV, totSchuld, cats, catTotals, evRows, schuldBlokken, schuldHistory,
|
totEV, totSchuld, catTotals, schuldBlokken, schuldHistory,
|
||||||
evVoortgangHistory, setEvVoortgangHistory,
|
evVoortgangHistory, setEvVoortgangHistory,
|
||||||
data, setData, schuld, locked, darkMode, T,
|
data, setData, schuld, locked, darkMode, T,
|
||||||
} = useApp();
|
} = useApp();
|
||||||
|
|
||||||
const nettoWaarde = totEV - totSchuld;
|
const nettoWaarde = totEV - totSchuld;
|
||||||
|
|
||||||
// Rendement t.o.v. de geinvesteerde fiat, zelfde formule als op de EV-tab.
|
|
||||||
const totGeinvesteerd = cats.reduce(
|
|
||||||
(a, c) => a + evRows.reduce((b, r) => b + (r[c.id] || 0), 0),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
const rendementBedrag = totGeinvesteerd > 0 ? totEV - totGeinvesteerd : null;
|
|
||||||
const rendementPct = totGeinvesteerd > 0 ? (rendementBedrag / totGeinvesteerd) * 100 : null;
|
|
||||||
const rendementKleur = rendementBedrag === null ? T.muted : rendementBedrag >= 0 ? GREEN : RED;
|
|
||||||
|
|
||||||
const schuldCatsDash = schuldBlokken.map((b) => ({
|
const schuldCatsDash = schuldBlokken.map((b) => ({
|
||||||
naam: b.naam, color: b.color,
|
naam: b.naam, color: b.color,
|
||||||
subtot: b.items.reduce((a, i) => a + (i.waarde || 0), 0),
|
subtot: b.items.reduce((a, i) => a + (i.waarde || 0), 0),
|
||||||
@@ -386,15 +377,11 @@ export default function VoortgangTab() {
|
|||||||
<div style={{ fontWeight: 700, fontSize: 12, letterSpacing: "0.08em", textTransform: "uppercase", color: "#f59e0b", marginBottom: 12 }}>Rendement Eigen Vermogen</div>
|
<div style={{ fontWeight: 700, fontSize: 12, letterSpacing: "0.08em", textTransform: "uppercase", color: "#f59e0b", marginBottom: 12 }}>Rendement Eigen Vermogen</div>
|
||||||
<div style={{ padding: "10px 0", borderBottom: `1px solid ${T.border}` }}>
|
<div style={{ padding: "10px 0", borderBottom: `1px solid ${T.border}` }}>
|
||||||
<div style={{ fontSize: 11, color: T.muted, marginBottom: 4 }}>Bedrag</div>
|
<div style={{ fontSize: 11, color: T.muted, marginBottom: 4 }}>Bedrag</div>
|
||||||
<div style={{ fontSize: 15, fontWeight: 700, color: rendementKleur }}>
|
<div style={{ fontSize: 15, fontWeight: 700, color: T.muted }}>—</div>
|
||||||
{rendementBedrag === null ? "—" : fmt(rendementBedrag)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div style={{ padding: "10px 0" }}>
|
<div style={{ padding: "10px 0" }}>
|
||||||
<div style={{ fontSize: 11, color: T.muted, marginBottom: 4 }}>Percentage</div>
|
<div style={{ fontSize: 11, color: T.muted, marginBottom: 4 }}>Percentage</div>
|
||||||
<div style={{ fontSize: 15, fontWeight: 700, color: rendementKleur }}>
|
<div style={{ fontSize: 15, fontWeight: 700, color: T.muted }}>—</div>
|
||||||
{rendementPct === null ? "—" : `${rendementPct >= 0 ? "+" : ""}${rendementPct.toFixed(1)}%`}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</VCard>
|
</VCard>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vibefinance",
|
"name": "vibefinance",
|
||||||
"version": "1.0.13",
|
"version": "1.0.11",
|
||||||
"description": "VibeFinance — persoonlijk vermogensbeheer",
|
"description": "VibeFinance — persoonlijk vermogensbeheer",
|
||||||
"private": true,
|
"private": true,
|
||||||
"repository": "https://vibehoogie.duckdns.org/vibe/VibeFinance",
|
"repository": "https://vibehoogie.duckdns.org/vibe/VibeFinance",
|
||||||
|
|||||||
@@ -124,23 +124,6 @@ if (-not $SkipScan) {
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
Write-Host "Imagescan schoon ($ernst)" -ForegroundColor Green
|
Write-Host "Imagescan schoon ($ernst)" -ForegroundColor Green
|
||||||
|
|
||||||
# Het postgres-image scannen we niet als gate: alle bevindingen daarin zitten
|
|
||||||
# in /usr/local/bin/gosu, en die binary wordt nooit uitgevoerd omdat de
|
|
||||||
# compose-bestanden de container al als `postgres` starten (zie BEHEER.md).
|
|
||||||
# Wat wél misgaat is achterstand: het image stond vier maanden op 18.4 met
|
|
||||||
# 143 bevindingen omdat niemand pullde. Daar waarschuwen we voor — falen zou
|
|
||||||
# onterecht zijn, want een release van de app hangt er niet van af.
|
|
||||||
$pgTag = "postgres:18-alpine"
|
|
||||||
$lokaal = (docker image inspect $pgTag --format '{{index .RepoDigests 0}}' 2>$null)
|
|
||||||
if ($lokaal) {
|
|
||||||
$remote = (docker buildx imagetools inspect $pgTag --format '{{.Manifest.Digest}}' 2>$null)
|
|
||||||
if ($remote -and $lokaal -notlike "*$remote*") {
|
|
||||||
Write-Host "Let op: $pgTag is nieuwer in de registry — draai 'docker compose pull postgres'." -ForegroundColor Yellow
|
|
||||||
} else {
|
|
||||||
Write-Host "$pgTag is actueel" -ForegroundColor Green
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Write-Step "[4/5] Imagescan overgeslagen (-SkipScan)"
|
Write-Step "[4/5] Imagescan overgeslagen (-SkipScan)"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user