Skip to content

Add utility for detecting and converting full-width Unicode characters#1

Draft
Copilot wants to merge 2 commits into
masterfrom
copilot/add-authentication-endpoint
Draft

Add utility for detecting and converting full-width Unicode characters#1
Copilot wants to merge 2 commits into
masterfrom
copilot/add-authentication-endpoint

Conversation

Copy link
Copy Markdown

Copilot AI commented Feb 1, 2026

Full-width Unicode characters (U+FF01–FF5E) are visually identical to ASCII but cause syntax errors and enable homograph attacks. The problem statement demonstrates authentication code where app.get looks like app.get but won't execute.

Changes

  • FullWidthCharacterUtils.java: Core detection and conversion utility

    • isFullWidthCharacter(char) - detects full-width chars including ideographic space (U+3000)
    • containsFullWidthCharacters(String) - scans strings for presence
    • toAscii(char|String) - converts to ASCII equivalents via offset subtraction (code_point - 0xFEE0)
    • findFullWidthCharacterPositions(String) - returns indices of violations
    • createFullWidthReport(String) - generates diagnostic output with Unicode code points
  • FullWidthCharacterExample.java: Demonstrates conversion of the authentication endpoint code

  • FullWidthCharacterUtilsTest.java: 43 test cases covering detection, conversion, null handling, and mixed content

Example Usage

String malicious = "app.get('/auth')";
FullWidthCharacterUtils.containsFullWidthCharacters(malicious);  // true
FullWidthCharacterUtils.toAscii(malicious);  // "app.get('/auth')"

// Diagnostic report
String report = FullWidthCharacterUtils.createFullWidthReport("abc");
// Output:
// Found 2 full-width character(s):
//   Position 0: 'a' (U+FF41) -> 'a' (U+0061)
//   Position 1: 'b' (U+FF42) -> 'b' (U+0062)

Documentation in FULLWIDTH_CHARS_README.md covers security implications and technical details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • dl.google.com
    • Triggering command: /usr/lib/jvm/temurin-11-jdk-amd64/bin/java /usr/lib/jvm/temurin-11-jdk-amd64/bin/java -Dorg.gradle.appname=gradlew -classpath /home/REDACTED/work/TTAG/TTAG/gradle/wrapper/gradle-wrapper.jar org.gradle.wrapper.GradleWrapperMain --info -Dorg.gradle.configureondemand=false -Dorg.gradle.configuration-cache.problems=warn -Dorg.gradle.dependency.verification=off -Dorg.gradle.warning.mode=none --no-daemon --init-script /opt/hostedtoolcache/CodeQL/2.24.0/x64/codeql/java/tools/setup-proxy.gradle --init-script /opt/hostedtoolcache/CodeQL/2.24.0/x64/codeql/java/tools/dep-graph.gradle ForceDependencyResolutionPlugin_resolveAllDependencies --stacktrace -DGITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR=dep-graph -DGITHUB_DEPENDENCY_GRAPH_JOB_ID=REDACTED -DGITHUB_DEPENDENCY_GRAPH_SHA=REDACTED (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

app.get('/authenticate', async (req, res) => {
const token = req.query.token;
const tokenType = req.query.stytch_token_type;

// Handle Discovery authentication.
if (tokenType !== 'discovery') {
console.error(Unrecognized token type: '${tokenType}');
res.status(400).send();
return;
}

const authResp = await stytchClient.magicLinks.discovery.authenticate({
discovery_magic_links_token: token,
});
if (authResp.status_code !== 200) {
console.error('Authentication error')
res.status(500).send();
return;
}

// Sign into existing Organization if already Member
const ist = authResp.intermediate_session_token
if (authResp.discovered_organizations.length > 0 ) {
const exchangeResp = await stytchClient.discovery.intermediateSessions.exchange({
intermediate_session_token: ist,
organization_id: authResp.discovered_organizations[0].organization.organization_id,
});
if (exchangeResp.status_code !== 200) {
console.error(Error exchanging IST into Organization: ${JSON.stringify(exchangeResp, null, 2)});
res.status(500).send();
return;
}
// Store the returned session and return session member information
// Using express sessions with the const key of StytchSessionToken
req.session.StytchSessionToken = exchangeResp.session_token;
res.send(Hello, ${authResp.member.email_address}! You're logged into the ${authResp.organization.organization_name} organization);
return;
}

// If not eligible to log into an existing org, create new one
const createResp = await stytchClient.discovery.organizations.create({
intermediate_session_token: ist,
});
if (createResp.status_code !== 200) {
console.error(Error creating Organization: '${JSON.stringify(createResp, null, 2)}');
res.status(500).send();
return;
}
// Store the returned session and return session member information
req.session.StytchSessionToken = createResp.session_token;
res.send(Hello, ${createResp.member.email_address}! You're logged into the ${createResp.organization.organization_name} organization);
return;
});


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Co-authored-by: montinode <258148236+montinode@users.noreply.github.com>
Copilot AI changed the title [WIP] Add authentication endpoint for discovery token Add utility for detecting and converting full-width Unicode characters Feb 1, 2026
Copilot AI requested a review from montinode February 1, 2026 03:55
Copy link
Copy Markdown
Owner

@montinode montinode left a comment

Choose a reason for hiding this comment

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

𝖠𝗉𝗉𝗋𝗈𝗏𝖾𝖽 𝔟𝔶𝔪𝔬𝔫𝔱𝔦

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants