From 4b14dd626f9c1f5166dccd43410b640f64d30692 Mon Sep 17 00:00:00 2001 From: joyfaker Date: Wed, 18 Mar 2026 11:34:57 +0800 Subject: [PATCH] Add files via upload --- OAuth2LoginSuccessHandler.java | 82 ++++++++++++++++++++++++++ SecurityConfig.java | 49 ++++++++++++++++ vulnerable.cpp | 101 +++++++++++++++++++++++++++++++++ vulnerable_test.c | 68 ++++++++++++++++++++++ 4 files changed, 300 insertions(+) create mode 100644 OAuth2LoginSuccessHandler.java create mode 100644 SecurityConfig.java create mode 100644 vulnerable.cpp create mode 100644 vulnerable_test.c diff --git a/OAuth2LoginSuccessHandler.java b/OAuth2LoginSuccessHandler.java new file mode 100644 index 0000000..bf05fe2 --- /dev/null +++ b/OAuth2LoginSuccessHandler.java @@ -0,0 +1,82 @@ +package com.softsafe.sast.platform.config.security; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.softsafe.sast.platform.config.OAuthUserExtractor; +import com.softsafe.sast.platform.dto.AuthUserReqDTO; +import com.softsafe.sast.platform.entity.UserInfo; +import com.softsafe.sast.platform.service.TokenService; +import com.softsafe.sast.platform.service.UserService; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.client.*; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.Map; + +@Component +@RequiredArgsConstructor +public class OAuth2LoginSuccessHandler implements org.springframework.security.web.authentication.AuthenticationSuccessHandler { + + private final TokenService tokenService; + private final OAuth2AuthorizedClientService authorizedClientService; + private final ObjectMapper objectMapper; + private final UserService userService; + private final OAuthUserExtractor oAuthUserExtractor; + + private final long ACCESS_TOKEN_SECONDS = 60 * 30; // 30min + private final long REFRESH_TOKEN_SECONDS = 60 * 60 * 24 * 7; // 7d + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { + OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication; + + // application user id + AuthUserReqDTO authUserReqDTO = oAuthUserExtractor.extract(oauthToken.getPrincipal(), oauthToken); + UserInfo userInfo = userService.getUserInfoByOauthUserIdAndSourceType( + authUserReqDTO.getId(), + authUserReqDTO.getProvider() + ); + if (userInfo == null){ + userInfo = UserInfo.builder() + .oauthType(authUserReqDTO.getProvider()) + .oauthUserId(authUserReqDTO.getId()) + .oauthUserName(authUserReqDTO.getUsername()) + .build(); + userService.save(userInfo); + } + + // optionally save third-party access token for backend usage (not returned to frontend) + OAuth2AuthorizedClient client = authorizedClientService.loadAuthorizedClient(oauthToken.getAuthorizedClientRegistrationId(), oauthToken.getName()); + if (client != null && client.getAccessToken() != null) { + String externalToken = client.getAccessToken().getTokenValue(); + // TODO save to secure storage if needed + } + try { + String accessJwt = tokenService.createAccessToken(userInfo.getId().toString(), ACCESS_TOKEN_SECONDS); + String refreshJwt = tokenService.createRefreshToken(userInfo.getId().toString(), REFRESH_TOKEN_SECONDS); + Cookie refreshTokenCookie = new Cookie("refreshToken", refreshJwt); + refreshTokenCookie.setHttpOnly(true); + refreshTokenCookie.setSecure(true); + refreshTokenCookie.setPath("/"); + //TODO 配置文件 + refreshTokenCookie.setDomain("lekker-hypertragically-jadwiga.ngrok-free.dev"); + refreshTokenCookie.setMaxAge((int) REFRESH_TOKEN_SECONDS); + response.addCookie(refreshTokenCookie); + + response.setContentType("application/json;charset=UTF-8"); + objectMapper.writeValue(response.getWriter(), Map.of( + "accessToken", accessJwt, + "expiresIn", ACCESS_TOKEN_SECONDS, + "userId", userInfo.getId().toString() + )); + } catch (Exception e) { + throw new ServletException(e); + } + } +} diff --git a/SecurityConfig.java b/SecurityConfig.java new file mode 100644 index 0000000..92bcf04 --- /dev/null +++ b/SecurityConfig.java @@ -0,0 +1,49 @@ +package com.softsafe.sast.platform.config.security; + +import com.softsafe.sast.platform.config.RestAccessDeniedHandler; +import com.softsafe.sast.platform.config.RestAuthenticationEntryPoint; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + private final RestAuthenticationEntryPoint restAuthEntryPoint; + private final RestAccessDeniedHandler restAccessDeniedHandler; + private final OAuth2LoginSuccessHandler oauth2LoginSuccessHandler; + + @Bean + @Order(2) + public SecurityFilterChain applicationSecurity(HttpSecurity http) throws Exception { + + http.exceptionHandling(ex -> ex + .authenticationEntryPoint(restAuthEntryPoint) + .accessDeniedHandler(restAccessDeniedHandler) + ) + .authorizeHttpRequests(authorize -> authorize + .requestMatchers( + "/", "/login", "/login.html", + "/error", + "/github/webhook", + "/api/sast/**", + "/favicon.ico", "/icons/**" + ).permitAll() + .anyRequest().authenticated() + ) + .oauth2Login(oauth2 -> oauth2 + .successHandler(oauth2LoginSuccessHandler) + .failureUrl("/login?error") + ) + .csrf(csrf -> csrf + .ignoringRequestMatchers("/github/webhook", "/api/sast/**") + ); + + return http.build(); + } +} diff --git a/vulnerable.cpp b/vulnerable.cpp new file mode 100644 index 0000000..685e986 --- /dev/null +++ b/vulnerable.cpp @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include + +using namespace std; + +// 1. 硬编码敏感信息 (CWE-798) +// SAST 会扫描变量名(如 api_key, secret)和高熵字符串 +const string AWS_SECRET_KEY = "AKIAIOSFODNN7EXAMPLE"; + +class UserManager { +public: + // 2. 也是硬编码,且作为类成员 + string db_password = "root"; + + // 3. SQL 注入 (CWE-89) + // 直接拼接字符串构建 SQL 查询是 C++ Web 后端常见的严重漏洞 + void queryUser(string userId) { + string query = "SELECT * FROM users WHERE id = '" + userId + "'"; + cout << "Executing query: " << query << endl; + } + + // 4. 命令注入 (CWE-78) + // 允许外部输入直接进入 system() 函数 + void pingHost(string ipAddress) { + string cmd = "ping -c 4 " + ipAddress; + system(cmd.c_str()); + } +}; + +void legacyBufferOverflow(char* input) { + char buffer[10]; + + // 5. 经典的栈缓冲区溢出 (CWE-121) + // 虽然是 C++,但混用 C 风格字符串处理函数非常常见且危险 + strcpy(buffer, input); +} + +void memoryLeakAndRawPointers() { + // 6. 内存泄漏 (CWE-401) + // 使用了 new 但没有 delete + int* data = new int[100]; + data[0] = 10; + + // 抛出异常可能导致 delete 永远不执行 (异常安全问题) + if (data[0] == 10) { + // throw runtime_error("Error occurred!"); + return; + } + + delete[] data; +} + +void iteratorInvalidation() { + vector numbers = {1, 2, 3, 4, 5}; + + // 7. 迭代器失效 (CWE-835/Logic Error) + // 在遍历 vector 时进行 push_back 会导致底层数组重新分配, + // 从而使迭代器失效,导致未定义行为或崩溃。 + for (auto it = numbers.begin(); it != numbers.end(); ++it) { + if (*it == 3) { + numbers.push_back(6); + } + } +} + +void weakRandomness() { + // 8. 弱伪随机数生成器 (CWE-338) + // srand/rand 不适合用于安全相关的随机数生成 + srand(time(NULL)); + int token = rand(); + cout << "Security Token: " << token << endl; +} + +int main(int argc, char* argv[]) { + UserManager um; + + if (argc < 2) { + return 1; + } + + // 模拟攻击路径 + um.queryUser(argv[1]); // 传入 "' OR '1'='1" 即可注入 + + legacyBufferOverflow(argv[1]); + + memoryLeakAndRawPointers(); + + iteratorInvalidation(); + + weakRandomness(); + + // 9. 被除数为零 (CWE-369) + int x = 0; + int y = 100 / x; + + return 0; +} \ No newline at end of file diff --git a/vulnerable_test.c b/vulnerable_test.c new file mode 100644 index 0000000..29da262 --- /dev/null +++ b/vulnerable_test.c @@ -0,0 +1,68 @@ +#include +#include +#include + +// 1. 硬编码凭证 (Hardcoded Credentials) +// SAST工具会扫描特定的变量名(如 password, key)和字符串比较 +void check_admin(char *input_pass) { + if (strcmp(input_pass, "SuperSecretAdminPassword123") == 0) { + printf("Access Granted!\n"); + } +} + +// 2. 内存泄漏 (Memory Leak) & 空指针解引用 (Null Pointer Dereference) +void memory_issues() { + char *ptr = (char *)malloc(50); + + // 错误:没有检查 malloc 是否返回 NULL 就直接使用 + ptr[0] = 'A'; + + // 错误:函数结束前没有调用 free(ptr),导致内存泄漏 + return; +} + +// 3. 释放后使用 (Use After Free) & 双重释放 (Double Free) +void heap_corruption() { + char *data = (char *)malloc(10); + free(data); + + // 错误:内存已被释放,但再次写入数据 + strcpy(data, "Hello"); + + // 错误:再次释放同一块内存 + free(data); +} + +void risky_processing(char *user_input) { + char buffer[10]; + + // 4. 栈缓冲区溢出 (Stack Buffer Overflow) + // 如果 user_input 长度超过 10,会覆盖栈内存 + strcpy(buffer, user_input); + + // 5. 格式化字符串漏洞 (Format String Vulnerability) + // 如果 user_input 包含 "%s" 或 "%n" 等格式符,会导致崩溃或内存泄露 + printf(user_input); +} + +int main(int argc, char *argv[]) { + char local_buf[100]; + + // 6. 使用了被废弃且极其危险的函数 (Use of Deprecated/Unsafe Function) + // gets() 不检查缓冲区长度,是导致缓冲区溢出的头号杀手 + printf("Enter specific text: "); + gets(local_buf); + + if (argc > 1) { + risky_processing(argv[1]); + check_admin(argv[1]); + } + + memory_issues(); + heap_corruption(); + + // 7. 逻辑错误/死代码 (Unreachable Code) + return 0; + + printf("This line will never run."); +} \ No newline at end of file