Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLeitwolf112026-11-06 15:00:16 +0300
committerLeitwolf112026-11-06 15:00:16 +0300
commit8f7f90b57ecc0bf5fba555b389c149ac068929b0 (patch)
tree28fd82e54ab5f977e114b309b8af425fabccde35
parent39a301172c3752e7b22f9ea2c3a4b1d4c73cfeb1 (diff)
downloadprogmaweb-8f7f90b57ecc0bf5fba555b389c149ac068929b0.tar.gz
Make the policies say what ViGet and support do today
The review found four promises the policies made beyond what exists: - ViGet takes no uploads from accounts yet; only Progmasoft publishes, through a server command. The Privacy Policy now says so, and that ViGet will record the IP address of each upload, for that one purpose and for the same time, once uploads open. ops/README.md tells the upload endpoint to do exactly that. - A yank leaves a JAR served by nginx and cached by Cloudflare for a year. The Security Policy says a malicious version is taken off ViGet and kept out of reach as evidence; ops/README.md gives the four steps: yank, move the JAR into a root-only quarantine, purge the Cloudflare cache, close the account. - The API cannot delete an account. ops/README.md gives the procedure support follows on a request from the account's address: a dump, then one transaction that removes the OpenIddict tokens and authorizations of the account, the applications it alone owns, and the account with its sessions and ownerships. - Pages allow Cloudflare's analytics script besides their own, and the Security Policy now names it. ops/README.md also records the nginx logs without client addresses on both servers, which make the Privacy Policy's statement about web-server logs true.
-rw-r--r--apps/web/lib/policies/de-DE.ts8
-rw-r--r--apps/web/lib/policies/en-US.ts8
-rw-r--r--apps/web/lib/policies/he-IL.ts8
-rw-r--r--apps/web/lib/policies/ru-RU.ts8
-rw-r--r--apps/web/tests/policies.test.mjs6
-rw-r--r--ops/README.md55
6 files changed, 76 insertions, 17 deletions
diff --git a/apps/web/lib/policies/de-DE.ts b/apps/web/lib/policies/de-DE.ts
index 8ae7d2e..b91d3ac 100644
--- a/apps/web/lib/policies/de-DE.ts
+++ b/apps/web/lib/policies/de-DE.ts
@@ -29,7 +29,7 @@ export const deDE: PolicyMessages = {
{
heading: "Ihre IP-Adresse",
paragraphs: [
- "Wir speichern Ihre IP-Adresse nur zu einem einzigen Zweck. Wenn Sie ein Paket oder ein Plugin auf ViGet hochladen, speichern wir die IP-Adresse, von der der Upload kam, zusammen mit dem Upload. Stellt sich heraus, dass der Upload Schadsoftware enthält, verwenden wir diese Adresse, um festzustellen, woher er kam, und übergeben sie den zuständigen Behörden. Für nichts anderes verwenden wir sie.",
+ "Wir speichern Ihre IP-Adresse nur zu einem einzigen Zweck. ViGet nimmt noch keine Uploads von Konten an: Bisher veröffentlicht nur Progmasoft dort. Sobald es das tut, speichert ViGet die IP-Adresse, von der ein Upload kam, zusammen mit dem Upload. Stellt sich heraus, dass ein Upload Schadsoftware enthält, verwenden wir diese Adresse, um festzustellen, woher er kam, und übergeben sie den zuständigen Behörden. Für nichts anderes verwenden wir sie.",
"Wir speichern keine IP-Adressen zu Ihrem Konto, zu Ihren Sitzungen oder in den Zugriffsprotokollen unserer Webserver.",
"Um das Erraten von Passwörtern zu bremsen, zählt der Anmeldedienst einige Minuten lang Anfragen je Adresse. Diese Zählung liegt nur im Arbeitsspeicher und wird nie gespeichert.",
],
@@ -57,7 +57,7 @@ export const deDE: PolicyMessages = {
{
heading: "Wie lange wir speichern",
paragraphs: [
- "Kontodaten bleiben gespeichert, solange das Konto besteht. Eine Sitzung wird entfernt, sobald sie beendet ist. Die IP-Adresse eines ViGet-Uploads bleibt gespeichert, solange der Upload auf ViGet liegt, und ein Jahr nach seiner Entfernung, damit auch einer spät eingehenden Meldung noch nachgegangen werden kann.",
+ "Kontodaten bleiben gespeichert, solange das Konto besteht. Eine Sitzung wird entfernt, sobald sie beendet ist. Die IP-Adresse eines ViGet-Uploads wird gespeichert, solange der Upload auf ViGet liegt, und ein Jahr nach seiner Entfernung, damit auch einer spät eingehenden Meldung noch nachgegangen werden kann.",
],
},
{
@@ -89,14 +89,14 @@ export const deDE: PolicyMessages = {
{
heading: "Verbindungen",
paragraphs: [
- "Jede Seite von Progmasoft wird nur über HTTPS ausgeliefert, und Browser werden angewiesen, dabei zu bleiben (HSTS). Jede Seite trägt eine Content Security Policy, die nur die Skripte zulässt, die die Seite selbst nennt.",
+ "Jede Seite von Progmasoft wird nur über HTTPS ausgeliefert, und Browser werden angewiesen, dabei zu bleiben (HSTS). Jede Seite trägt eine Content Security Policy, die nur die Skripte zulässt, die die Seite selbst nennt, und das Analyseskript von Cloudflare.",
],
},
{
heading: "Schadsoftware auf ViGet",
paragraphs: [
"Es ist verboten, Schadsoftware auf ViGet hochzuladen: Software, die den Computern, auf denen sie läuft, schadet, sie ausspäht oder die Kontrolle über sie übernimmt, oder die etwas anderes tut, als sie angibt.",
- "Wenn wir solche Software finden oder auf sie hingewiesen werden, entfernen wir sie, schließen das Konto, das sie hochgeladen hat, und übergeben die IP-Adresse des Uploads den zuständigen Behörden, wie es die Datenschutzerklärung beschreibt.",
+ "Wenn wir solche Software finden oder auf sie hingewiesen werden, nehmen wir sie von ViGet: Ihre Dateien werden nicht mehr ausgeliefert und nur noch als Beweismittel unzugänglich aufbewahrt. Wir schließen das Konto, das sie hochgeladen hat, und übergeben die IP-Adresse des Uploads den zuständigen Behörden, wie es die Datenschutzerklärung beschreibt.",
"Jedes Plugin, das ViGet ausliefert, ist mit seinem SHA-256-Digest verzeichnet, und Tazel prüft den Digest, bevor es das Plugin verwendet.",
],
},
diff --git a/apps/web/lib/policies/en-US.ts b/apps/web/lib/policies/en-US.ts
index 01536ca..198deda 100644
--- a/apps/web/lib/policies/en-US.ts
+++ b/apps/web/lib/policies/en-US.ts
@@ -29,7 +29,7 @@ export const enUS: PolicyMessages = {
{
heading: "Your IP address",
paragraphs: [
- "We keep your IP address for one purpose only. When you upload a package or a plugin to ViGet, we record the IP address the upload came from, together with the upload. If the upload turns out to contain malicious software, we use that address to establish where it came from and give it to the competent authorities. We use it for nothing else.",
+ "We keep your IP address for one purpose only. ViGet does not yet take uploads from accounts: only Progmasoft publishes to it. Once it does, ViGet will record the IP address each upload came from, together with the upload. If an upload turns out to contain malicious software, we will use that address to establish where it came from and give it to the competent authorities. We will use it for nothing else.",
"We do not keep IP addresses with your account, with your sessions or in the access logs of our web servers.",
"To slow down password guessing, the sign-in service counts requests per address for a few minutes. The count is held in memory and never written down.",
],
@@ -57,7 +57,7 @@ export const enUS: PolicyMessages = {
{
heading: "How long we keep it",
paragraphs: [
- "Account data is kept for as long as the account exists. A session is removed once it has ended. The IP address of a ViGet upload is kept while the upload is on ViGet and for one year after it was removed, so that a report that arrives late can still be followed.",
+ "Account data is kept for as long as the account exists. A session is removed once it has ended. The IP address of a ViGet upload will be kept while the upload is on ViGet and for one year after it was removed, so that a report that arrives late can still be followed.",
],
},
{
@@ -89,14 +89,14 @@ export const enUS: PolicyMessages = {
{
heading: "Connections",
paragraphs: [
- "Every Progmasoft site is served only over HTTPS, and browsers are told to keep it that way (HSTS). Every page carries a Content Security Policy, which allows only the scripts the page itself names.",
+ "Every Progmasoft site is served only over HTTPS, and browsers are told to keep it that way (HSTS). Every page carries a Content Security Policy, which allows only the scripts the page itself names and the analytics script of Cloudflare.",
],
},
{
heading: "Malicious software on ViGet",
paragraphs: [
"Uploading malicious software to ViGet is forbidden: software that harms, spies on or takes control of the computers it runs on, or that does something other than what it says it does.",
- "When we find such software or are told about it, we remove it, close the account that uploaded it, and give the IP address of the upload to the competent authorities, as the Privacy Policy describes.",
+ "When we find such software or are told about it, we take it off ViGet: its files are no longer served, and are kept out of reach only as evidence. We close the account that uploaded it and give the IP address of the upload to the competent authorities, as the Privacy Policy describes.",
"Every plugin ViGet serves is listed with its SHA-256 digest, and Tazel checks the digest before it uses the plugin.",
],
},
diff --git a/apps/web/lib/policies/he-IL.ts b/apps/web/lib/policies/he-IL.ts
index e36d680..06f09dd 100644
--- a/apps/web/lib/policies/he-IL.ts
+++ b/apps/web/lib/policies/he-IL.ts
@@ -28,7 +28,7 @@ export const heIL: PolicyMessages = {
{
heading: "כתובת ה־IP שלך",
paragraphs: [
- "אנחנו שומרים את כתובת ה־IP שלך למטרה אחת בלבד. כשמעלים חבילה או תוסף ל־ViGet, אנחנו רושמים את כתובת ה־IP שממנה הגיעה ההעלאה, יחד עם ההעלאה. אם מתברר שההעלאה מכילה תוכנה זדונית, אנחנו משתמשים בכתובת הזו כדי לקבוע מאין היא הגיעה, ומוסרים אותה לרשויות המוסמכות. לשום דבר אחר איננו משתמשים בה.",
+ "אנחנו שומרים את כתובת ה־IP שלך למטרה אחת בלבד. ‏ViGet עדיין אינו מקבל העלאות מחשבונות: רק Progmasoft מפרסמת בו. כשזה יתאפשר, ‏ViGet ירשום את כתובת ה־IP שממנה הגיעה כל העלאה, יחד עם ההעלאה. אם יתברר שהעלאה מכילה תוכנה זדונית, נשתמש בכתובת הזו כדי לקבוע מאין היא הגיעה, ונמסור אותה לרשויות המוסמכות. לשום דבר אחר לא נשתמש בה.",
"איננו שומרים כתובות IP עם החשבון שלך, עם ההתחברויות שלך או ביומני הגישה של שרתי האינטרנט שלנו.",
"כדי להאט ניחוש סיסמאות, שירות ההתחברות סופר בקשות לפי כתובת במשך כמה דקות. הספירה נשמרת בזיכרון בלבד ולעולם אינה נכתבת.",
],
@@ -56,7 +56,7 @@ export const heIL: PolicyMessages = {
{
heading: "כמה זמן אנחנו שומרים",
paragraphs: [
- "נתוני החשבון נשמרים כל עוד החשבון קיים. התחברות נמחקת ברגע שהסתיימה. כתובת ה־IP של העלאה ל־ViGet נשמרת כל עוד ההעלאה נמצאת ב־ViGet ועוד שנה אחרי שהוסרה, כדי שגם בדיווח שמגיע באיחור עדיין יהיה אפשר לטפל.",
+ "נתוני החשבון נשמרים כל עוד החשבון קיים. התחברות נמחקת ברגע שהסתיימה. כתובת ה־IP של העלאה ל־ViGet תישמר כל עוד ההעלאה נמצאת ב־ViGet ועוד שנה אחרי שהוסרה, כדי שגם בדיווח שמגיע באיחור עדיין יהיה אפשר לטפל.",
],
},
{
@@ -88,14 +88,14 @@ export const heIL: PolicyMessages = {
{
heading: "חיבורים",
paragraphs: [
- "כל אתר של Progmasoft מוגש רק ב־HTTPS, והדפדפנים מתבקשים להמשיך כך (HSTS). כל דף נושא Content Security Policy, שמתירה רק את הסקריפטים שהדף עצמו מציין.",
+ "כל אתר של Progmasoft מוגש רק ב־HTTPS, והדפדפנים מתבקשים להמשיך כך (HSTS). כל דף נושא Content Security Policy, שמתירה רק את הסקריפטים שהדף עצמו מציין ואת סקריפט האנליטיקה של Cloudflare.",
],
},
{
heading: "תוכנה זדונית ב־ViGet",
paragraphs: [
"אסור להעלות ל־ViGet תוכנה זדונית: תוכנה שפוגעת במחשבים שהיא רצה עליהם, מרגלת אחריהם או משתלטת עליהם, או שעושה משהו אחר ממה שהיא מצהירה.",
- "כשאנחנו מוצאים תוכנה כזו או מקבלים עליה דיווח, אנחנו מסירים אותה, סוגרים את החשבון שהעלה אותה, ומוסרים את כתובת ה־IP של ההעלאה לרשויות המוסמכות, כפי שמתואר במדיניות הפרטיות.",
+ "כשאנחנו מוצאים תוכנה כזו או מקבלים עליה דיווח, אנחנו מסירים אותה מ־ViGet: הקבצים שלה כבר אינם מוגשים, והם נשמרים במקום שאין אליו גישה רק כראיה. אנחנו סוגרים את החשבון שהעלה אותה ומוסרים את כתובת ה־IP של ההעלאה לרשויות המוסמכות, כפי שמתואר במדיניות הפרטיות.",
"כל תוסף ש־ViGet מגיש רשום עם תמצית ה־SHA-256 שלו, ו־Tazel בודק את התמצית לפני שהוא משתמש בתוסף.",
],
},
diff --git a/apps/web/lib/policies/ru-RU.ts b/apps/web/lib/policies/ru-RU.ts
index 35c7a0a..936322f 100644
--- a/apps/web/lib/policies/ru-RU.ts
+++ b/apps/web/lib/policies/ru-RU.ts
@@ -29,7 +29,7 @@ export const ruRU: PolicyMessages = {
{
heading: "Ваш IP-адрес",
paragraphs: [
- "Мы храним ваш IP-адрес только для одной цели. Когда вы загружаете пакет или плагин в ViGet, мы записываем IP-адрес, с которого пришла загрузка, вместе с самой загрузкой. Если окажется, что загрузка содержит вредоносное программное обеспечение, мы используем этот адрес, чтобы установить, откуда она пришла, и передаём его компетентным органам. Ни для чего другого мы его не используем.",
+ "Мы храним ваш IP-адрес только для одной цели. ViGet пока не принимает загрузки от аккаунтов: публикует в нём только Progmasoft. Когда это станет возможно, ViGet будет записывать IP-адрес, с которого пришла каждая загрузка, вместе с самой загрузкой. Если окажется, что загрузка содержит вредоносное программное обеспечение, мы используем этот адрес, чтобы установить, откуда она пришла, и передадим его компетентным органам. Ни для чего другого мы его не используем.",
"Мы не храним IP-адреса вместе с вашим аккаунтом, вашими сессиями или в журналах доступа наших веб-серверов.",
"Чтобы затруднить подбор паролей, служба входа в течение нескольких минут считает запросы с каждого адреса. Этот счёт находится только в памяти и никуда не записывается.",
],
@@ -57,7 +57,7 @@ export const ruRU: PolicyMessages = {
{
heading: "Как долго мы храним данные",
paragraphs: [
- "Данные аккаунта хранятся, пока существует аккаунт. Сессия удаляется после её окончания. IP-адрес загрузки в ViGet хранится, пока загрузка находится в ViGet, и ещё один год после её удаления, чтобы можно было разобраться и с поздно поступившим сообщением.",
+ "Данные аккаунта хранятся, пока существует аккаунт. Сессия удаляется после её окончания. IP-адрес загрузки в ViGet будет храниться, пока загрузка находится в ViGet, и ещё один год после её удаления, чтобы можно было разобраться и с поздно поступившим сообщением.",
],
},
{
@@ -89,14 +89,14 @@ export const ruRU: PolicyMessages = {
{
heading: "Соединения",
paragraphs: [
- "Каждый сайт Progmasoft доступен только по HTTPS, и браузерам предписано так и продолжать (HSTS). Каждая страница содержит Content Security Policy, которая разрешает только те скрипты, которые называет сама страница.",
+ "Каждый сайт Progmasoft доступен только по HTTPS, и браузерам предписано так и продолжать (HSTS). Каждая страница содержит Content Security Policy, которая разрешает только скрипты, названные самой страницей, и скрипт аналитики Cloudflare.",
],
},
{
heading: "Вредоносное ПО в ViGet",
paragraphs: [
"Загружать в ViGet вредоносное программное обеспечение запрещено: программы, которые вредят компьютерам, на которых работают, шпионят за ними или захватывают над ними контроль, или делают не то, что заявляют.",
- "Если мы находим такую программу или нам о ней сообщают, мы удаляем её, закрываем аккаунт, который её загрузил, и передаём IP-адрес загрузки компетентным органам, как описано в Политике конфиденциальности.",
+ "Если мы находим такую программу или нам о ней сообщают, мы убираем её из ViGet: её файлы больше не выдаются и хранятся в недоступном месте только как доказательство. Мы закрываем аккаунт, который её загрузил, и передаём IP-адрес загрузки компетентным органам, как описано в Политике конфиденциальности.",
"Каждый плагин, который выдаёт ViGet, указан со своим дайджестом SHA-256, и Tazel проверяет дайджест перед использованием плагина.",
],
},
diff --git a/apps/web/tests/policies.test.mjs b/apps/web/tests/policies.test.mjs
index d967b95..2429b62 100644
--- a/apps/web/tests/policies.test.mjs
+++ b/apps/web/tests/policies.test.mjs
@@ -49,7 +49,11 @@ test("the privacy policy keeps the IP address for ViGet uploads only and sells n
text.startsWith("We keep your IP address for one purpose only."),
);
assert.ok(address);
- assert.match(address, /upload a package or a plugin to ViGet/);
+ assert.match(
+ address,
+ /ViGet will record the IP address each upload came from/,
+ );
+ assert.match(address, /ViGet does not yet take uploads from accounts/);
assert.match(address, /competent authorities/);
assert.ok(
privacy.some((text) =>
diff --git a/ops/README.md b/ops/README.md
index 6b8ad50..283ca0b 100644
--- a/ops/README.md
+++ b/ops/README.md
@@ -73,6 +73,41 @@ without a database for a short time.
The account database belongs to Progmaweb alone. Other services on the host have their own databases and roles; do
not share a role or a database with them.
+### Deleting an account
+
+The Privacy Policy lets an owner ask, from the address of the account, to have it deleted. The API has no deletion
+endpoint, so support does it in the database, after checking that the request came from that address. Dump the
+database first (`pg_dump progmaweb_accounts`), then, as the `progmaweb` role, in one transaction, with the `id` of the
+account (`select id from accounts where normalized_account_name = upper('<Name>')`):
+
+```sql
+BEGIN;
+-- What the account allowed applications to do: OpenIddict keeps the account id as the subject.
+DELETE FROM oauth_tokens WHERE "Subject" = '<id>';
+DELETE FROM oauth_authorizations WHERE "Subject" = '<id>';
+-- Applications the account alone owns, with what was issued for them.
+CREATE TEMP TABLE owned ON COMMIT DROP AS
+ SELECT "ApplicationId" AS id FROM oauth_application_owners GROUP BY "ApplicationId"
+ HAVING bool_and("AccountId" = '<id>');
+DELETE FROM oauth_tokens WHERE "ApplicationId" IN (SELECT id FROM owned);
+DELETE FROM oauth_authorizations WHERE "ApplicationId" IN (SELECT id FROM owned);
+DELETE FROM oauth_applications WHERE "Id" IN (SELECT id FROM owned);
+-- The account; its sessions and its remaining application ownerships go with it (ON DELETE CASCADE).
+DELETE FROM accounts WHERE id = '<id>';
+COMMIT;
+```
+
+The Tazel plugins an account published stay in ViGet under its name: projects depend on them, and a removed name
+could be registered by someone else. Delete the dump once the deletion is confirmed.
+
+### Logs without client addresses
+
+The Privacy Policy says that IP addresses are kept only with ViGet uploads. On both servers the `main` log format of
+`/etc/nginx/nginx.conf` therefore has no `$remote_addr` and no `$http_x_forwarded_for`, and `error_log` keeps only
+`crit` entries, because a lower level names the client of a failed request. The change is
+`ops/aisupport/nginx-no-ip.pl` (applied by hand on the first server on 2026-11-06, with a copy of the previous file
+in `/root/policy-20261106`). A new site in `conf.d` uses the `main` format or one of its own without an address.
+
### Releases and rollback
- Restarting the API keeps every account and session. Step 4 of the activation contract applies to both services.
@@ -212,6 +247,26 @@ The publisher of the descriptor must be the name of an existing Progmasoft Accou
The command checks the descriptor of the JAR, the publisher, its account and the version, which is never replaced.
`viget yank-tazel-plugin <Publisher> <Name> <Version>` yanks a version, and `--undo` takes the yank back.
+### Taking malicious software off ViGet
+
+The Security Policy (progmasoft.com/security) says that malicious software found on ViGet is taken off it and that
+the uploader's account is closed. A yank alone does not do that: it marks the version, while Nginx keeps serving the
+JAR and Cloudflare keeps its copy for a year. Taking a version off ViGet is four steps, as root:
+
+1. Yank the version with `viget yank-tazel-plugin`, as above, so Tazel no longer resolves it.
+2. Move the JAR out of `/srv/viget/files` into `/root/quarantine/viget/<Publisher>/<Name>/<Version>/` (mode 0700). It
+ stops being served, and the file stays as evidence for the authorities; it is not deleted.
+3. Purge its address from the Cloudflare cache (`POST /zones/<zone>/purge_cache` with
+ `{"files": ["https://viget.progmasoft.com/dslplugins/tazel/<Publisher>/<Name>/<Version>/<Name>-<Version>.jar"]}`)
+ and check that the address now answers 404.
+4. Close the account that uploaded it, as described under "Deleting an account", once whatever the authorities need
+ from it has been kept.
+
+ViGet takes uploads only from the publishers `ViGet__TazelPublishers` names, through the command above. When uploads
+open to every account, the upload endpoint has to keep the IP address of each upload with the upload, for as long as
+the upload is on ViGet and one year after it is removed: the Privacy Policy promises exactly that, and nothing else
+keeps an IP address.
+
## Maven repository
`maven.progmasoft.com/tazel` is a static Maven repository: it holds `com.progmasoft.tazel:tazel-api`, which plugins of