Files
document-haness/docs/clean-architecture-backend-template/final/evidence/rendered/a14-f002-webproblemsanitizer-alreadysafe-branch.svg
T
DongHyeonkaandClaude Opus 5 b2963105a8 docs(keycloak-session-store): import the session-storage lab as a new project
The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.

Follows the import procedure in README.md.

  source/     the originating repository verbatim — 78 documents, 28 SVGs,
              8 manifests, plus .source-revision recording the commit
  final/      the SSOT
    document.md   729 lines written from the 29 experiment documents, not
                  concatenated: what was predicted, what was measured, and
                  where the measurement itself was wrong
    evidence/raw    125 outputs, flattened to <experiment>__<file> because
                    the originals collided (01-baseline.txt appeared three
                    times) and the audit only globs the top level
    evidence/meta   one per raw file; command and exitCode are null and the
                    README says why rather than inventing them
    evidence/browser  22 captures
    assets/       three diagrams through techviz
    .techviz/     their VizSpecs

A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.

Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.

verify-pipeline.py passes. audit-records.py reports no issues.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 22:51:59 +09:00

147 lines
12 KiB
XML

<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1360" viewBox="0 0 1200 1360" role="img">
<title>terminal evidence</title>
<desc>Terminal-style rendering generated from retained command output. Sensitive-looking values are redacted in the visual asset.</desc>
<rect x="1" y="1" width="1198" height="1358" rx="14" fill="#0d1117" stroke="#30363d"/>
<rect x="1" y="1" width="1198" height="44" rx="14" fill="#161b22"/>
<rect x="1" y="30" width="1198" height="14" fill="#161b22"/>
<circle cx="24" cy="22" r="6" fill="#ff5f57"/>
<circle cx="44" cy="22" r="6" fill="#febc2e"/>
<circle cx="64" cy="22" r="6" fill="#28c840"/>
<text x="92" y="27" fill="#8b949e" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace">terminal evidence</text>
<text x="24" y="68" fill="#c9d1d9" font-size="15" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">$ set -e
set -o pipefail
D=$(mktemp -d); trap &#x27;rm -rf &quot;$D&quot;&#x27; EXIT
C=/shared/codebase/clean-architecture-backend-template
</tspan><tspan x="24" dy="22">W=$C/src/adapter/inbound/web
CACHE=/root/.gradle/caches/modules-2/files-2.1
java -version 2&gt;&amp;1 | head -1
cp=&quot;&quot;
while IFS</tspan><tspan x="24" dy="22">= read -r line; do
coord=${line%%=*}; confs=${line#*=}
case &quot;,$confs,&quot; in *&quot;,runtimeClasspath,&quot;*) ;; *) continue ;; </tspan><tspan x="24" dy="22">esac
g=${coord%%:*}; rest=${coord#*:}; n=${rest%%:*}; v=${rest##*:}
jar=$(find &quot;$CACHE/$g/$n/$v&quot; -name &#x27;*.jar&#x27; ! -na</tspan><tspan x="24" dy="22">me &#x27;*sources*&#x27; ! -name &#x27;*javadoc*&#x27; 2&gt;/dev/null | head -1)
[ -n &quot;$jar&quot; ] &amp;&amp; cp=&quot;$cp:$jar&quot;
done &lt; &lt;(grep -E &#x27;^[a-zA-Z0-9</tspan><tspan x="24" dy="22">._-]+:[^=]+=&#x27; &quot;$W/gradle.lockfile&quot;)
CP=$(find &quot;$W/build/libs&quot; -name &#x27;*+21234e38cdb9.jar&#x27; | tr &#x27;\n&#x27; &#x27;:&#x27;)${cp#:}
cat &gt; &quot;$D</tspan><tspan x="24" dy="22">/AlreadySafeProbe.java&quot; &lt;&lt;&#x27;JAVA&#x27;
import dev.caskeleton.adapter.inbound.web.error.WebProblemSanitizer;
import java.util.L</tspan><tspan x="24" dy="22">inkedHashMap;
import java.util.Locale;
import java.util.Map;
/** alreadySafe 의 삼항이 어느 가지를 타는지 입력마다 직접</tspan><tspan x="24" dy="22"> 본다. */
public final class AlreadySafeProbe {
/** 제어 문자를 소스에 직접 적지 않으려고 코드 점으로 만든다. */
pr</tspan><tspan x="24" dy="22">ivate static String ch(int code) {
return String.valueOf((char) code);
}
/** 메서드 본문의 삼항 조건 그대로. */</tspan><tspan x="24" dy="22">
private static boolean takesRedactedBranch(String input) {
return input.trim().toLowerCase(Locale.ROOT).isEmpty()</tspan><tspan x="24" dy="22">;
}
private static String show(String value) {
StringBuilder shown = new StringBuilder();
for (char each : </tspan><tspan x="24" dy="22">value.toCharArray()) {
shown.append(each &lt; 0x20 || each &gt; 0x7e ? String.format(&quot;U+%04X&quot;, (int) each) : each);
</tspan><tspan x="24" dy="22">}
return shown.toString();
}
/** 두 판정이 갈리는 코드 점을 전 범위에서 센다. 예시 몇 개로 &quot;&quot;을 말할 수 없다.</tspan><tspan x="24" dy="22"> */
private static void sweep(WebProblemSanitizer sanitizer) {
int gap = 0;
int returnsTrue = 0;
int first</tspan><tspan x="24" dy="22"> = -1;
int last = -1;
StringBuilder ranges = new StringBuilder();
boolean inRun = false;
for (int code =</tspan><tspan x="24" dy="22"> 0; code &lt;= 0xFFFF; code++) {
String value = ch(code);
boolean inGap = !value.isBlank() &amp;&amp; value.trim().isEm</tspan><tspan x="24" dy="22">pty();
if (inGap) {
gap++;
if (sanitizer.alreadySafe(value, 200)) {
returnsTrue++;
</tspan><tspan x="24" dy="22"> }
if (!inRun) {
first = code;
inRun = true;
}
last = code;
} else i</tspan><tspan x="24" dy="22">f (inRun) {
ranges.append(ranges.length() == 0 ? &quot;&quot; : &quot;, &quot;)
.append(String.format(&quot;U+%04X..U+%04X&quot;, </tspan><tspan x="24" dy="22">first, last));
inRun = false;
}
}
if (inRun) {
ranges.append(ranges.length() == 0 ? &quot;&quot; : &quot;, </tspan><tspan x="24" dy="22">&quot;)
.append(String.format(&quot;U+%04X..U+%04X&quot;, first, last));
}
System.out.println();
System.out.print</tspan><tspan x="24" dy="22">ln(&quot;[두 판정이 갈리는 코드 점 — 기본 다국어 평면 전체를 훑는다]&quot;);
System.out.printf(&quot; %-22s %d%n&quot;, &quot;공백은 아닌데</tspan><tspan x="24" dy="22"> 다듬기면 비는 것&quot;, gap);
System.out.printf(&quot; %-22s %s%n&quot;, &quot;그 구간&quot;, ranges);
System.out.printf(&quot; %-22s %d%n&quot;</tspan><tspan x="24" dy="22">, &quot;그중 alreadySafe 가 참&quot;, returnsTrue);
}
public static void main(String[] args) {
WebProblemSanitizer saniti</tspan><tspan x="24" dy="22">zer = new WebProblemSanitizer();
Map&lt;String, String&gt; inputs = new LinkedHashMap&lt;&gt;();
inputs.put(&quot;보통 문장&quot;, &quot;th</tspan><tspan x="24" dy="22">e request was rejected&quot;);
inputs.put(&quot;앞뒤 공백&quot;, &quot; spaced &quot;);
inputs.put(&quot;널 문자&quot;, ch(0));
inputs.put(&quot;</tspan><tspan x="24" dy="22">널 문자 셋&quot;, ch(0) + ch(0) + ch(0));
inputs.put(&quot;백스페이스&quot;, ch(8));
inputs.put(&quot;탈출 문자&quot;, ch(27));
input</tspan><tspan x="24" dy="22">s.put(&quot;삭제 문자&quot;, ch(127));
inputs.put(&quot;보통 공백만&quot;, &quot; &quot;);
inputs.put(&quot;줄 구분자&quot;, ch(0x2028));
inputs.p</tspan><tspan x="24" dy="22">ut(&quot;붙임 공백&quot;, ch(0x00A0));
System.out.println();
System.out.printf(&quot; %-14s %-14s %-9s %-11s %-14s %s%n&quot;,
</tspan><tspan x="24" dy="22"> &quot;입력&quot;, &quot;보인 값&quot;, &quot;isBlank&quot;, &quot;trim 이 빔&quot;, &quot;편집됨 가지&quot;, &quot;alreadySafe&quot;);
for (var entry : inputs.entrySet()) </tspan><tspan x="24" dy="22">{
String value = entry.getValue();
boolean blank = value.isBlank();
boolean trimmedEmpty = takesRedact</tspan><tspan x="24" dy="22">edBranch(value);
String branch = blank ? &quot;선행에서 끊김&quot; : (trimmedEmpty ? &quot;탄다&quot; : &quot;안 탄다&quot;);
System.out.p</tspan><tspan x="24" dy="22">rintf(&quot; %-14s %-14s %-9s %-11s %-14s %s%n&quot;,
entry.getKey(), show(value), blank, trimmedEmpty, branch,
</tspan><tspan x="24" dy="22"> sanitizer.alreadySafe(value, 200));
}
sweep(sanitizer);
}
}
JAVA
javac -encoding UTF-8 -cp &quot;$CP&quot; -d &quot;$D&quot; &quot;$</tspan><tspan x="24" dy="22">D/AlreadySafeProbe.java&quot;
java -Dstdout.encoding=UTF-8 -cp &quot;$CP:$D&quot; AlreadySafeProbe
</tspan></text>
<text x="24" y="860" fill="#8b949e" font-size="13" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">cwd: /shared/codebase/clean-architecture-backend-template/src</tspan></text>
<text x="24" y="882" fill="#8b949e" font-size="13" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">time: 2026-09-02T19:53:40+00:00 · exit 0</tspan></text>
<line x1="24" y1="896" x2="1176" y2="896" stroke="#30363d"/>
<text x="24" y="936" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">openjdk version &quot;21.0.12&quot; 2026-07-21</tspan></text>
<text x="24" y="958" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="980" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 입력 보인 값 isBlank trim 이 빔 편집됨 가지 alreadySafe</tspan></text>
<text x="24" y="1002" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 보통 문장 the request was rejected false false 안 탄다 true</tspan></text>
<text x="24" y="1024" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 앞뒤 공백 spaced false false 안 탄다 true</tspan></text>
<text x="24" y="1046" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 널 문자 U+0000 false true 탄다 true</tspan></text>
<text x="24" y="1068" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 널 문자 셋 U+0000U+0000U+0000 false true 탄다 true</tspan></text>
<text x="24" y="1090" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 백스페이스 U+0008 false true 탄다 true</tspan></text>
<text x="24" y="1112" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 탈출 문자 U+001B false true 탄다 true</tspan></text>
<text x="24" y="1134" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 삭제 문자 U+007F false false 안 탄다 false</tspan></text>
<text x="24" y="1156" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 보통 공백만 true true 선행에서 끊김 false</tspan></text>
<text x="24" y="1178" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 줄 구분자 U+2028 true false 선행에서 끊김 false</tspan></text>
<text x="24" y="1200" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 붙임 공백 U+00A0 false false 안 탄다 true</tspan></text>
<text x="24" y="1222" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="1244" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">[두 판정이 갈리는 코드 점 — 기본 다국어 평면 전체를 훑는다]</tspan></text>
<text x="24" y="1266" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 공백은 아닌데 다듬기면 비는 것 23</tspan></text>
<text x="24" y="1288" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 그 구간 U+0000..U+0008, U+000E..U+001B</tspan></text>
<text x="24" y="1310" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 그중 alreadySafe 가 참 23</tspan></text>
</svg>