Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development: Test VSCode plugin support #8840

Open
wants to merge 57 commits into
base: develop
Choose a base branch
from

Conversation

janthoXO
Copy link
Contributor

@janthoXO janthoXO commented Jun 21, 2024

THIS PR IS ONLY FOR TESTING PURPOSES ON TEST SERVERS

Description

The PR includes the following main points:

  1. API support now Bearer as well as cookie
    The API looks first if a valid authentication token is provided in the cookie. if not the authorization header is checked.
  2. Add CSRF security by custom header refer to OWASP
    Mainly the custom header X-ARTEMIS-CSRF is needed now for every request
  3. Adjust cors config
    the application.yml defines a "allowed-origin-pattern" which was not checked in the WebConfigurer.java
  4. Add endpoints for rekeying and getting exercises by the projectKey

Testserver States

Note

These badges show the state of the test servers.
Green = Currently available, Red = Currently locked
Click on the badges to get to the test servers.







Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a CSRF protection filter to enhance security.
    • Added a new HTTP interceptor for CSRF handling in the Angular application.
    • Implemented a method to trade cookies for bearer tokens in the account service.
    • Enhanced JWT extraction and validation processes.
  • Bug Fixes

    • Removed the online IDE button for programming exercises in the student actions component.
  • Documentation

    • Updated comments and logging for CORS configuration visibility.
  • Tests

    • Modified test suites to reflect changes in component functionality and visibility conditions.

@janthoXO janthoXO linked an issue Jun 21, 2024 that may be closed by this pull request
@github-actions github-actions bot added server Pull requests that update Java code. (Added Automatically!) config-change Pull requests that change the config in a way that they require a deployment via Ansible. labels Jun 21, 2024
@Hialus
Copy link
Member

Hialus commented Jun 21, 2024

As this comment was not addressed in #8819 (and this is the PR which contains those changes now), I'll leave it here again so it is not lost/forgotten about:

From what I get you have a client that for some reason cannot read/set the "Set-Cookie/Cookie" headers, but can read the body or a response and set the "Authorization" Header. Every proper library for executing HTTP requests should be able to handle arbitrary headers nowadays.
Our various iOS/Android apps are also able to use the Cookie headers instead of the Authorization header. Adding a separate way of authentication just for a single client that happens to use a bad requesting library is not a good idea imo.

Edit: I see that Stephan has raised similar concerns in the linked issue, so maybe we can discuss this in the next dev meeting on monday.

@janthoXO
Copy link
Contributor Author

As this comment was not addressed in #8819 (and this is the PR which contains those changes now), I'll leave it here again so it is not lost/forgotten about:

From what I get you have a client that for some reason cannot read/set the "Set-Cookie/Cookie" headers, but can read the body or a response and set the "Authorization" Header. Every proper library for executing HTTP requests should be able to handle arbitrary headers nowadays.
Our various iOS/Android apps are also able to use the Cookie headers instead of the Authorization header. Adding a separate way of authentication just for a single client that happens to use a bad requesting library is not a good idea imo.
Edit: I see that Stephan has raised similar concerns in the linked issue, so maybe we can discuss this in the next dev meeting on monday.

theres gonna be a discussion on monday concerning this issue

@janthoXO
Copy link
Contributor Author

janthoXO commented Jun 22, 2024

just for a single client that happens to use a bad requesting library is not a good idea imo.

but to answer that its just the fetch API which is able to handle cookies. the problem is though that vscode plugins dont support cookie nor local storage

@Hialus
Copy link
Member

Hialus commented Jun 22, 2024

just for a single client that happens to use a bad requesting library is not a good idea imo.

but to answer that its just the fetch API which is able to handle cookies. the problem is though that vscode plugins dont support cookie nor local storage

I've looked into the fetch docs and it provides the response.headers.get(String) method for getting response headers (and various ways to also create headers). Therefore I do not see why it shouldn't be possible to just read and write the proper cookie headers manually. I've done much the same with Pythons requests library in the past. Your library does not need to provide first party cookie support for you to use cookies, as cookies are just headers in HTTP.

@janthoXO
Copy link
Contributor Author

janthoXO commented Jun 22, 2024

I've looked into the fetch docs and it provides the response.headers.get(String) method

It indeed does provide this method. But still if you call the method or in general print all headers the set cookie header is still missing

@janthoXO
Copy link
Contributor Author

janthoXO commented Jun 22, 2024

Screen.Recording.2024-06-22.at.16.32.17.mov

thats a request on the {base_url}/api/public/authenticate endpoint

@Hialus
Copy link
Member

Hialus commented Jun 22, 2024

According to several online sources it seems like you have to set the credentials param to include or same-origin for the fetch api to enable cookies.
So like this:

fetch(url, {
  credentials: 'include'
})

@janthoXO
Copy link
Contributor Author

According to several online sources it seems like you have to set the credentials param to include or same-origin for the fetch api to enable cookies. So like this:

fetch(url, {
  credentials: 'include'
})

I'm aware that the fetch API does support cookies. But the vscode extension environment does not. So cookies wont work in a VSCode extension.

Copy link

github-actions bot commented Jul 5, 2024

There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions.

@github-actions github-actions bot added the stale label Jul 5, 2024
@github-actions github-actions bot closed this Jul 19, 2024
@janthoXO janthoXO linked an issue Sep 7, 2024 that may be closed by this pull request
1 task
@janthoXO janthoXO reopened this Sep 21, 2024
@github-actions github-actions bot added the client Pull requests that update TypeScript code. (Added Automatically!) label Sep 21, 2024
@janthoXO janthoXO added the deploy:artemis-test9 Testserver for Project Theia label Oct 8, 2024
…mation-on-redirect' into feature/vscode-plugin
@janthoXO janthoXO temporarily deployed to artemis-test9.artemis.cit.tum.de October 8, 2024 15:36 — with GitHub Actions Inactive
@github-actions github-actions bot added tests lock:artemis-test9 and removed deploy:artemis-test9 Testserver for Project Theia labels Oct 8, 2024
@janthoXO janthoXO marked this pull request as ready for review October 8, 2024 16:23
@janthoXO janthoXO requested a review from a team as a code owner October 8, 2024 16:23
Copy link

coderabbitai bot commented Oct 8, 2024

Walkthrough

The changes in this pull request introduce multiple enhancements across various components of the application. A new CSRF filter, CsrfArtemisFilter, is added to the security configuration, and modifications are made to the CORS handling in WebConfigurer. The JWT handling is improved with new methods for extraction and validation. Additionally, several new methods are added to services and resources related to programming exercises and authentication. Angular components are updated to accommodate new functionalities, such as a CSRF interceptor and modifications to the UI for programming exercises.

Changes

File Change Summary
src/main/java/de/tum/cit/aet/artemis/core/config/SecurityConfiguration.java Added CsrfArtemisFilter, updated filter chain to include it before UsernamePasswordAuthenticationFilter.
src/main/java/de/tum/cit/aet/artemis/core/config/WebConfigurer.java Enhanced logging in corsFilter method, updated conditions for registering the CORS filter.
src/main/java/de/tum/cit/aet/artemis/core/config/websocket/WebsocketConfiguration.java Modified JWT validation logic in httpSessionHandshakeInterceptor method.
src/main/java/de/tum/cit/aet/artemis/core/security/filter/CsrfArtemisFilter.java Introduced a new CSRF filter class with custom filtering logic.
src/main/java/de/tum/cit/aet/artemis/core/security/jwt/JWTCookieService.java Simplified constructor, added buildTheiaCookie method, updated cookie attributes.
src/main/java/de/tum/cit/aet/artemis/core/security/jwt/JWTFilter.java Refactored JWT extraction logic, introduced extractValidJwt method.
src/main/java/de/tum/cit/aet/artemis/core/security/jwt/TokenProvider.java Added createToken method with duration and tools parameters, updated existing token creation method.
src/main/java/de/tum/cit/aet/artemis/core/web/open/PublicUserJwtResource.java Changed authorize method return type to include access token, added getTheiaToken method.
src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseBuildConfig.java Added @JsonIgnoreProperties annotation to ignore programmingExercise during serialization.
src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseBuildConfigRepository.java Added default method findByExerciseIdElseThrow.
src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseResource.java Added methods getBuildConfig and getExerciseByProjectKey, updated constructor.
src/main/webapp/app/app.module.ts Added CsrfInterceptor to the Angular application module.
src/main/webapp/app/core/auth/account.service.ts Introduced rekeyCookieToBearerToken method.
src/main/webapp/app/core/csrf/csrf.interceptor.ts Created new CsrfInterceptor class for handling CSRF protection in HTTP requests.
src/main/webapp/app/exercises/programming/manage/services/programming-exercise.service.ts Added getBuildConfig method to retrieve build configuration.
src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.html Removed online IDE button for programming exercises.
src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.ts Removed properties and logic related to Theia profile.
src/main/webapp/app/shared/components/code-button/code-button.component.html Added a new button for starting an online IDE, conditionally rendered.
src/main/webapp/app/shared/components/code-button/code-button.component.ts Integrated support for PROFILE_THEIA, added related properties and methods.
src/test/javascript/spec/component/overview/exercise-details/exercise-details-student-actions.component.spec.ts Removed tests related to the visibility of the "start theia" button.
src/test/javascript/spec/component/shared/code-button.component.spec.ts Added new tests for the visibility of the "start theia" button based on conditions.
src/test/javascript/spec/helpers/mocks/service/mock-account.service.ts Added rekeyCookieToBearerToken method returning an observable.
src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise.service.ts Added getBuildConfig method to the mock service.

Possibly related PRs

Suggested labels

bugfix, feature

Suggested reviewers

  • krusche
  • florian-glombik
  • BBesrour
  • EneaGore
  • pzdr7
  • SimonEntholzer

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@janthoXO janthoXO added the deploy:artemis-test9 Testserver for Project Theia label Oct 8, 2024
@janthoXO janthoXO temporarily deployed to artemis-test9.artemis.cit.tum.de October 8, 2024 16:36 — with GitHub Actions Inactive
@github-actions github-actions bot added lock:artemis-test9 and removed deploy:artemis-test9 Testserver for Project Theia labels Oct 8, 2024
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 34

🧹 Outside diff range comments (3)
src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseBuildConfig.java (1)

Line range hint 242-247: Potential security concern in toString() method

The toString() method includes sensitive information like buildScript. Consider excluding sensitive data from the toString() output to prevent accidental exposure of this information in logs or error messages.

You could modify the toString() method to exclude or mask sensitive information. For example:

@Override
public String toString() {
    return "BuildJobConfig{" + "id=" + getId() + ", sequentialTestRuns=" + sequentialTestRuns + ", branch='" + branch + '\'' 
            + ", checkoutSolutionRepository=" + checkoutSolutionRepository + ", checkoutPath='" + checkoutPath + '\'' 
            + ", timeoutSeconds=" + timeoutSeconds + ", dockerFlags='" + dockerFlags + '\'' + ", testwiseCoverageEnabled=" + testwiseCoverageEnabled
            + ", theiaImage='" + theiaImage + '\'' + ", allowBranching=" + allowBranching + ", branchRegex='" + branchRegex + '\'' + '}';
}
src/main/java/de/tum/cit/aet/artemis/core/config/SecurityConfiguration.java (1)

Line range hint 1-279: Overall assessment: Changes align with PR objectives and enhance security.

The introduction of CsrfArtemisFilter and its integration into the security filter chain aligns well with the PR objectives of enhancing CSRF security. The implementation follows good coding practices and maintains the single responsibility principle for the affected method.

To fully align with our coding guidelines, consider implementing constructor injection for the new CsrfArtemisFilter as suggested earlier. This change would improve testability and adhere more closely to our dependency injection practices.

The changes effectively contribute to the overall security enhancements mentioned in the PR objectives, particularly in strengthening CSRF protection.

src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.ts (1)

Line range hint 301-369: Improved feedback request conditions in assureConditionsSatisfied method.

The assureConditionsSatisfied method has been updated with new conditions for requesting automatic non-graded feedback. This includes checks for:

  1. Programming exercise specific conditions
  2. Due date check
  3. Previous feedback request check
  4. Rate limiting (max 10 requests)
  5. Check for existing Athena result on the latest submission

These additions improve the robustness of the feedback request process.

However, consider extracting some of these checks into separate methods to improve readability and maintainability.

For example:

private isProgrammingExerciseConditionsSatisfied(): boolean {
    if (this.exercise.type === ExerciseType.PROGRAMMING) {
        const latestResult = this.gradedParticipation?.results && this.gradedParticipation.results.find(({ assessmentType }) => assessmentType === AssessmentType.AUTOMATIC);
        const someHiddenTestsPassed = latestResult?.score !== undefined;
        const testsNotPassedWarning = this.translateService.instant('artemisApp.exercise.notEnoughPoints');
        if (!someHiddenTestsPassed) {
            window.alert(testsNotPassedWarning);
            return false;
        }
    }
    return true;
}

private isWithinDueDate(): boolean {
    const afterDueDate = !this.exercise.dueDate || dayjs().isSameOrAfter(this.exercise.dueDate);
    if (afterDueDate) {
        const dueDateWarning = this.translateService.instant('artemisApp.exercise.feedbackRequestAfterDueDate');
        this.alertService.warning(dueDateWarning);
        return false;
    }
    return true;
}

// ... other extracted methods

assureConditionsSatisfied(): boolean {
    this.updateParticipations();
    return this.isProgrammingExerciseConditionsSatisfied() &&
           this.isWithinDueDate() &&
           this.isNotAlreadyRequested() &&
           this.isWithinRateLimit() &&
           !this.hasAthenaResultForlatestSubmission();
}

This refactoring would make the assureConditionsSatisfied method more readable and easier to maintain.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE

📥 Commits

Files that changed from the base of the PR and between b6b47e7 and 98b7184.

⛔ Files ignored due to path filters (2)
  • src/main/resources/config/application-dev.yml is excluded by !**/*.yml
  • src/main/resources/config/application.yml is excluded by !**/*.yml
📒 Files selected for processing (23)
  • src/main/java/de/tum/cit/aet/artemis/core/config/SecurityConfiguration.java (3 hunks)
  • src/main/java/de/tum/cit/aet/artemis/core/config/WebConfigurer.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/core/config/websocket/WebsocketConfiguration.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/core/security/filter/CsrfArtemisFilter.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/core/security/jwt/JWTCookieService.java (3 hunks)
  • src/main/java/de/tum/cit/aet/artemis/core/security/jwt/JWTFilter.java (2 hunks)
  • src/main/java/de/tum/cit/aet/artemis/core/security/jwt/TokenProvider.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/core/web/open/PublicUserJwtResource.java (5 hunks)
  • src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseBuildConfig.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseBuildConfigRepository.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseResource.java (7 hunks)
  • src/main/webapp/app/app.module.ts (2 hunks)
  • src/main/webapp/app/core/auth/account.service.ts (1 hunks)
  • src/main/webapp/app/core/csrf/csrf.interceptor.ts (1 hunks)
  • src/main/webapp/app/exercises/programming/manage/services/programming-exercise.service.ts (2 hunks)
  • src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.html (0 hunks)
  • src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.ts (1 hunks)
  • src/main/webapp/app/shared/components/code-button/code-button.component.html (1 hunks)
  • src/main/webapp/app/shared/components/code-button/code-button.component.ts (6 hunks)
  • src/test/javascript/spec/component/overview/exercise-details/exercise-details-student-actions.component.spec.ts (0 hunks)
  • src/test/javascript/spec/component/shared/code-button.component.spec.ts (5 hunks)
  • src/test/javascript/spec/helpers/mocks/service/mock-account.service.ts (1 hunks)
  • src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise.service.ts (2 hunks)
💤 Files with no reviewable changes (2)
  • src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.html
  • src/test/javascript/spec/component/overview/exercise-details/exercise-details-student-actions.component.spec.ts
🧰 Additional context used
📓 Path-based instructions (21)
src/main/java/de/tum/cit/aet/artemis/core/config/SecurityConfiguration.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

src/main/java/de/tum/cit/aet/artemis/core/config/WebConfigurer.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

src/main/java/de/tum/cit/aet/artemis/core/config/websocket/WebsocketConfiguration.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

src/main/java/de/tum/cit/aet/artemis/core/security/filter/CsrfArtemisFilter.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

src/main/java/de/tum/cit/aet/artemis/core/security/jwt/JWTCookieService.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

src/main/java/de/tum/cit/aet/artemis/core/security/jwt/JWTFilter.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

src/main/java/de/tum/cit/aet/artemis/core/security/jwt/TokenProvider.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

src/main/java/de/tum/cit/aet/artemis/core/web/open/PublicUserJwtResource.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseBuildConfig.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseBuildConfigRepository.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseResource.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

src/main/webapp/app/app.module.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/core/auth/account.service.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/core/csrf/csrf.interceptor.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/exercises/programming/manage/services/programming-exercise.service.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/components/code-button/code-button.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/main/webapp/app/shared/components/code-button/code-button.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/test/javascript/spec/component/shared/code-button.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/test/javascript/spec/helpers/mocks/service/mock-account.service.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise.service.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

📓 Learnings (3)
src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseResource.java (3)
Learnt from: Hialus
PR: ls1intum/Artemis#8607
File: src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseResource.java:64-64
Timestamp: 2024-06-15T20:04:18.637Z
Learning: For the Artemis project, import statements are automatically managed by formatters and should not be commented on.
Learnt from: Hialus
PR: ls1intum/Artemis#8607
File: src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseResource.java:64-64
Timestamp: 2024-10-08T15:35:42.972Z
Learning: For the Artemis project, import statements are automatically managed by formatters and should not be commented on.
Learnt from: Hialus
PR: ls1intum/Artemis#8607
File: src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseResource.java:64-64
Timestamp: 2024-10-08T15:35:48.767Z
Learning: For the Artemis project, import statements are automatically managed by formatters and should not be commented on.
src/main/webapp/app/core/auth/account.service.ts (4)
Learnt from: iyannsch
PR: ls1intum/Artemis#9379
File: src/main/webapp/app/core/auth/account.service.ts:383-390
Timestamp: 2024-10-08T11:30:31.107Z
Learning: In `src/main/webapp/app/core/auth/account.service.ts`, error handling for service methods like `rekeyCookieToBearerToken` should be handled in the using component, not within the service method.
Learnt from: iyannsch
PR: ls1intum/Artemis#9379
File: src/main/webapp/app/core/auth/account.service.ts:383-390
Timestamp: 2024-10-08T15:35:52.595Z
Learning: In `src/main/webapp/app/core/auth/account.service.ts`, error handling for service methods like `rekeyCookieToBearerToken` should be handled in the using component, not within the service method.
Learnt from: iyannsch
PR: ls1intum/Artemis#9379
File: src/main/webapp/app/core/auth/account.service.ts:383-390
Timestamp: 2024-10-08T15:35:48.768Z
Learning: In 'src/main/webapp/app/core/auth/account.service.ts', the `rekeyCookieToBearerToken` method receives a plain string response from the server (not JSON), hence `responseType` should be set to `'text'`.
Learnt from: iyannsch
PR: ls1intum/Artemis#9379
File: src/main/webapp/app/core/auth/account.service.ts:383-390
Timestamp: 2024-10-04T12:59:06.788Z
Learning: In 'src/main/webapp/app/core/auth/account.service.ts', the `rekeyCookieToBearerToken` method receives a plain string response from the server (not JSON), hence `responseType` should be set to `'text'`.
src/main/webapp/app/shared/components/code-button/code-button.component.html (3)
Learnt from: iyannsch
PR: ls1intum/Artemis#9379
File: src/main/webapp/app/shared/components/code-button/code-button.component.html:102-0
Timestamp: 2024-10-08T15:35:48.768Z
Learning: For the `startOnlineIDE()` function that causes an instantaneous redirect, adding a loading state to its button is unnecessary.
Learnt from: iyannsch
PR: ls1intum/Artemis#9379
File: src/main/webapp/app/shared/components/code-button/code-button.component.html:102-0
Timestamp: 2024-10-08T15:35:42.972Z
Learning: For the `startOnlineIDE()` function that causes an instantaneous redirect, adding a loading state to its button is unnecessary.
Learnt from: iyannsch
PR: ls1intum/Artemis#9379
File: src/main/webapp/app/shared/components/code-button/code-button.component.html:102-0
Timestamp: 2024-09-28T13:48:54.189Z
Learning: For the `startOnlineIDE()` function that causes an instantaneous redirect, adding a loading state to its button is unnecessary.
🪛 Biome
src/main/webapp/app/shared/components/code-button/code-button.component.ts

[error] 363-363: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)

src/test/javascript/spec/component/shared/code-button.component.spec.ts

[error] 602-602: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)

🔇 Additional comments (25)
src/main/webapp/app/core/csrf/csrf.interceptor.ts (2)

1-2: LGTM: Imports are correct and follow guidelines.

The import statements are appropriate for the CSRF interceptor implementation and use single quotes as per the coding guidelines.


4-4: LGTM: Class declaration follows best practices and guidelines.

The CsrfInterceptor class is correctly exported and implements the HttpInterceptor interface. The class name uses PascalCase, adhering to the coding guidelines.

src/test/javascript/spec/helpers/mocks/service/mock-programming-exercise.service.ts (1)

5-5: LGTM: New import added correctly.

The new import for ProgrammingExerciseBuildConfig is correctly added and follows the existing import style in the file.

src/main/java/de/tum/cit/aet/artemis/core/security/jwt/JWTCookieService.java (2)

20-22: Improved constructor design

The simplified constructor aligns better with the single responsibility principle by removing the unnecessary Environment dependency. This change enhances the class's focus on JWT cookie-related operations.


Line range hint 1-72: Overall assessment: Well-structured and secure implementation

The changes to JWTCookieService have improved its design and functionality:

  1. The constructor is now more focused, adhering to the single responsibility principle.
  2. The new buildTheiaCookie method extends functionality while maintaining good coding practices.
  3. Cookie security settings have been enhanced to support cross-site requests securely.

These modifications align well with the provided coding guidelines, particularly in terms of REST API best practices, security considerations, and code organization. The suggestions provided are minor and aimed at further improving flexibility and documentation.

src/main/webapp/app/app.module.ts (1)

29-29: LGTM: Import statement correctly added

The import of HTTP_INTERCEPTORS from @angular/common/http is correctly placed and follows Angular style guidelines. This import is necessary for adding the CSRF interceptor to the providers.

src/main/java/de/tum/cit/aet/artemis/core/config/WebConfigurer.java (2)

123-124: LGTM: Improved CORS configuration check.

The expanded conditional check now considers both getAllowedOrigins and getAllowedOriginPatterns. This change provides more flexibility in CORS configuration and aligns with the PR objectives to adjust CORS settings.


120-124: Overall assessment: CORS configuration improvements look good.

The changes to the corsFilter method enhance the CORS configuration flexibility and provide better visibility into the settings. These modifications align well with the PR objectives to adjust CORS configuration and improve security measures.

A minor suggestion was made to adjust the log level for the new logging statements. Otherwise, the changes appear to be correct and improve the functionality as intended.

src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingExerciseBuildConfig.java (1)

28-28: LGTM: Improved JSON serialization handling

The addition of @JsonIgnoreProperties(value = { "programmingExercise" }) is a good change. It prevents the programmingExercise field from being included in JSON representations of this class, which can help avoid circular references during serialization. This aligns with the REST API best practice of using minimal DTOs as mentioned in the coding guidelines.

src/main/java/de/tum/cit/aet/artemis/core/config/SecurityConfiguration.java (1)

38-38: LGTM: New import for CsrfArtemisFilter.

The import statement for CsrfArtemisFilter is correctly placed and follows the naming conventions.

src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.ts (3)

14-14: LGTM: Import statement updated correctly.

The removal of the faDesktop icon import is consistent with the changes made to the component, likely related to the removal of Theia-related functionality.


Line range hint 93-126: LGTM: Theia-related logic removed from ngOnInit.

The removal of Theia-related logic from the ngOnInit method is consistent with the overall changes to the component. The remaining initialization logic for other exercise types looks correct.


21-21: Verify usage of remaining IDE-related properties.

The removal of Theia-related properties (theiaEnabled and theiaPortalURL) is consistent with the changes mentioned in the summary. However, localVCEnabled and athenaEnabled properties are still present.

Please ensure that these remaining properties are still used and necessary. If not, consider removing them as well.

✅ Verification successful

IDE-related properties are still in use.

The properties localVCEnabled and athenaEnabled are found in multiple components within the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify usage of localVCEnabled and athenaEnabled properties
rg --type typescript "localVCEnabled|athenaEnabled" src/main/webapp

Length of output: 104


Script:

#!/bin/bash
# Verify usage of localVCEnabled and athenaEnabled properties in TypeScript files
rg "localVCEnabled|athenaEnabled" src/main/webapp --glob "*.ts"

Length of output: 5562

src/main/webapp/app/exercises/programming/manage/services/programming-exercise.service.ts (2)

30-30: LGTM: New import statement is correctly implemented.

The import for ProgrammingExerciseBuildConfig is properly placed and follows the correct naming convention.


30-30: Summary: Excellent addition of build configuration retrieval functionality.

The new import and getBuildConfig method have been implemented cleanly and efficiently. They follow Angular best practices and the existing coding style of the file. These changes enhance the service's capabilities without disrupting the existing codebase.

Great job on maintaining code quality while expanding functionality!

Also applies to: 664-667

src/main/java/de/tum/cit/aet/artemis/core/security/filter/CsrfArtemisFilter.java (1)

38-38: Verify All API Endpoints Are Protected

The shouldNotFilter method excludes requests that do not start with /api/, meaning only /api/** endpoints are protected by this filter. It's important to verify that there are no other endpoints that require CSRF protection but do not start with /api/.

Run the following script to identify endpoints outside /api/:

This script searches for REST controller methods with mappings not starting with /api/. Verify if any of these endpoints should be protected by the CSRF filter.

src/main/java/de/tum/cit/aet/artemis/core/security/jwt/JWTFilter.java (1)

36-41: Handle Missing or Invalid JWT Gracefully

Ensure that the application handles missing or invalid JWT tokens appropriately. If the token is invalid or absent, and the request requires authentication, the user should receive a proper error response.

To verify the behavior, you can test unauthorized requests:

src/main/java/de/tum/cit/aet/artemis/core/security/jwt/TokenProvider.java (1)

98-99: Ensure correct method delegation

The createToken(Authentication authentication, boolean rememberMe) method now delegates token creation to the new method with a duration parameter. This change maintains the original functionality while promoting code reuse.

src/main/java/de/tum/cit/aet/artemis/core/web/open/PublicUserJwtResource.java (6)

5-7: Approval of Added Imports

The imports of Duration, Date, and Map are appropriate and necessary for the new functionality implemented in the class.


33-33: Approval of Added Import for RequestParam

The import of RequestParam is correctly added to support request parameter handling in the new method getTheiaToken.


40-40: Approval of Security-Related Imports

The added imports for EnforceAtLeastStudent, JWTFilter, and TokenProvider are necessary for implementing the enhanced security features.

Also applies to: 43-44


59-60: Proper Use of Dependency Injection for TokenProvider

Injecting TokenProvider via constructor injection aligns with best practices and the project's dependency injection guidelines.

Also applies to: 65-67


59-60: Field Access Modifiers Align with Best Practices

The tokenProvider field is declared as private final, adhering to the principle of least privilege and ensuring immutability.


119-122: Verification of JWT Extraction Logic

Please verify that the JWTFilter.extractValidJwt method correctly handles edge cases and does not introduce security vulnerabilities.

Verification Script:

This script searches for the implementation and all usages of extractValidJwt to help ensure it is implemented securely and used consistently.

src/main/webapp/app/shared/components/code-button/code-button.component.ts (1)

360-375: ⚠️ Potential issue

Avoid using non-null assertion operator; handle potential null values

Using the non-null assertion operator ! in this.exercise.id! (line 363) can lead to runtime errors if this.exercise.id is null or undefined. It's safer to add a null check to ensure this.exercise.id is defined before using it.

Apply this diff to fix the issue:

 private initTheia(profileInfo: ProfileInfo) {
     if (profileInfo.activeProfiles?.includes(PROFILE_THEIA) && this.exercise) {
+        if (this.exercise.id) {
             this.programmingExerciseService.getBuildConfig(this.exercise.id).subscribe((buildConfig) => {
                 if (this.exercise) {
                     this.exercise.buildConfig = buildConfig;
                     // Set variables now, sanitize later on
                     this.theiaPortalURL = profileInfo.theiaPortalURL ?? '';
                     // Verify that all conditions are met
                     if (this.theiaPortalURL !== '' && this.exercise.allowOnlineIde && this.exercise.buildConfig?.theiaImage) {
                         this.theiaEnabled = true;
                     }
                 }
             });
+        }
     }
 }
⛔ Skipped due to learnings
Learnt from: iyannsch
PR: ls1intum/Artemis#9379
File: src/main/webapp/app/shared/components/code-button/code-button.component.ts:143-170
Timestamp: 2024-10-08T15:35:42.972Z
Learning: In `code-button.component.ts`, `this.exercise.id` is checked before use, so it's acceptable to use the non-null assertion operator `!` on `this.exercise.id!`.
Learnt from: iyannsch
PR: ls1intum/Artemis#9379
File: src/main/webapp/app/shared/components/code-button/code-button.component.ts:143-170
Timestamp: 2024-09-30T08:34:49.363Z
Learning: In `code-button.component.ts`, `this.exercise.id` is checked before use, so it's acceptable to use the non-null assertion operator `!` on `this.exercise.id!`.
Learnt from: florian-glombik
PR: ls1intum/Artemis#8858
File: src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.ts:97-107
Timestamp: 2024-10-08T15:35:48.768Z
Learning: In the context of PR ls1intum/Artemis#8858, avoid suggesting the removal of non-null assertions.
Learnt from: florian-glombik
PR: ls1intum/Artemis#8858
File: src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.ts:97-107
Timestamp: 2024-10-08T15:35:42.972Z
Learning: In the context of PR ls1intum/Artemis#8858, avoid suggesting the removal of non-null assertions.
Learnt from: florian-glombik
PR: ls1intum/Artemis#8858
File: src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.ts:97-107
Timestamp: 2024-07-09T19:10:33.905Z
Learning: In the context of PR ls1intum/Artemis#8858, avoid suggesting the removal of non-null assertions.
Learnt from: florian-glombik
PR: ls1intum/Artemis#8858
File: src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.ts:97-111
Timestamp: 2024-10-08T15:35:52.595Z
Learning: In the context of PR ls1intum/Artemis#8858, non-null assertions should not be flagged or suggested for removal.
Learnt from: florian-glombik
PR: ls1intum/Artemis#8858
File: src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.ts:97-111
Timestamp: 2024-07-09T19:06:18.665Z
Learning: In the context of PR ls1intum/Artemis#8858, non-null assertions should not be flagged or suggested for removal.
🧰 Tools
🪛 Biome

[error] 363-363: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)

Comment on lines +5 to +11
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#employing-custom-request-headers-for-ajaxapi
// Clone the request and add the CSRF token header
const csrfReq = req.clone({ headers: req.headers.set('X-ARTEMIS-CSRF', 'Dennis ist schuld') });

return next.handle(csrfReq);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

⚠️ Potential issue

Security Issue: Hardcoded CSRF Token Detected

The intercept method in csrf.interceptor.ts uses a hardcoded CSRF token 'Dennis ist schuld'. This practice compromises the security mechanism intended to prevent CSRF attacks.

Recommendation:

  • Replace the hardcoded token with a dynamically generated CSRF token.

    import { Injectable } from '@angular/core';
    import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
    import { Observable } from 'rxjs';
    
    @Injectable()
    export class CsrfInterceptor implements HttpInterceptor {
        constructor(private csrfService: CsrfService) {}
    
        intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
            const csrfToken = this.csrfService.getToken();
            const csrfReq = req.clone({ headers: req.headers.set('X-ARTEMIS-CSRF', csrfToken) });
            return next.handle(csrfReq);
        }
    }

This change ensures that each session utilizes a unique and securely generated CSRF token, significantly strengthening the application's defense against CSRF attacks.

🔗 Analysis chain

Security concern: Replace hardcoded CSRF token with a dynamically generated value.

The intercept method correctly adds a custom CSRF header to all requests. However, using a hardcoded value ('Dennis ist schuld') for the CSRF token significantly reduces its effectiveness in preventing CSRF attacks.

To improve security, consider the following:

  1. Generate a unique CSRF token for each session.
  2. Store the token securely (e.g., in a service or secure storage).
  3. Retrieve the token dynamically in the interceptor.

Here's a suggested improvement:

import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class CsrfInterceptor implements HttpInterceptor {
    constructor(private csrfService: CsrfService) {}

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const csrfToken = this.csrfService.getToken();
        const csrfReq = req.clone({ headers: req.headers.set('X-ARTEMIS-CSRF', csrfToken) });
        return next.handle(csrfReq);
    }
}

This approach ensures that a unique and securely generated CSRF token is used for each request, significantly enhancing the application's security against CSRF attacks.

To ensure that the CSRF token is not hardcoded in other parts of the codebase, run the following script:

This script will help identify any other instances where the CSRF token might be hardcoded, allowing for a comprehensive security review.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for hardcoded CSRF tokens in TypeScript files
rg --type typescript "'X-ARTEMIS-CSRF'" -C 3

Length of output: 87


Script:

#!/bin/bash
# Search for hardcoded CSRF tokens in TypeScript files using file extensions
rg --glob '*.ts' --glob '*.tsx' 'X-ARTEMIS-CSRF' -C 3

Length of output: 854

Comment on lines +39 to +47
/**
* Find a build config by its programming exercise's id and throw an Exception if it cannot be found
*
* @param programmingExerciseId of the programming exercise.
* @return The programming exercise related to the given id
*/
default ProgrammingExerciseBuildConfig findByExerciseIdElseThrow(long programmingExerciseId) {
return getValueElseThrow(findByProgrammingExerciseId(programmingExerciseId));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

LGTM! Consider enhancing the Javadoc.

The new method findByExerciseIdElseThrow is well-implemented and aligns with the coding guidelines. It promotes code reuse, has a clear single responsibility, and uses appropriate error handling.

Consider enhancing the Javadoc by specifying the exception type that may be thrown:

 /**
  * Find a build config by its programming exercise's id and throw an Exception if it cannot be found
  *
  * @param programmingExerciseId of the programming exercise.
  * @return The programming exercise related to the given id
+ * @throws javax.persistence.EntityNotFoundException if the build config cannot be found
  */
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Find a build config by its programming exercise's id and throw an Exception if it cannot be found
*
* @param programmingExerciseId of the programming exercise.
* @return The programming exercise related to the given id
*/
default ProgrammingExerciseBuildConfig findByExerciseIdElseThrow(long programmingExerciseId) {
return getValueElseThrow(findByProgrammingExerciseId(programmingExerciseId));
}
/**
* Find a build config by its programming exercise's id and throw an Exception if it cannot be found
*
* @param programmingExerciseId of the programming exercise.
* @return The programming exercise related to the given id
* @throws javax.persistence.EntityNotFoundException if the build config cannot be found
*/
default ProgrammingExerciseBuildConfig findByExerciseIdElseThrow(long programmingExerciseId) {
return getValueElseThrow(findByProgrammingExerciseId(programmingExerciseId));
}

@@ -42,4 +42,5 @@ export class MockAccountService implements IAccountService {
addSshPublicKey = (sshPublicKey: string) => of();
getVcsAccessToken = (participationId: number) => of();
createVcsAccessToken = (participationId: number) => of();
rekeyCookieToBearerToken = () => of();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

LGTM! Consider adding a TODO comment for future implementation.

The rekeyCookieToBearerToken method is correctly implemented as a stub for the mock service. It follows the existing coding style and is consistent with other methods in the class.

To improve clarity for future developers, consider adding a TODO comment:

rekeyCookieToBearerToken = () => of(); // TODO: Implement mock behavior if needed for specific tests

@@ -18,6 +19,7 @@ export class MockProgrammingExerciseService {
deleteTasksWithSolutionEntries = (exerciseId: number) => of();
getDiffReport = (exerciseId: number) => of({});
getBuildLogStatistics = (exerciseId: number) => of({});
getBuildConfig = (exerciseId: number) => of({});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider improving type safety and mock implementation.

While the new getBuildConfig method follows the pattern of other methods in this mock service, there are a few suggestions for improvement:

  1. Add a return type annotation to enhance type safety. It should likely be Observable<ProgrammingExerciseBuildConfig>.
  2. Consider returning a mock ProgrammingExerciseBuildConfig object instead of an empty object to better simulate the real service behavior.

Here's a suggested improvement:

getBuildConfig = (exerciseId: number): Observable<ProgrammingExerciseBuildConfig> => of({
  // Add mock properties here
  // For example:
  // id: exerciseId,
  // someProperty: 'mockValue',
});

This change would make the mock more useful for testing scenarios that depend on the structure of ProgrammingExerciseBuildConfig.

Comment on lines +36 to +45
/**
* Builds the cookie with Theia flag
*
* @param duration the duration of the cookie and the jwt
* @return the login ResponseCookie containing the JWT
*/
public ResponseCookie buildTheiaCookie(long duration) {
String jwt = tokenProvider.createToken(SecurityContextHolder.getContext().getAuthentication(), duration, "THEIA");
return buildJWTCookie(jwt, Duration.of(duration, ChronoUnit.MILLIS));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

New method for Theia-specific cookie creation

The buildTheiaCookie method is a well-designed addition that follows good coding practices:

  • It adheres to the single responsibility principle.
  • It reuses existing code (buildJWTCookie), following the DRY principle.
  • The method signature uses a primitive type for the duration parameter.

Consider enhancing the method's documentation by specifying the unit of the duration parameter (e.g., milliseconds, seconds) for clarity.

Comment on lines +393 to +395
form.method = 'GET';
form.action = this.theiaPortalURL;
form.target = newWindow.name;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use 'POST' method for form submission to protect sensitive data

Currently, the form uses the 'GET' method, which appends form data to the URL. Since sensitive data like gitUri and artemisToken are being submitted, it's recommended to use 'POST' to prevent these values from appearing in the URL and browser history.

Apply this diff to fix the issue:

-form.method = 'GET';
+form.method = 'POST';
 form.action = this.theiaPortalURL;
 form.target = newWindow.name;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
form.method = 'GET';
form.action = this.theiaPortalURL;
form.target = newWindow.name;
form.method = 'POST';
form.action = this.theiaPortalURL;
form.target = newWindow.name;

}

async startOnlineIDE() {
const artemisToken: string = (await this.accountService.rekeyCookieToBearerToken().toPromise()) ?? '';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle potential errors during token retrieval

The asynchronous call to retrieve artemisToken may fail, leading to unhandled exceptions. Consider adding error handling to manage potential failures when obtaining the token.

Apply this diff to improve error handling:

-const artemisToken: string = (await this.accountService.rekeyCookieToBearerToken().toPromise()) ?? '';
+let artemisToken: string = '';
+try {
+    artemisToken = (await this.accountService.rekeyCookieToBearerToken().toPromise()) ?? '';
+} catch (error) {
+    console.error('Failed to retrieve Artemis token:', error);
+    // Optionally, notify the user about the failure
+    return;
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const artemisToken: string = (await this.accountService.rekeyCookieToBearerToken().toPromise()) ?? '';
let artemisToken: string = '';
try {
artemisToken = (await this.accountService.rekeyCookieToBearerToken().toPromise()) ?? '';
} catch (error) {
console.error('Failed to retrieve Artemis token:', error);
// Optionally, notify the user about the failure
return;
}

if (Object.hasOwn(data, key)) {
const input = Array.from(inputs).find((input) => input.name === key);
expect(input).toBeDefined();
expect(input!.value).toBe(data[key]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid using the non-null assertion operator '!'; replace with a safer alternative

The use of the non-null assertion operator ! on input can lead to runtime errors if input is null or undefined. Since you have already asserted that input is defined with expect(input).toBeDefined();, you can safely access input.value without the non-null assertion.

Apply this diff to fix the issue:

- expect(input!.value).toBe(data[key]);
+ expect(input.value).toBe(data[key]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expect(input!.value).toBe(data[key]);
expect(input.value).toBe(data[key]);
🧰 Tools
🪛 Biome

[error] 602-602: Forbidden non-null assertion.

Unsafe fix: Replace with optional chain operator ?. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator

(lint/style/noNonNullAssertion)

Comment on lines +502 to +515
/**
* GET /programming-exercises/:exerciseId/build-config : get the build config of "exerciseId" programmingExercise.
*
* @param exerciseId the id of the programmingExercise to retrieve the config for
* @return the ResponseEntity with status 200 (OK) and with body the programmingExerciseBuildConfig, or with status 404 (Not Found)
*/
@GetMapping("programming-exercises/{exerciseId}/build-config")
@EnforceAtLeastStudentInExercise
public ResponseEntity<ProgrammingExerciseBuildConfig> getBuildConfig(@PathVariable long exerciseId) {
log.debug("REST request to get build config of ProgrammingExercise : {}", exerciseId);
var buildConfig = programmingExerciseBuildConfigRepository.findByExerciseIdElseThrow(exerciseId);

return ResponseEntity.ok().body(buildConfig);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid returning entities directly from REST endpoints

Returning the ProgrammingExerciseBuildConfig entity directly might expose internal implementation details and sensitive information. According to the coding guidelines dtos:{java_records,no_entities,min_data,single_resp}, it's recommended to use a Data Transfer Object (DTO) that contains only the necessary data for the client.

Consider creating a DTO for ProgrammingExerciseBuildConfig and modifying the method to return this DTO. This will help in maintaining a clear separation between your internal data models and the API contracts.

Comment on lines +556 to +569
/**
* GET /programming-exercises : Queries a programming exercise by its project key.
*
* @return the ProgrammingExercise of this project key in an ResponseEntity or 404 Not Found if no exercise exists
*/
@GetMapping("programming-exercises/project-key/{projectKey}")
@EnforceAtLeastStudent
public ResponseEntity<ProgrammingExercise> getExerciseByProjectKey(@PathVariable String projectKey) {
final ProgrammingExercise exercise = programmingExerciseRepository.findOneByProjectKeyOrThrow(projectKey, false);
authCheckService.checkIsAtLeastRoleInExerciseElseThrow(Role.STUDENT, exercise.getId());

return ResponseEntity.ok(exercise);
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid returning entities directly from REST endpoints

Returning the ProgrammingExercise entity directly could lead to the exposure of sensitive data and tight coupling between your API and internal data models. As per the coding guidelines dtos:{java_records,no_entities,min_data,single_resp}, it's advisable to use a DTO that includes only the necessary fields for the client.

Consider creating a ProgrammingExerciseDTO and updating the method to return this DTO:

 public ResponseEntity<ProgrammingExerciseDTO> getExerciseByProjectKey(@PathVariable String projectKey) {
     final ProgrammingExercise exercise = programmingExerciseRepository.findOneByProjectKeyOrThrow(projectKey, false);
     authCheckService.checkIsAtLeastRoleInExerciseElseThrow(Role.STUDENT, exercise.getId());

-    return ResponseEntity.ok(exercise);
+    ProgrammingExerciseDTO exerciseDTO = new ProgrammingExerciseDTO(exercise);
+    return ResponseEntity.ok(exerciseDTO);
 }

Committable suggestion was skipped due to low confidence.

Copy link

There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
client Pull requests that update TypeScript code. (Added Automatically!) config-change Pull requests that change the config in a way that they require a deployment via Ansible. core Pull requests that affect the corresponding module programming Pull requests that affect the corresponding module security Pull requests that address a security vulnerability server Pull requests that update Java code. (Added Automatically!) stale tests
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Add CSRF token VSCode extension WIP
4 participants