mirror of
https://github.com/imgfloat/server.git
synced 2026-02-05 03:39:26 +00:00
Sanitize log input
This commit is contained in:
@@ -103,6 +103,7 @@ public class SchemaMigration implements ApplicationRunner {
|
||||
return;
|
||||
}
|
||||
|
||||
// SECURITY: This is ok, because tableName and columnName are controlled internally and not from user input.
|
||||
try {
|
||||
jdbcTemplate.execute(
|
||||
"ALTER TABLE " + tableName + " ADD COLUMN " + columnName + " " + dataType + " DEFAULT " + defaultValue
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package dev.kruhlmann.imgfloat.config;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -16,19 +20,15 @@ import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepo
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
|
||||
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
|
||||
import org.springframework.security.web.csrf.CsrfToken;
|
||||
import org.springframework.security.web.csrf.CsrfException;
|
||||
import org.springframework.security.web.csrf.CsrfFilter;
|
||||
import org.springframework.security.web.csrf.CsrfToken;
|
||||
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
import org.springframework.web.util.WebUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@@ -85,10 +85,12 @@ public class SecurityConfig {
|
||||
)
|
||||
.logout((logout) -> logout.logoutSuccessUrl("/").permitAll())
|
||||
.exceptionHandling((exceptions) ->
|
||||
exceptions.defaultAuthenticationEntryPointFor(
|
||||
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),
|
||||
new AntPathRequestMatcher("/api/**")
|
||||
).accessDeniedHandler(csrfAccessDeniedHandler())
|
||||
exceptions
|
||||
.defaultAuthenticationEntryPointFor(
|
||||
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),
|
||||
new AntPathRequestMatcher("/api/**")
|
||||
)
|
||||
.accessDeniedHandler(csrfAccessDeniedHandler())
|
||||
)
|
||||
.csrf((csrf) ->
|
||||
csrf
|
||||
|
||||
@@ -124,7 +124,7 @@ public class ChannelApiController {
|
||||
LOG.warn(
|
||||
"No authorized Twitch client found for {} while fetching admin suggestions for {}",
|
||||
sessionUsername,
|
||||
broadcaster
|
||||
broadcaster.replaceAll("[\n\r]", "_")
|
||||
);
|
||||
return List.of();
|
||||
}
|
||||
@@ -185,7 +185,7 @@ public class ChannelApiController {
|
||||
authorizationService.userMatchesSessionUsernameOrThrowHttpError(broadcaster, sessionUsername);
|
||||
LOG.info(
|
||||
"Updating canvas for {} by {}: {}x{}",
|
||||
broadcaster,
|
||||
broadcaster.replaceAll("[\n\r]", "_"),
|
||||
sessionUsername,
|
||||
request.getWidth(),
|
||||
request.getHeight()
|
||||
@@ -276,7 +276,7 @@ public class ChannelApiController {
|
||||
LOG.info(
|
||||
"Updating visibility for asset {} on {} by {} to hidden={} ",
|
||||
assetId,
|
||||
broadcaster,
|
||||
broadcaster.replaceAll("[\n\r]", "_"),
|
||||
sessionUsername,
|
||||
request.isHidden()
|
||||
);
|
||||
@@ -284,7 +284,12 @@ public class ChannelApiController {
|
||||
.updateVisibility(broadcaster, assetId, request)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElseThrow(() -> {
|
||||
LOG.warn("Visibility update for missing asset {} on {} by {}", assetId, broadcaster, sessionUsername);
|
||||
LOG.warn(
|
||||
"Visibility update for missing asset {} on {} by {}",
|
||||
assetId.replaceAll("[\n\r]", "_"),
|
||||
broadcaster.replaceAll("[\n\r]", "_"),
|
||||
sessionUsername
|
||||
);
|
||||
return new ResponseStatusException(NOT_FOUND, "Asset not found");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -95,7 +95,11 @@ public class ViewController {
|
||||
broadcaster,
|
||||
sessionUsername
|
||||
);
|
||||
LOG.info("Rendering admin console for {} (requested by {})", broadcaster, sessionUsername);
|
||||
LOG.info(
|
||||
"Rendering admin console for {} (requested by {})",
|
||||
broadcaster.replaceAll("[\n\r]", "_"),
|
||||
sessionUsername
|
||||
);
|
||||
Settings settings = settingsService.get();
|
||||
model.addAttribute("broadcaster", broadcaster.toLowerCase());
|
||||
model.addAttribute("username", sessionUsername);
|
||||
|
||||
@@ -51,6 +51,7 @@ public class AuthorizationService {
|
||||
String broadcaster,
|
||||
String sessionUsername
|
||||
) {
|
||||
broadcaster = broadcaster.replaceAll("[\n\r]", "_");
|
||||
if (!userIsBroadcasterOrChannelAdminForBroadcaster(broadcaster, sessionUsername)) {
|
||||
LOG.warn(
|
||||
"Access denied for broadcaster/admin-only action by user {} on broadcaster {}",
|
||||
|
||||
@@ -129,11 +129,14 @@ public class ChannelDirectoryService {
|
||||
|
||||
public Optional<AssetView> createAsset(String broadcaster, MultipartFile file) throws IOException {
|
||||
long fileSize = file.getSize();
|
||||
long maxSize = uploadLimitBytes;
|
||||
if (fileSize > maxSize) {
|
||||
if (fileSize > uploadLimitBytes) {
|
||||
throw new ResponseStatusException(
|
||||
PAYLOAD_TOO_LARGE,
|
||||
String.format("Uploaded file is too large (%d bytes). Maximum allowed is %d bytes.", fileSize, maxSize)
|
||||
String.format(
|
||||
"Uploaded file is too large (%d bytes). Maximum allowed is %d bytes.",
|
||||
fileSize,
|
||||
uploadLimitBytes
|
||||
)
|
||||
);
|
||||
}
|
||||
Channel channel = getOrCreateChannel(broadcaster);
|
||||
|
||||
@@ -349,8 +349,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<script th:inline="javascript">
|
||||
const broadcaster = /*[[${broadcaster}]]*/ '';
|
||||
const username = /*[[${username}]]*/ '';
|
||||
const broadcaster = /*[[${broadcaster}]]*/ "";
|
||||
const username = /*[[${username}]]*/ "";
|
||||
const UPLOAD_LIMIT_BYTES = /*[[${uploadLimitBytes}]]*/ 0;
|
||||
const SETTINGS = JSON.parse(/*[[${settingsJson}]]*/);
|
||||
</script>
|
||||
|
||||
@@ -115,9 +115,9 @@ class ChannelApiIntegrationTest {
|
||||
|
||||
mockMvc
|
||||
.perform(
|
||||
delete("/api/channels/{broadcaster}/assets/{id}", broadcaster, assetId).with(
|
||||
oauth2Login().attributes((attrs) -> attrs.put("preferred_username", broadcaster))
|
||||
).with(csrf())
|
||||
delete("/api/channels/{broadcaster}/assets/{id}", broadcaster, assetId)
|
||||
.with(oauth2Login().attributes((attrs) -> attrs.put("preferred_username", broadcaster)))
|
||||
.with(csrf())
|
||||
)
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user