Устранить уязвимости backend-образа
Some checks failed
Проверка, сборка и развёртывание Magistr / Обязательные проверки (push) Successful in 1m12s
Проверка, сборка и развёртывание Magistr / Собрать backend image (push) Successful in 6m31s
Проверка, сборка и развёртывание Magistr / Собрать frontend image (push) Successful in 25s
Проверка, сборка и развёртывание Magistr / Проверить опубликованные образы (push) Failing after 6m40s
Проверка, сборка и развёртывание Magistr / Развернуть проверенные digests (push) Has been skipped

This commit is contained in:
Zuev
2026-07-30 00:28:02 +03:00
parent 7b5053f42a
commit c3f477788e
13 changed files with 226 additions and 65 deletions

View File

@@ -3,7 +3,7 @@
## Обзор проекта ## Обзор проекта
Проект представляет собой систему управления университетским расписанием. Проект представляет собой систему управления университетским расписанием.
- **Backend**: Java 17, Spring Boot 3.2.5 (Мультитенантная архитектура: отдельная БД для каждого клиента) - **Backend**: Java 17, Spring Boot 3.5.16 (Мультитенантная архитектура: отдельная БД для каждого клиента)
- **Frontend**: Vanilla JavaScript + HTML/CSS (без фреймворков) - **Frontend**: Vanilla JavaScript + HTML/CSS (без фреймворков)
- **Database**: PostgreSQL (множество БД, управляются через Flyway) - **Database**: PostgreSQL (множество БД, управляются через Flyway)
- **Локальный URL**: localhost:80 - **Локальный URL**: localhost:80

View File

@@ -16,7 +16,10 @@ RUN mvn org.apache.maven.plugins:maven-dependency-plugin:3.6.1:copy \
FROM eclipse-temurin:17-jre-alpine@sha256:02320dd4ce20e243dfb915c686089cf9315c763084fafbb12d5c9993aee18b57 FROM eclipse-temurin:17-jre-alpine@sha256:02320dd4ce20e243dfb915c686089cf9315c763084fafbb12d5c9993aee18b57
# Best practice: run as a non-root user # Устанавливаем исправления безопасности, опубликованные после сборки базового образа.
RUN apk upgrade --no-cache
# Запускаем приложение от непривилегированного пользователя.
RUN addgroup -S spring && adduser -S spring -G spring RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring USER spring:spring

View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version> <version>3.5.16</version>
<relativePath/> <relativePath/>
</parent> </parent>
@@ -18,6 +18,7 @@
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
<postgresql.version>42.7.12</postgresql.version>
</properties> </properties>
<dependencies> <dependencies>
@@ -44,6 +45,10 @@
<groupId>org.flywaydb</groupId> <groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId> <artifactId>flyway-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.postgresql</groupId> <groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId> <artifactId>postgresql</artifactId>

View File

@@ -15,6 +15,7 @@ import com.magistr.app.repository.DepartmentRepository;
import com.magistr.app.repository.TeacherCreationRequestRepository; import com.magistr.app.repository.TeacherCreationRequestRepository;
import com.magistr.app.repository.TeacherDepartmentAssignmentRepository; import com.magistr.app.repository.TeacherDepartmentAssignmentRepository;
import com.magistr.app.repository.UserRepository; import com.magistr.app.repository.UserRepository;
import com.magistr.app.service.BusinessTimeService;
import com.magistr.app.service.ScheduleQueryService; import com.magistr.app.service.ScheduleQueryService;
import com.magistr.app.service.SubjectImportService; import com.magistr.app.service.SubjectImportService;
import com.magistr.app.service.TeacherDepartmentService; import com.magistr.app.service.TeacherDepartmentService;
@@ -22,7 +23,10 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import java.time.Clock;
import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@@ -34,6 +38,12 @@ import static org.mockito.Mockito.when;
class DepartmentWorkspaceControllerTest { class DepartmentWorkspaceControllerTest {
private static final LocalDate TODAY = LocalDate.of(2026, 7, 29);
private static final BusinessTimeService BUSINESS_TIME = new BusinessTimeService(
Clock.fixed(Instant.parse("2026-07-29T09:00:00Z"), ZoneOffset.UTC),
ZoneOffset.UTC
);
@AfterEach @AfterEach
void clearAuthContext() { void clearAuthContext() {
AuthContext.clear(); AuthContext.clear();
@@ -71,7 +81,7 @@ class DepartmentWorkspaceControllerTest {
TeacherDepartmentService teacherDepartmentService = mock(TeacherDepartmentService.class); TeacherDepartmentService teacherDepartmentService = mock(TeacherDepartmentService.class);
User assignedTeacher = user(10L, "assigned", "Петров Препод Петрович", 2L); User assignedTeacher = user(10L, "assigned", "Петров Препод Петрович", 2L);
User directTeacher = user(11L, "direct", "Препод Тест Тестович", 1L); User directTeacher = user(11L, "direct", "Препод Тест Тестович", 1L);
when(teacherDepartmentService.findTeachersForDepartmentAtDate(1L, LocalDate.now())) when(teacherDepartmentService.findTeachersForDepartmentAtDate(1L, TODAY))
.thenReturn(List.of(directTeacher, assignedTeacher)); .thenReturn(List.of(directTeacher, assignedTeacher));
DepartmentWorkspaceController controller = new DepartmentWorkspaceController( DepartmentWorkspaceController controller = new DepartmentWorkspaceController(
mock(SubjectRepository.class), mock(SubjectRepository.class),
@@ -82,7 +92,8 @@ class DepartmentWorkspaceControllerTest {
mock(TeacherCreationRequestRepository.class), mock(TeacherCreationRequestRepository.class),
mock(ScheduleQueryService.class), mock(ScheduleQueryService.class),
teacherDepartmentService, teacherDepartmentService,
mock(SubjectImportService.class) mock(SubjectImportService.class),
BUSINESS_TIME
); );
List<UserResponse> teachers = controller.getTeachers(1L); List<UserResponse> teachers = controller.getTeachers(1L);

View File

@@ -9,12 +9,15 @@ import com.magistr.app.model.User;
import com.magistr.app.repository.SubjectRepository; import com.magistr.app.repository.SubjectRepository;
import com.magistr.app.repository.TeacherSubjectRepository; import com.magistr.app.repository.TeacherSubjectRepository;
import com.magistr.app.repository.UserRepository; import com.magistr.app.repository.UserRepository;
import com.magistr.app.service.BusinessTimeService;
import com.magistr.app.service.TeacherDepartmentService; import com.magistr.app.service.TeacherDepartmentService;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import java.time.LocalDate; import java.time.Clock;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@@ -25,6 +28,11 @@ import static org.mockito.Mockito.when;
class TeacherSubjectControllerTest { class TeacherSubjectControllerTest {
private static final BusinessTimeService BUSINESS_TIME = new BusinessTimeService(
Clock.fixed(Instant.parse("2026-07-29T09:00:00Z"), ZoneOffset.UTC),
ZoneOffset.UTC
);
@AfterEach @AfterEach
void clearAuthContext() { void clearAuthContext() {
AuthContext.clear(); AuthContext.clear();
@@ -46,13 +54,14 @@ class TeacherSubjectControllerTest {
when(teacherDepartmentService.hasAssignmentAtDate( when(teacherDepartmentService.hasAssignmentAtDate(
teacher.getId(), teacher.getId(),
1L, 1L,
LocalDate.now() BUSINESS_TIME.today()
)).thenReturn(true); )).thenReturn(true);
TeacherSubjectController controller = new TeacherSubjectController( TeacherSubjectController controller = new TeacherSubjectController(
teacherSubjectRepository, teacherSubjectRepository,
userRepository, userRepository,
subjectRepository, subjectRepository,
teacherDepartmentService teacherDepartmentService,
BUSINESS_TIME
); );
AuthContext.setCurrentUser(new AuthenticatedUser(1L, "кафедра", Role.DEPARTMENT, 1L)); AuthContext.setCurrentUser(new AuthenticatedUser(1L, "кафедра", Role.DEPARTMENT, 1L));

View File

@@ -6,7 +6,7 @@
graph TD graph TD
Client["🌐 Браузер"] -->|HTTPS| Caddy["Caddy Proxy"] Client["🌐 Браузер"] -->|HTTPS| Caddy["Caddy Proxy"]
Caddy -->|:80| Frontend["Frontend<br/>(Apache httpd + строгий CSP)"] Caddy -->|:80| Frontend["Frontend<br/>(Apache httpd + строгий CSP)"]
Caddy -->|/api/*| Backend["Backend<br/>(Spring Boot 3.2.5)"] Caddy -->|/api/*| Backend["Backend<br/>(Spring Boot 3.5.16)"]
Backend --> TenantRouter{"TenantRoutingDataSource"} Backend --> TenantRouter{"TenantRoutingDataSource"}
TenantRouter -->|swsu.zuev.company| DB1["PostgreSQL<br/>swsu_db"] TenantRouter -->|swsu.zuev.company| DB1["PostgreSQL<br/>swsu_db"]
@@ -27,7 +27,7 @@ graph TD
- **JS-модули:** Vanilla JavaScript с ES6 Modules (`import`/`export`) - **JS-модули:** Vanilla JavaScript с ES6 Modules (`import`/`export`)
- **Browser security:** same-origin runtime-ресурсы и CSP без `unsafe-inline`/`unsafe-eval` - **Browser security:** same-origin runtime-ресурсы и CSP без `unsafe-inline`/`unsafe-eval`
### Backend (Spring Boot 3.2.5) ### Backend (Spring Boot 3.5.16)
- **Тип:** REST API сервер - **Тип:** REST API сервер
- **Язык:** Java 17 - **Язык:** Java 17
- **Порт:** 8080 (внутренний) - **Порт:** 8080 (внутренний)

View File

@@ -86,7 +86,9 @@ Backend собирается через multi-stage сборку Maven:
1. Этап сборки: `maven:3.9.9-eclipse-temurin-17@sha256:f58d59b...` → `mvn package` 1. Этап сборки: `maven:3.9.9-eclipse-temurin-17@sha256:f58d59b...` → `mvn package`
2. OpenTelemetry Java Agent `2.28.1` загружается как фиксированный Maven-артефакт и 2. OpenTelemetry Java Agent `2.28.1` загружается как фиксированный Maven-артефакт и
проверяется по закреплённому SHA-256 проверяется по закреплённому SHA-256
3. Этап запуска: `eclipse-temurin:17-jre-alpine@sha256:02320dd4...` → `java -jar app.jar` 3. Этап запуска: `eclipse-temurin:17-jre-alpine@sha256:02320dd4...`; перед добавлением
непривилегированного пользователя устанавливаются доступные исправления Alpine через
`apk upgrade --no-cache`, затем выполняется `java -jar app.jar`
### Dockerfile (Frontend) ### Dockerfile (Frontend)

View File

@@ -13,7 +13,7 @@
| Компонент | Технология | | Компонент | Технология |
|-----------|-----------| |-----------|-----------|
| **Backend** | Java 17, Spring Boot 3.2.5 | | **Backend** | Java 17, Spring Boot 3.5.16 |
| **Frontend** | Vanilla JavaScript (ES6 Modules) + HTML/CSS | | **Frontend** | Vanilla JavaScript (ES6 Modules) + HTML/CSS |
| **База данных** | PostgreSQL (через Flyway миграции) | | **База данных** | PostgreSQL (через Flyway миграции) |
| **Контейнеризация** | Docker, Docker Compose | | **Контейнеризация** | Docker, Docker Compose |

View File

@@ -1,11 +1,37 @@
{ {
"generated_at": "2026-07-29T20:02:59.230356+00:00", "generated_at": "2026-07-29T21:00:52.911357+00:00",
"nodes": { "nodes": {
"_gitea_workflows_docker_build_build_and_push_docker_images": { "_gitea_workflows_docker_build_backend_image_pipeline": {
"code_fingerprint": "80373e004c326a786715725be21ddb9214b6ff382a02cb829acad1bb9c9098f8", "code_fingerprint": "672f3effb2b85afa48bca601809662a5567dc0d57a24b224d4c7f1624899b222",
"label": "Workflow сборки и публикации Docker-образов", "label": "Pipeline backend-образа",
"last": "2026-07-29T19:59:04.551881+00:00", "last": "2026-07-29T20:38:53.488440+00:00",
"provenance": [ "provenance": [
{
"date": "2026-07-29T20:38:53.488440+00:00",
"outcome": "useful",
"q": "The goal you specified requires a project to execute but there is no POM in this directory (/workspace)."
}
],
"score": 0.999647225,
"source_file": ".gitea/workflows/docker-build.yaml",
"status": "tentative",
"uses": 1
},
"_gitea_workflows_docker_build_build_and_push_docker_images": {
"code_fingerprint": "672f3effb2b85afa48bca601809662a5567dc0d57a24b224d4c7f1624899b222",
"label": "Workflow сборки и публикации Docker-образов",
"last": "2026-07-29T20:38:53.488440+00:00",
"provenance": [
{
"date": "2026-07-29T20:38:53.488440+00:00",
"outcome": "useful",
"q": "The goal you specified requires a project to execute but there is no POM in this directory (/workspace)."
},
{
"date": "2026-07-29T20:12:38.880629+00:00",
"outcome": "useful",
"q": "сам можешь выполнить эти действия?"
},
{ {
"date": "2026-07-29T19:59:04.551881+00:00", "date": "2026-07-29T19:59:04.551881+00:00",
"outcome": "useful", "outcome": "useful",
@@ -17,10 +43,42 @@
"q": "теперь напиши мне инструкцию как это всё выыкатить на прод. у мня уже поднят на проде этот сервис со старой базой данных, но я могу её затереть. ci/cd тоже настроен, по сути я прямо сейчас могу залить это в ветку мейн и оно должно раскататься. каие есть подводные камни после изменений безопасности которые мы внесли?" "q": "теперь напиши мне инструкцию как это всё выыкатить на прод. у мня уже поднят на проде этот сервис со старой базой данных, но я могу её затереть. ci/cd тоже настроен, по сути я прямо сейчас могу залить это в ветку мейн и оно должно раскататься. каие есть подводные камни после изменений безопасности которые мы внесли?"
} }
], ],
"score": 1.999355769, "score": 3.996372989,
"source_file": ".gitea/workflows/docker-build.yaml", "source_file": ".gitea/workflows/docker-build.yaml",
"status": "preferred", "status": "preferred",
"uses": 2 "uses": 4
},
"_gitea_workflows_docker_build_deploy_to_k8s": {
"code_fingerprint": "672f3effb2b85afa48bca601809662a5567dc0d57a24b224d4c7f1624899b222",
"label": "Job деплоя в Kubernetes",
"last": "2026-07-29T20:12:38.880629+00:00",
"provenance": [
{
"date": "2026-07-29T20:12:38.880629+00:00",
"outcome": "useful",
"q": "сам можешь выполнить эти действия?"
}
],
"score": 0.999226384,
"source_file": ".gitea/workflows/docker-build.yaml",
"status": "tentative",
"uses": 1
},
"_gitea_workflows_docker_build_frontend_image_pipeline": {
"code_fingerprint": "672f3effb2b85afa48bca601809662a5567dc0d57a24b224d4c7f1624899b222",
"label": "Pipeline frontend-образа",
"last": "2026-07-29T20:38:53.488440+00:00",
"provenance": [
{
"date": "2026-07-29T20:38:53.488440+00:00",
"outcome": "useful",
"q": "The goal you specified requires a project to execute but there is no POM in this directory (/workspace)."
}
],
"score": 0.999647225,
"source_file": ".gitea/workflows/docker-build.yaml",
"status": "tentative",
"uses": 1
}, },
"backend_src_main_java_com_magistr_app_config_auth_authorizationinterceptor_authorizationinterceptor": { "backend_src_main_java_com_magistr_app_config_auth_authorizationinterceptor_authorizationinterceptor": {
"code_fingerprint": "70b01b3fa22e058ee997250c52cc0affee54ab37aa5f8cc7eb74411487f1bbbf", "code_fingerprint": "70b01b3fa22e058ee997250c52cc0affee54ab37aa5f8cc7eb74411487f1bbbf",
@@ -43,7 +101,7 @@
"q": "Почему RequireRoles связывает множество контроллеров, моделей и сервисов как cross-community bridge?" "q": "Почему RequireRoles связывает множество контроллеров, моделей и сервисов как cross-community bridge?"
} }
], ],
"score": 1.861135007, "score": 1.859406956,
"source_file": "backend/src/main/java/com/magistr/app/config/auth/AuthorizationInterceptor.java", "source_file": "backend/src/main/java/com/magistr/app/config/auth/AuthorizationInterceptor.java",
"status": "preferred", "status": "preferred",
"uses": 3 "uses": 3
@@ -59,7 +117,7 @@
"q": "Какие подсистемы связывают исправления аутентификации, tenant-конфигурации, расписания и инфраструктуры?" "q": "Какие подсистемы связывают исправления аутентификации, tenant-конфигурации, расписания и инфраструктуры?"
} }
], ],
"score": 0.661231273, "score": 0.660617324,
"source_file": "backend/src/main/java/com/magistr/app/config/auth/JwtTokenService.java", "source_file": "backend/src/main/java/com/magistr/app/config/auth/JwtTokenService.java",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -75,7 +133,7 @@
"q": "Почему RequireRoles связывает множество контроллеров, моделей и сервисов как cross-community bridge?" "q": "Почему RequireRoles связывает множество контроллеров, моделей и сервисов как cross-community bridge?"
} }
], ],
"score": 0.556344408, "score": 0.555827846,
"source_file": "backend/src/main/java/com/magistr/app/config/auth/RequireRoles.java", "source_file": "backend/src/main/java/com/magistr/app/config/auth/RequireRoles.java",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -91,7 +149,7 @@
"q": "Как безопасно обновлять tenant DataSource без потери рабочего подключения?" "q": "Как безопасно обновлять tenant DataSource без потери рабочего подключения?"
} }
], ],
"score": 0.676664767, "score": 0.676036488,
"source_file": "backend/src/main/java/com/magistr/app/config/DataInitializer.java", "source_file": "backend/src/main/java/com/magistr/app/config/DataInitializer.java",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -107,7 +165,7 @@
"q": "проанализируй весь проект на баги и ошибки и дополни '/mnt/HDD/ProjectMagistr/magistr/BUG_REPORT.md'" "q": "проанализируй весь проект на баги и ошибки и дополни '/mnt/HDD/ProjectMagistr/magistr/BUG_REPORT.md'"
} }
], ],
"score": 0.643559327, "score": 0.642961786,
"source_file": "backend/src/main/java/com/magistr/app/config/tenant/ConfigMapUpdater.java", "source_file": "backend/src/main/java/com/magistr/app/config/tenant/ConfigMapUpdater.java",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -123,7 +181,7 @@
"q": "Как безопасно заменить tenant DataSource без потери рабочего подключения?" "q": "Как безопасно заменить tenant DataSource без потери рабочего подключения?"
} }
], ],
"score": 0.676550037, "score": 0.675921865,
"source_file": "backend/src/main/java/com/magistr/app/config/tenant/TenantConfig.java", "source_file": "backend/src/main/java/com/magistr/app/config/tenant/TenantConfig.java",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -149,7 +207,7 @@
"q": "проанализируй весь проект на баги и ошибки и дополни '/mnt/HDD/ProjectMagistr/magistr/BUG_REPORT.md'" "q": "проанализируй весь проект на баги и ошибки и дополни '/mnt/HDD/ProjectMagistr/magistr/BUG_REPORT.md'"
} }
], ],
"score": 1.99677413, "score": 1.994920139,
"source_file": "backend/src/main/java/com/magistr/app/config/tenant/TenantConfigWatcher.java", "source_file": "backend/src/main/java/com/magistr/app/config/tenant/TenantConfigWatcher.java",
"status": "preferred", "status": "preferred",
"uses": 3 "uses": 3
@@ -175,7 +233,7 @@
"q": "Какие подсистемы связывают исправления аутентификации, tenant-конфигурации, расписания и инфраструктуры?" "q": "Какие подсистемы связывают исправления аутентификации, tenant-конфигурации, расписания и инфраструктуры?"
} }
], ],
"score": 2.014446076, "score": 2.012575677,
"source_file": "backend/src/main/java/com/magistr/app/config/tenant/TenantRoutingDataSource.java", "source_file": "backend/src/main/java/com/magistr/app/config/tenant/TenantRoutingDataSource.java",
"status": "preferred", "status": "preferred",
"uses": 3 "uses": 3
@@ -191,7 +249,7 @@
"q": "Почему в выборе аудитории показаны корпус и этаж?" "q": "Почему в выборе аудитории показаны корпус и этаж?"
} }
], ],
"score": 0.831862505, "score": 0.831090126,
"source_file": "backend/src/main/java/com/magistr/app/controller/ClassroomController.java", "source_file": "backend/src/main/java/com/magistr/app/controller/ClassroomController.java",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -207,7 +265,7 @@
"q": "Какие подсистемы связывают исправления аутентификации, tenant-конфигурации, расписания и инфраструктуры?" "q": "Какие подсистемы связывают исправления аутентификации, tenant-конфигурации, расписания и инфраструктуры?"
} }
], ],
"score": 0.661231273, "score": 0.660617324,
"source_file": "backend/src/main/java/com/magistr/app/controller/DatabaseController.java", "source_file": "backend/src/main/java/com/magistr/app/controller/DatabaseController.java",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -228,7 +286,7 @@
"q": "Как безопасно заменить tenant DataSource без потери рабочего подключения?" "q": "Как безопасно заменить tenant DataSource без потери рабочего подключения?"
} }
], ],
"score": 1.353214804, "score": 1.351958353,
"source_file": "backend/src/main/java/com/magistr/app/controller/DatabaseController.java", "source_file": "backend/src/main/java/com/magistr/app/controller/DatabaseController.java",
"status": "preferred", "status": "preferred",
"uses": 2 "uses": 2
@@ -244,7 +302,7 @@
"q": "проанализируй весь проект на баги и ошибки и дополни '/mnt/HDD/ProjectMagistr/magistr/BUG_REPORT.md'" "q": "проанализируй весь проект на баги и ошибки и дополни '/mnt/HDD/ProjectMagistr/magistr/BUG_REPORT.md'"
} }
], ],
"score": 0.643559327, "score": 0.642961786,
"source_file": "backend/src/main/java/com/magistr/app/controller/GlobalExceptionHandler.java", "source_file": "backend/src/main/java/com/magistr/app/controller/GlobalExceptionHandler.java",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -265,7 +323,7 @@
"q": "Какие подсистемы связывают исправления аутентификации, tenant-конфигурации, расписания и инфраструктуры?" "q": "Какие подсистемы связывают исправления аутентификации, tenant-конфигурации, расписания и инфраструктуры?"
} }
], ],
"score": 1.492325672, "score": 1.490940058,
"source_file": "backend/src/main/java/com/magistr/app/controller/ScheduleController.java", "source_file": "backend/src/main/java/com/magistr/app/controller/ScheduleController.java",
"status": "preferred", "status": "preferred",
"uses": 2 "uses": 2
@@ -281,7 +339,7 @@
"q": "Как безопасно валидировать и сохранять точечные изменения расписания?" "q": "Как безопасно валидировать и сохранять точечные изменения расписания?"
} }
], ],
"score": 0.671815777, "score": 0.671192001,
"source_file": "backend/src/main/java/com/magistr/app/controller/ScheduleOverrideController.java", "source_file": "backend/src/main/java/com/magistr/app/controller/ScheduleOverrideController.java",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -302,7 +360,7 @@
"q": "проанализируй весь проект на баги и ошибки и дополни '/mnt/HDD/ProjectMagistr/magistr/BUG_REPORT.md'" "q": "проанализируй весь проект на баги и ошибки и дополни '/mnt/HDD/ProjectMagistr/magistr/BUG_REPORT.md'"
} }
], ],
"score": 1.319879771, "score": 1.318654271,
"source_file": "backend/src/main/java/com/magistr/app/controller/ScheduleRuleAdminController.java", "source_file": "backend/src/main/java/com/magistr/app/controller/ScheduleRuleAdminController.java",
"status": "preferred", "status": "preferred",
"uses": 2 "uses": 2
@@ -318,7 +376,7 @@
"q": "хотим поменять название сервиса, придумай что-то короткое одним словом подходящее по смыслу" "q": "хотим поменять название сервиса, придумай что-то короткое одним словом подходящее по смыслу"
} }
], ],
"score": 0.851581632, "score": 0.850790945,
"source_file": "backend/src/main/java/com/magistr/app/model/AcademicCalendar.java", "source_file": "backend/src/main/java/com/magistr/app/model/AcademicCalendar.java",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -334,7 +392,7 @@
"q": "Почему в выборе аудитории показаны корпус и этаж?" "q": "Почему в выборе аудитории показаны корпус и этаж?"
} }
], ],
"score": 0.831862505, "score": 0.831090126,
"source_file": "backend/src/main/java/com/magistr/app/model/Classroom.java", "source_file": "backend/src/main/java/com/magistr/app/model/Classroom.java",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -350,7 +408,7 @@
"q": "хотим поменять название сервиса, придумай что-то короткое одним словом подходящее по смыслу" "q": "хотим поменять название сервиса, придумай что-то короткое одним словом подходящее по смыслу"
} }
], ],
"score": 0.851581632, "score": 0.850790945,
"source_file": "backend/src/main/java/com/magistr/app/model/Department.java", "source_file": "backend/src/main/java/com/magistr/app/model/Department.java",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -371,7 +429,7 @@
"q": "Корректны ли inferred-связи вокруг ScheduleRule?" "q": "Корректны ли inferred-связи вокруг ScheduleRule?"
} }
], ],
"score": 1.23328582, "score": 1.232140722,
"source_file": "backend/src/main/java/com/magistr/app/model/ScheduleRule.java", "source_file": "backend/src/main/java/com/magistr/app/model/ScheduleRule.java",
"status": "preferred", "status": "preferred",
"uses": 2 "uses": 2
@@ -387,7 +445,7 @@
"q": "Какие связи затрагивает полная валидация правил расписания в исправлении №7?" "q": "Какие связи затрагивает полная валидация правил расписания в исправлении №7?"
} }
], ],
"score": 0.676320444, "score": 0.675692485,
"source_file": "backend/src/main/java/com/magistr/app/model/ScheduleRuleSlot.java", "source_file": "backend/src/main/java/com/magistr/app/model/ScheduleRuleSlot.java",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -408,7 +466,7 @@
"q": "в дополнительные фильры просмотра расписаний нужно добавить выбор семестра" "q": "в дополнительные фильры просмотра расписаний нужно добавить выбор семестра"
} }
], ],
"score": 1.682676032, "score": 1.681113679,
"source_file": "backend/src/main/java/com/magistr/app/model/Semester.java", "source_file": "backend/src/main/java/com/magistr/app/model/Semester.java",
"status": "preferred", "status": "preferred",
"uses": 2 "uses": 2
@@ -424,7 +482,7 @@
"q": "хотим поменять название сервиса, придумай что-то короткое одним словом подходящее по смыслу" "q": "хотим поменять название сервиса, придумай что-то короткое одним словом подходящее по смыслу"
} }
], ],
"score": 0.851581632, "score": 0.850790945,
"source_file": "backend/src/main/java/com/magistr/app/model/StudentGroup.java", "source_file": "backend/src/main/java/com/magistr/app/model/StudentGroup.java",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -440,7 +498,7 @@
"q": "Какие связи затрагивает полная валидация правил расписания в исправлении №7?" "q": "Какие связи затрагивает полная валидация правил расписания в исправлении №7?"
} }
], ],
"score": 0.676320444, "score": 0.675692485,
"source_file": "backend/src/main/java/com/magistr/app/repository/SemesterRepository.java", "source_file": "backend/src/main/java/com/magistr/app/repository/SemesterRepository.java",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -461,7 +519,7 @@
"q": "Как безопасно валидировать и сохранять точечные изменения расписания?" "q": "Как безопасно валидировать и сохранять точечные изменения расписания?"
} }
], ],
"score": 1.502910177, "score": 1.501514735,
"source_file": "backend/src/main/java/com/magistr/app/service/ScheduleQueryService.java", "source_file": "backend/src/main/java/com/magistr/app/service/ScheduleQueryService.java",
"status": "preferred", "status": "preferred",
"uses": 2 "uses": 2
@@ -477,7 +535,7 @@
"q": "Корректны ли inferred-связи вокруг ScheduleRule?" "q": "Корректны ли inferred-связи вокруг ScheduleRule?"
} }
], ],
"score": 0.556965376, "score": 0.556448237,
"source_file": "backend/src/test/java/com/magistr/app/model/ScheduleRuleTest.java", "source_file": "backend/src/test/java/com/magistr/app/model/ScheduleRuleTest.java",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -493,7 +551,7 @@
"q": "Какие подсистемы связывают исправления аутентификации, tenant-конфигурации, расписания и инфраструктуры?" "q": "Какие подсистемы связывают исправления аутентификации, tenant-конфигурации, расписания и инфраструктуры?"
} }
], ],
"score": 0.661231273, "score": 0.660617324,
"source_file": "compose.yaml", "source_file": "compose.yaml",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -509,7 +567,7 @@
"q": "новая схема на проде на удалённом сервере создана через CREATE SCHEMA public. я проверил, давай дальше" "q": "новая схема на проде на удалённом сервере создана через CREATE SCHEMA public. я проверил, давай дальше"
} }
], ],
"score": 0.999937245, "score": 0.99900881,
"source_file": "compose.yaml", "source_file": "compose.yaml",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
@@ -525,32 +583,58 @@
"q": "теперь напиши мне инструкцию как это всё выыкатить на прод. у мня уже поднят на проде этот сервис со старой базой данных, но я могу её затереть. ci/cd тоже настроен, по сути я прямо сейчас могу залить это в ветку мейн и оно должно раскататься. каие есть подводные камни после изменений безопасности которые мы внесли?" "q": "теперь напиши мне инструкцию как это всё выыкатить на прод. у мня уже поднят на проде этот сервис со старой базой данных, но я могу её затереть. ci/cd тоже настроен, по сути я прямо сейчас могу залить это в ветку мейн и оно должно раскататься. каие есть подводные камни после изменений безопасности которые мы внесли?"
} }
], ],
"score": 0.999418524, "score": 0.998490571,
"source_file": "docs/DATABASE.md", "source_file": "docs/DATABASE.md",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1
}, },
"docs_infrastructure_jwt_secret_policy": { "docs_infrastructure_gitea_actions_cicd": {
"code_fingerprint": "fbce586f3b64dba203d336f5744d9895924e86e122cdde1939c0dec1600e1af1", "code_fingerprint": "40b5d07ca2998f186f60fcab44ece5a5bf3ee2ad747acfbe1be3c29d3b7a00f5",
"label": "Политика JWT-секрета", "label": "CI/CD Gitea Actions в инфраструктуре",
"last": "2026-07-29T19:26:44.188240+00:00", "last": "2026-07-29T20:38:53.488440+00:00",
"provenance": [ "provenance": [
{
"date": "2026-07-29T20:38:53.488440+00:00",
"outcome": "useful",
"q": "The goal you specified requires a project to execute but there is no POM in this directory (/workspace)."
}
],
"score": 0.999647225,
"source_file": "docs/INFRASTRUCTURE.md",
"status": "tentative",
"uses": 1
},
"docs_infrastructure_jwt_secret_policy": {
"code_fingerprint": "40b5d07ca2998f186f60fcab44ece5a5bf3ee2ad747acfbe1be3c29d3b7a00f5",
"label": "Политика JWT-секрета",
"last": "2026-07-29T20:12:38.880629+00:00",
"provenance": [
{
"date": "2026-07-29T20:12:38.880629+00:00",
"outcome": "useful",
"q": "сам можешь выполнить эти действия?"
},
{ {
"date": "2026-07-29T19:26:44.188240+00:00", "date": "2026-07-29T19:26:44.188240+00:00",
"outcome": "useful", "outcome": "useful",
"q": "теперь напиши мне инструкцию как это всё выыкатить на прод. у мня уже поднят на проде этот сервис со старой базой данных, но я могу её затереть. ci/cd тоже настроен, по сути я прямо сейчас могу залить это в ветку мейн и оно должно раскататься. каие есть подводные камни после изменений безопасности которые мы внесли?" "q": "теперь напиши мне инструкцию как это всё выыкатить на прод. у мня уже поднят на проде этот сервис со старой базой данных, но я могу её затереть. ci/cd тоже настроен, по сути я прямо сейчас могу залить это в ветку мейн и оно должно раскататься. каие есть подводные камни после изменений безопасности которые мы внесли?"
} }
], ],
"score": 0.999418524, "score": 1.997716954,
"source_file": "docs/INFRASTRUCTURE.md", "source_file": "docs/INFRASTRUCTURE.md",
"status": "tentative", "status": "preferred",
"uses": 1 "uses": 2
}, },
"docs_infrastructure_kubernetes_production": { "docs_infrastructure_kubernetes_production": {
"code_fingerprint": "fbce586f3b64dba203d336f5744d9895924e86e122cdde1939c0dec1600e1af1", "code_fingerprint": "40b5d07ca2998f186f60fcab44ece5a5bf3ee2ad747acfbe1be3c29d3b7a00f5",
"label": "Production-инфраструктура Kubernetes", "label": "Production-инфраструктура Kubernetes",
"last": "2026-07-29T19:59:04.551881+00:00", "last": "2026-07-29T20:12:38.880629+00:00",
"provenance": [ "provenance": [
{
"date": "2026-07-29T20:12:38.880629+00:00",
"outcome": "useful",
"q": "сам можешь выполнить эти действия?"
},
{ {
"date": "2026-07-29T19:59:04.551881+00:00", "date": "2026-07-29T19:59:04.551881+00:00",
"outcome": "useful", "outcome": "useful",
@@ -562,13 +646,13 @@
"q": "теперь напиши мне инструкцию как это всё выыкатить на прод. у мня уже поднят на проде этот сервис со старой базой данных, но я могу её затереть. ci/cd тоже настроен, по сути я прямо сейчас могу залить это в ветку мейн и оно должно раскататься. каие есть подводные камни после изменений безопасности которые мы внесли?" "q": "теперь напиши мне инструкцию как это всё выыкатить на прод. у мня уже поднят на проде этот сервис со старой базой данных, но я могу её затереть. ci/cd тоже настроен, по сути я прямо сейчас могу залить это в ветку мейн и оно должно раскататься. каие есть подводные камни после изменений безопасности которые мы внесли?"
} }
], ],
"score": 1.999355769, "score": 2.996725764,
"source_file": "docs/INFRASTRUCTURE.md", "source_file": "docs/INFRASTRUCTURE.md",
"status": "preferred", "status": "preferred",
"uses": 2 "uses": 3
}, },
"docs_infrastructure_tenants_configmap": { "docs_infrastructure_tenants_configmap": {
"code_fingerprint": "fbce586f3b64dba203d336f5744d9895924e86e122cdde1939c0dec1600e1af1", "code_fingerprint": "40b5d07ca2998f186f60fcab44ece5a5bf3ee2ad747acfbe1be3c29d3b7a00f5",
"label": "Tenants ConfigMap", "label": "Tenants ConfigMap",
"last": "2026-07-29T19:26:44.188240+00:00", "last": "2026-07-29T19:26:44.188240+00:00",
"provenance": [ "provenance": [
@@ -578,7 +662,7 @@
"q": "теперь напиши мне инструкцию как это всё выыкатить на прод. у мня уже поднят на проде этот сервис со старой базой данных, но я могу её затереть. ci/cd тоже настроен, по сути я прямо сейчас могу залить это в ветку мейн и оно должно раскататься. каие есть подводные камни после изменений безопасности которые мы внесли?" "q": "теперь напиши мне инструкцию как это всё выыкатить на прод. у мня уже поднят на проде этот сервис со старой базой данных, но я могу её затереть. ci/cd тоже настроен, по сути я прямо сейчас могу залить это в ветку мейн и оно должно раскататься. каие есть подводные камни после изменений безопасности которые мы внесли?"
} }
], ],
"score": 0.999418524, "score": 0.998490571,
"source_file": "docs/INFRASTRUCTURE.md", "source_file": "docs/INFRASTRUCTURE.md",
"status": "tentative", "status": "tentative",
"uses": 1 "uses": 1

View File

@@ -0,0 +1,25 @@
---
type: "query"
date: "2026-07-29T21:27:26.637336+00:00"
question: "Проверить опубликованные образы: backend Trivy HIGH/CRITICAL scan завершился exitcode 1"
contributor: "graphify"
outcome: "useful"
source_nodes: ["Workflow сборки и публикации Docker-образов", "Pipeline backend-образа", "CI/CD Gitea Actions в инфраструктуре", "SpringBootApplication"]
---
# Q: Проверить опубликованные образы: backend Trivy HIGH/CRITICAL scan завершился exitcode 1
## Answer
CI дошёл до сканирования опубликованного backend-образа, поэтому передача исходников в контейнеры уже исправлена. HIGH/CRITICAL находились в устаревших пакетах Alpine и Java-зависимостях Spring Boot 3.2.5. Backend обновлён до Spring Boot 3.5.16, PostgreSQL JDBC закреплён на 42.7.12, для Flyway 11 добавлен flyway-database-postgresql, runtime устанавливает исправления Alpine. Локальный образ прошёл тот же Trivy gate: Alpine 0, app.jar 0, OpenTelemetry agent 0. Полный backend-набор: 300 тестов без ошибок; обязательные CI, frontend, Compose и supply-chain проверки также прошли.
## Outcome
- Signal: useful
## Source Nodes
- Workflow сборки и публикации Docker-образов
- Pipeline backend-образа
- CI/CD Gitea Actions в инфраструктуре
- SpringBootApplication

View File

@@ -1,19 +1,20 @@
# Lessons # Lessons
_Auto-generated by `graphify reflect` from 14 session memories in graphify-out/memory/. Deterministic; no LLM. Use for orientation — verify before relying, and revisit dead ends if the code has changed since._ _Auto-generated by `graphify reflect` from 16 session memories in graphify-out/memory/. Deterministic; no LLM. Use for orientation — verify before relying, and revisit dead ends if the code has changed since._
## Summary ## Summary
- 13 useful · 1 dead ends · 0 corrected · 0 unmarked - 15 useful · 1 dead ends · 0 corrected · 0 unmarked
## Lessons ## Lessons
**Preferred sources** — corroborated by ≥2 useful results; start here. **Preferred sources** — corroborated by ≥2 useful results; start here.
- `Workflow сборки и публикации Docker-образов` (4× useful)
- `Production-инфраструктура Kubernetes` (3× useful)
- `ScheduleGeneratorService` (3× useful) - `ScheduleGeneratorService` (3× useful)
- `TenantRoutingDataSource` (3× useful) - `TenantRoutingDataSource` (3× useful)
- `Production-инфраструктура Kubernetes` (2× useful) - `Политика JWT-секрета` (2× useful)
- `Workflow сборки и публикации Docker-образов` (2× useful)
- `TenantConfigWatcher` (3× useful) - `TenantConfigWatcher` (3× useful)
- `AuthorizationInterceptor` (3× useful) - `AuthorizationInterceptor` (3× useful)
- `Semester` (2× useful) - `Semester` (2× useful)
@@ -25,10 +26,13 @@ _Auto-generated by `graphify reflect` from 14 session memories in graphify-out/m
**Tentative** — useful in fewer than 2 results; verify before relying. **Tentative** — useful in fewer than 2 results; verify before relying.
- `CI/CD Gitea Actions в инфраструктуре` (1× useful)
- `Pipeline backend-образа` (1× useful)
- `Pipeline frontend-образа` (1× useful)
- `Job деплоя в Kubernetes` (1× useful)
- `PostgreSQL-сервис Docker Compose` (1× useful) - `PostgreSQL-сервис Docker Compose` (1× useful)
- `Flyway-миграции` (1× useful) - `Flyway-миграции` (1× useful)
- `Tenants ConfigMap` (1× useful) - `Tenants ConfigMap` (1× useful)
- `Политика JWT-секрета` (1× useful)
- `AcademicCalendar` (1× useful) - `AcademicCalendar` (1× useful)
- `Department` (1× useful) - `Department` (1× useful)
- `StudentGroup` (1× useful) - `StudentGroup` (1× useful)

View File

@@ -123,6 +123,15 @@ check_downloaded_backend_artifact() {
fi fi
} }
check_backend_runtime_updates() {
local dockerfile="$PROJECT_ROOT/backend/Dockerfile"
require_file "$dockerfile" || return
if ! grep -Fq 'RUN apk upgrade --no-cache' "$dockerfile"; then
fail "runtime-образ backend должен устанавливать доступные исправления Alpine"
fi
}
check_testcontainer_images() { check_testcontainer_images() {
local image_catalog="$PROJECT_ROOT/backend/src/test/java/com/magistr/app/testing/TestContainerImages.java" local image_catalog="$PROJECT_ROOT/backend/src/test/java/com/magistr/app/testing/TestContainerImages.java"
@@ -160,6 +169,7 @@ check_compose_images
check_actions check_actions
check_workflow_gates check_workflow_gates
check_downloaded_backend_artifact check_downloaded_backend_artifact
check_backend_runtime_updates
check_testcontainer_images check_testcontainer_images
check_optional_kubernetes_images check_optional_kubernetes_images

View File

@@ -37,6 +37,14 @@ if PROJECT_ROOT="$FIXTURE_DIR" bash "$CHECK_SCRIPT" >/dev/null 2>&1; then
exit 1 exit 1
fi fi
cp "$ROOT_DIR/backend/Dockerfile" "$FIXTURE_DIR/backend/Dockerfile"
sed -i '/RUN apk upgrade --no-cache/d' "$FIXTURE_DIR/backend/Dockerfile"
if PROJECT_ROOT="$FIXTURE_DIR" bash "$CHECK_SCRIPT" >/dev/null 2>&1; then
printf 'Ошибка теста: отсутствие Alpine security updates не было отклонено\n' >&2
exit 1
fi
cp "$ROOT_DIR/backend/Dockerfile" "$FIXTURE_DIR/backend/Dockerfile"
cp "$ROOT_DIR/.gitea/workflows/docker-build.yaml" \ cp "$ROOT_DIR/.gitea/workflows/docker-build.yaml" \
"$FIXTURE_DIR/.gitea/workflows/docker-build.yaml" "$FIXTURE_DIR/.gitea/workflows/docker-build.yaml"
sed -i "/gitea.event_name == 'workflow_dispatch'/d" \ sed -i "/gitea.event_name == 'workflow_dispatch'/d" \