agent-messenger 2.34.0 → 2.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/dist/package.json +1 -1
- package/dist/src/platforms/kakaotalk/auth/kakao-login.d.ts +2 -0
- package/dist/src/platforms/kakaotalk/auth/kakao-login.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/auth/kakao-login.js +2 -2
- package/dist/src/platforms/kakaotalk/auth/kakao-login.js.map +1 -1
- package/dist/src/platforms/kakaotalk/auth/oauth-refresh.d.ts +28 -0
- package/dist/src/platforms/kakaotalk/auth/oauth-refresh.d.ts.map +1 -0
- package/dist/src/platforms/kakaotalk/auth/oauth-refresh.js +100 -0
- package/dist/src/platforms/kakaotalk/auth/oauth-refresh.js.map +1 -0
- package/dist/src/platforms/kakaotalk/client.d.ts +2 -0
- package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/client.js +14 -1
- package/dist/src/platforms/kakaotalk/client.js.map +1 -1
- package/dist/src/platforms/kakaotalk/index.d.ts +3 -1
- package/dist/src/platforms/kakaotalk/index.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/index.js +1 -0
- package/dist/src/platforms/kakaotalk/index.js.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/login-response.d.ts +9 -0
- package/dist/src/platforms/kakaotalk/protocol/login-response.d.ts.map +1 -0
- package/dist/src/platforms/kakaotalk/protocol/login-response.js +32 -0
- package/dist/src/platforms/kakaotalk/protocol/login-response.js.map +1 -0
- package/dist/src/platforms/kakaotalk/protocol/session.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/session.js +29 -20
- package/dist/src/platforms/kakaotalk/protocol/session.js.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/types.d.ts +1 -0
- package/dist/src/platforms/kakaotalk/protocol/types.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/types.js.map +1 -1
- package/dist/src/platforms/kakaotalk/types.d.ts +1 -0
- package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/types.js.map +1 -1
- package/dist/src/platforms/teams/client.d.ts +4 -4
- package/dist/src/platforms/teams/client.d.ts.map +1 -1
- package/dist/src/platforms/teams/client.js +14 -14
- package/dist/src/platforms/teams/client.js.map +1 -1
- package/dist/src/platforms/teams/commands/chat.d.ts +2 -0
- package/dist/src/platforms/teams/commands/chat.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/chat.js +7 -2
- package/dist/src/platforms/teams/commands/chat.js.map +1 -1
- package/dist/src/platforms/teams/commands/format.d.ts +3 -0
- package/dist/src/platforms/teams/commands/format.d.ts.map +1 -0
- package/dist/src/platforms/teams/commands/format.js +10 -0
- package/dist/src/platforms/teams/commands/format.js.map +1 -0
- package/dist/src/platforms/teams/commands/message.d.ts +1 -0
- package/dist/src/platforms/teams/commands/message.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/message.js +4 -1
- package/dist/src/platforms/teams/commands/message.js.map +1 -1
- package/dist/src/platforms/teams/html-sanitizer.d.ts +17 -0
- package/dist/src/platforms/teams/html-sanitizer.d.ts.map +1 -0
- package/dist/src/platforms/teams/html-sanitizer.js +120 -0
- package/dist/src/platforms/teams/html-sanitizer.js.map +1 -0
- package/dist/src/platforms/teams/index.d.ts +1 -1
- package/dist/src/platforms/teams/index.d.ts.map +1 -1
- package/dist/src/platforms/teams/index.js.map +1 -1
- package/dist/src/platforms/teams/types.d.ts +1 -0
- package/dist/src/platforms/teams/types.d.ts.map +1 -1
- package/dist/src/platforms/teams/types.js.map +1 -1
- package/dist/src/platforms/webex/client.d.ts.map +1 -1
- package/dist/src/platforms/webex/client.js +1 -1
- package/dist/src/platforms/webex/client.js.map +1 -1
- package/dist/src/shared/utils/markdown-to-html.d.ts.map +1 -0
- package/dist/src/{platforms/webex → shared/utils}/markdown-to-html.js +0 -0
- package/dist/src/shared/utils/markdown-to-html.js.map +1 -0
- package/docs/content/docs/cli/kakaotalk.mdx +10 -0
- package/docs/content/docs/sdk/kakaotalk.mdx +54 -0
- package/package.json +1 -1
- package/skills/agent-channeltalk/SKILL.md +1 -1
- package/skills/agent-channeltalkbot/SKILL.md +1 -1
- package/skills/agent-discord/SKILL.md +1 -1
- package/skills/agent-discordbot/SKILL.md +1 -1
- package/skills/agent-imessage/SKILL.md +1 -1
- package/skills/agent-instagram/SKILL.md +1 -1
- package/skills/agent-kakaotalk/SKILL.md +27 -1
- package/skills/agent-kakaotalk/references/authentication.md +59 -0
- package/skills/agent-kakaotalk/references/common-patterns.md +8 -0
- package/skills/agent-line/SKILL.md +1 -1
- package/skills/agent-slack/SKILL.md +1 -1
- package/skills/agent-slackbot/SKILL.md +1 -1
- package/skills/agent-teams/SKILL.md +44 -2
- package/skills/agent-teams/references/common-patterns.md +2 -2
- package/skills/agent-telegram/SKILL.md +1 -1
- package/skills/agent-telegrambot/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +1 -1
- package/skills/agent-webexbot/SKILL.md +1 -1
- package/skills/agent-wechatbot/SKILL.md +1 -1
- package/skills/agent-whatsapp/SKILL.md +1 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/platforms/kakaotalk/auth/kakao-login.ts +2 -2
- package/src/platforms/kakaotalk/auth/oauth-refresh.test.ts +225 -0
- package/src/platforms/kakaotalk/auth/oauth-refresh.ts +144 -0
- package/src/platforms/kakaotalk/client.test.ts +55 -0
- package/src/platforms/kakaotalk/client.ts +19 -2
- package/src/platforms/kakaotalk/index.test.ts +11 -0
- package/src/platforms/kakaotalk/index.ts +8 -0
- package/src/platforms/kakaotalk/protocol/NOTICE.md +19 -4
- package/src/platforms/kakaotalk/protocol/login-response.test.ts +64 -0
- package/src/platforms/kakaotalk/protocol/login-response.ts +40 -0
- package/src/platforms/kakaotalk/protocol/session.ts +29 -21
- package/src/platforms/kakaotalk/protocol/types.ts +1 -0
- package/src/platforms/kakaotalk/types.ts +1 -0
- package/src/platforms/teams/client.test.ts +127 -0
- package/src/platforms/teams/client.ts +27 -14
- package/src/platforms/teams/commands/chat.test.ts +70 -1
- package/src/platforms/teams/commands/chat.ts +15 -4
- package/src/platforms/teams/commands/format.ts +12 -0
- package/src/platforms/teams/commands/message.test.ts +56 -0
- package/src/platforms/teams/commands/message.ts +6 -2
- package/src/platforms/teams/html-sanitizer.test.ts +123 -0
- package/src/platforms/teams/html-sanitizer.ts +134 -0
- package/src/platforms/teams/index.ts +1 -0
- package/src/platforms/teams/types.ts +2 -0
- package/src/platforms/webex/client.ts +2 -1
- package/src/{platforms/webex → shared/utils}/markdown-to-html.test.ts +7 -0
- package/src/{platforms/webex → shared/utils}/markdown-to-html.ts +0 -0
- package/dist/src/platforms/webex/markdown-to-html.d.ts.map +0 -1
- package/dist/src/platforms/webex/markdown-to-html.js.map +0 -1
- /package/dist/src/{platforms/webex → shared/utils}/markdown-to-html.d.ts +0 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { sanitizeTeamsHtml } from './html-sanitizer'
|
|
4
|
+
|
|
5
|
+
describe('sanitizeTeamsHtml', () => {
|
|
6
|
+
it('keeps a Teams mention tag with its id attribute', () => {
|
|
7
|
+
expect(sanitizeTeamsHtml('Hey <at id="29:x">John</at>')).toBe('Hey <at id="29:x">John</at>')
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('keeps whitelisted formatting tags', () => {
|
|
11
|
+
expect(sanitizeTeamsHtml('<b>bold</b>')).toBe('<b>bold</b>')
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('escapes a disallowed tag with a dangerous attribute', () => {
|
|
15
|
+
expect(sanitizeTeamsHtml('<img src=x onerror=alert(1)>')).toBe('<img src=x onerror=alert(1)>')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('escapes script tags entirely', () => {
|
|
19
|
+
expect(sanitizeTeamsHtml('<script>alert(1)</script>')).toBe('<script>alert(1)</script>')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('keeps a whitelisted tag but drops its disallowed attribute', () => {
|
|
23
|
+
expect(sanitizeTeamsHtml('<b onclick="evil()">x</b>')).toBe('<b>x</b>')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('drops an unsafe href but keeps the anchor tag', () => {
|
|
27
|
+
expect(sanitizeTeamsHtml('<a href="javascript:alert(1)">x</a>')).toBe('<a>x</a>')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('escapes a bare < that is not part of a recognized tag', () => {
|
|
31
|
+
expect(sanitizeTeamsHtml('a < b')).toBe('a < b')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
describe('bypass attempts', () => {
|
|
35
|
+
it('blocks case-variant script tags', () => {
|
|
36
|
+
expect(sanitizeTeamsHtml('<ScRiPt>x</ScRiPt>')).toBe('<ScRiPt>x</ScRiPt>')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('recognizes a whitelisted tag regardless of case', () => {
|
|
40
|
+
expect(sanitizeTeamsHtml('<AT ID="x">John</AT>')).toBe('<at id="x">John</at>')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('blocks a tag name followed directly by non-whitespace garbage', () => {
|
|
44
|
+
const result = sanitizeTeamsHtml('<script/x>alert(1)</script>')
|
|
45
|
+
expect(result).not.toContain('<script')
|
|
46
|
+
expect(result).toContain('<script/x>')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('blocks a tag name followed by whitespace before the closing bracket', () => {
|
|
50
|
+
const result = sanitizeTeamsHtml('<script\n>alert(1)</script>')
|
|
51
|
+
expect(result).not.toContain('<script')
|
|
52
|
+
expect(result).toContain('<script')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('drops a quoted dangerous attribute alongside an allowed one', () => {
|
|
56
|
+
expect(sanitizeTeamsHtml('<at id="x" onload="y">John</at>')).toBe('<at id="x">John</at>')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('drops an unquoted dangerous attribute alongside an allowed one', () => {
|
|
60
|
+
expect(sanitizeTeamsHtml('<at id=x onerror=y>John</at>')).toBe('<at id="x">John</at>')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('never produces a live tag from nested/malformed markup', () => {
|
|
64
|
+
const result = sanitizeTeamsHtml('<<script>script>')
|
|
65
|
+
expect(result).not.toMatch(/<script[^>]*>/)
|
|
66
|
+
expect(result).toBe('<<script>script>')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('sanitizes a disallowed tag nested inside a whitelisted one', () => {
|
|
70
|
+
expect(sanitizeTeamsHtml('<b><script>alert(1)</script></b>')).toBe('<b><script>alert(1)</script></b>')
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('does not double-escape already-escaped content', () => {
|
|
74
|
+
expect(sanitizeTeamsHtml('<script>')).toBe('<script>')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('accepts void/self-closing br in any spelling', () => {
|
|
78
|
+
expect(sanitizeTeamsHtml('<br>')).toBe('<br>')
|
|
79
|
+
expect(sanitizeTeamsHtml('<br/>')).toBe('<br>')
|
|
80
|
+
expect(sanitizeTeamsHtml('<br />')).toBe('<br>')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('allows an https href', () => {
|
|
84
|
+
expect(sanitizeTeamsHtml('<a href="https://ok.com">link</a>')).toBe('<a href="https://ok.com">link</a>')
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('drops a javascript: href', () => {
|
|
88
|
+
expect(sanitizeTeamsHtml('<a href="javascript:alert(1)">link</a>')).toBe('<a>link</a>')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('drops a javascript: href even with leading whitespace before it', () => {
|
|
92
|
+
expect(sanitizeTeamsHtml('<a href=" javascript:alert(1)">link</a>')).toBe('<a>link</a>')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('escapes hyphenated custom elements instead of rewriting them to a shorter allowed tag', () => {
|
|
96
|
+
// Without a hyphen in the tag-name pattern, "<a-b>" matched as "<a>" plus
|
|
97
|
+
// leftover text, silently rewriting an unknown element into an anchor.
|
|
98
|
+
expect(sanitizeTeamsHtml('<a-b onclick="x">hi</a-b>')).toBe('<a-b onclick="x">hi</a-b>')
|
|
99
|
+
expect(sanitizeTeamsHtml('<my-widget>x</my-widget>')).toBe('<my-widget>x</my-widget>')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('escapes tag names that merely start with an allowed name', () => {
|
|
103
|
+
// A tag name must end at a real boundary. Otherwise "<at:id>" matched the
|
|
104
|
+
// "at" prefix and was promoted into a Teams mention, and "<b_extra>" into
|
|
105
|
+
// bold — arbitrary markup turning into formatting or a mention.
|
|
106
|
+
expect(sanitizeTeamsHtml('<at:id>x</at:id>')).toBe('<at:id>x</at:id>')
|
|
107
|
+
expect(sanitizeTeamsHtml('<b_extra>x</b_extra>')).toBe('<b_extra>x</b_extra>')
|
|
108
|
+
expect(sanitizeTeamsHtml('<b.bold>x</b.bold>')).toBe('<b.bold>x</b.bold>')
|
|
109
|
+
expect(sanitizeTeamsHtml('<a:link href="https://evil.com">x</a:link>')).toBe(
|
|
110
|
+
'<a:link href="https://evil.com">x</a:link>',
|
|
111
|
+
)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('ignores attribute names that merely end with an allowed name', () => {
|
|
115
|
+
// An attribute name must start at a real boundary. Otherwise "foo.id"
|
|
116
|
+
// was truncated to "id" and "foo.href" to "href", letting arbitrary
|
|
117
|
+
// attributes forge a mention or a link.
|
|
118
|
+
expect(sanitizeTeamsHtml('<at foo.id="29:evil">J</at>')).toBe('<at>J</at>')
|
|
119
|
+
expect(sanitizeTeamsHtml('<at x:id="29:evil">J</at>')).toBe('<at>J</at>')
|
|
120
|
+
expect(sanitizeTeamsHtml('<a foo.href="https://evil.com">x</a>')).toBe('<a>x</a>')
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
})
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whitelist-based sanitizer for `--format html` content.
|
|
3
|
+
*
|
|
4
|
+
* Kept local to the Teams platform (not `src/shared/utils/`) because `<at>`
|
|
5
|
+
* is a Teams-specific mention marker — sharing this out would leak
|
|
6
|
+
* Teams-only semantics into webex's transformer.
|
|
7
|
+
*
|
|
8
|
+
* This is a tag/attribute whitelist, not a full HTML parser: it recognizes
|
|
9
|
+
* `<name ...>` / `</name>` tokens with a regex, keeps the ones on the
|
|
10
|
+
* whitelist (stripping any attribute not explicitly allowed for that tag),
|
|
11
|
+
* and escapes everything else — including malformed or disallowed tags and
|
|
12
|
+
* any stray `<`/`>` that isn't part of a recognized tag. Text that has no
|
|
13
|
+
* literal `<`/`>` (e.g. already-escaped entities like `<script>`)
|
|
14
|
+
* passes through untouched, so content is never double-escaped.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const ALLOWED_TAGS = new Set([
|
|
18
|
+
'at',
|
|
19
|
+
'a',
|
|
20
|
+
'b',
|
|
21
|
+
'i',
|
|
22
|
+
'u',
|
|
23
|
+
's',
|
|
24
|
+
'strong',
|
|
25
|
+
'em',
|
|
26
|
+
'code',
|
|
27
|
+
'pre',
|
|
28
|
+
'br',
|
|
29
|
+
'p',
|
|
30
|
+
'ul',
|
|
31
|
+
'ol',
|
|
32
|
+
'li',
|
|
33
|
+
'blockquote',
|
|
34
|
+
'h1',
|
|
35
|
+
'h2',
|
|
36
|
+
'h3',
|
|
37
|
+
'h4',
|
|
38
|
+
'h5',
|
|
39
|
+
'h6',
|
|
40
|
+
'hr',
|
|
41
|
+
])
|
|
42
|
+
|
|
43
|
+
// Same rule as src/shared/utils/markdown-to-html.ts's SAFE_URL_PATTERN, kept
|
|
44
|
+
// as a separate copy here since this module intentionally does not import
|
|
45
|
+
// from the shared markdown transformer (see the file-level comment above).
|
|
46
|
+
const SAFE_URL_PATTERN = /^(https?:|mailto:|\/|#)/i
|
|
47
|
+
|
|
48
|
+
// Captures: 1) leading "/" for a closing tag, 2) the tag name, 3) everything
|
|
49
|
+
// else up to the closing ">" (attributes and/or a trailing self-closing
|
|
50
|
+
// "/"). Requires a letter immediately after "<" (or after "</"), so a bare
|
|
51
|
+
// "<" not followed by a tag name — e.g. the outer "<" in "<<script>" — is
|
|
52
|
+
// left as plain text instead of being absorbed into a match.
|
|
53
|
+
//
|
|
54
|
+
// The name must end at a real boundary — whitespace, "/" or ">" — so a name is
|
|
55
|
+
// only ever matched in full. Without that lookahead a tag whose name merely
|
|
56
|
+
// starts with an allowed one was promoted into it: "<at:id>" became a Teams
|
|
57
|
+
// mention and "<b_extra>" became bold. Requiring the boundary makes those fail
|
|
58
|
+
// the whitelist and get escaped like any other unknown tag.
|
|
59
|
+
const TAG_PATTERN = /<(\/)?([a-zA-Z][a-zA-Z0-9-]*)(?=[\s/>])([^<>]*)>/g
|
|
60
|
+
|
|
61
|
+
// The name must likewise start at a boundary — the beginning of the attribute
|
|
62
|
+
// section, whitespace, or the "/" of a self-closing tag. Scanning without one
|
|
63
|
+
// let a disallowed name be truncated to an allowed suffix, so `foo.id` was read
|
|
64
|
+
// as `id` and `foo.href` as `href`, forging mentions and links out of arbitrary
|
|
65
|
+
// attributes.
|
|
66
|
+
const ATTR_PATTERN = /(?<=^|[\s/])([a-zA-Z][a-zA-Z0-9-]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/g
|
|
67
|
+
|
|
68
|
+
export function sanitizeTeamsHtml(html: string): string {
|
|
69
|
+
let result = ''
|
|
70
|
+
let lastIndex = 0
|
|
71
|
+
TAG_PATTERN.lastIndex = 0
|
|
72
|
+
|
|
73
|
+
let match: RegExpExecArray | null
|
|
74
|
+
while ((match = TAG_PATTERN.exec(html)) !== null) {
|
|
75
|
+
result += escapeAngles(html.slice(lastIndex, match.index))
|
|
76
|
+
result += sanitizeTag(match)
|
|
77
|
+
lastIndex = TAG_PATTERN.lastIndex
|
|
78
|
+
}
|
|
79
|
+
result += escapeAngles(html.slice(lastIndex))
|
|
80
|
+
|
|
81
|
+
return result
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function sanitizeTag(match: RegExpExecArray): string {
|
|
85
|
+
const [fullMatch, closingSlash, rawName, rest] = match
|
|
86
|
+
const name = rawName.toLowerCase()
|
|
87
|
+
|
|
88
|
+
if (!ALLOWED_TAGS.has(name)) {
|
|
89
|
+
return escapeAngles(fullMatch)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (closingSlash) {
|
|
93
|
+
return `</${name}>`
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const attrs = parseAttributes(rest)
|
|
97
|
+
const kept: string[] = []
|
|
98
|
+
|
|
99
|
+
if (name === 'at') {
|
|
100
|
+
const id = attrs.get('id')
|
|
101
|
+
if (id !== undefined) kept.push(`id="${escapeAttributeValue(id)}"`)
|
|
102
|
+
} else if (name === 'a') {
|
|
103
|
+
const href = attrs.get('href')
|
|
104
|
+
if (href !== undefined && isSafeUrl(href)) kept.push(`href="${escapeAttributeValue(href)}"`)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return kept.length > 0 ? `<${name} ${kept.join(' ')}>` : `<${name}>`
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function parseAttributes(rest: string): Map<string, string> {
|
|
111
|
+
const attrs = new Map<string, string>()
|
|
112
|
+
ATTR_PATTERN.lastIndex = 0
|
|
113
|
+
|
|
114
|
+
let match: RegExpExecArray | null
|
|
115
|
+
while ((match = ATTR_PATTERN.exec(rest)) !== null) {
|
|
116
|
+
const name = match[1].toLowerCase()
|
|
117
|
+
const value = match[2] ?? match[3] ?? match[4] ?? ''
|
|
118
|
+
if (!attrs.has(name)) attrs.set(name, value)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return attrs
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function isSafeUrl(url: string): boolean {
|
|
125
|
+
return SAFE_URL_PATTERN.test(url.trim())
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function escapeAngles(text: string): string {
|
|
129
|
+
return text.replaceAll('<', '<').replaceAll('>', '>')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function escapeAttributeValue(value: string): string {
|
|
133
|
+
return value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"')
|
|
134
|
+
}
|
|
@@ -87,6 +87,8 @@ export type TeamsAccountType = 'work' | 'personal'
|
|
|
87
87
|
|
|
88
88
|
export type TeamsRegion = 'amer' | 'emea' | 'apac'
|
|
89
89
|
|
|
90
|
+
export type TeamsMessageFormat = 'text' | 'markdown' | 'html'
|
|
91
|
+
|
|
90
92
|
export type TeamsAuthMethod = 'device-code' | 'browser' | 'extracted' | 'manual'
|
|
91
93
|
|
|
92
94
|
export interface TeamsAccount {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto'
|
|
2
2
|
|
|
3
|
+
import { escapeHtml, markdownToHtml, stripMarkdown } from '@/shared/utils/markdown-to-html'
|
|
4
|
+
|
|
3
5
|
import { WebexCredentialManager } from './credential-manager'
|
|
4
6
|
import { WebexEncryptionService } from './encryption'
|
|
5
7
|
import type { WebexScr } from './encryption'
|
|
@@ -11,7 +13,6 @@ import {
|
|
|
11
13
|
toRestId,
|
|
12
14
|
} from './id-normalizer'
|
|
13
15
|
import { KmsKeyProvider } from './kms-key-provider'
|
|
14
|
-
import { escapeHtml, markdownToHtml, stripMarkdown } from './markdown-to-html'
|
|
15
16
|
import type { WebexConfig, WebexMembership, WebexMessage, WebexPerson, WebexSpace } from './types'
|
|
16
17
|
import { WebexError } from './types'
|
|
17
18
|
|
|
@@ -112,6 +112,13 @@ describe('markdownToHtml', () => {
|
|
|
112
112
|
it('preserves whitespace-only input', () => {
|
|
113
113
|
expect(markdownToHtml(' ')).toBe(' ')
|
|
114
114
|
})
|
|
115
|
+
|
|
116
|
+
it('does not clobber a fenced block with an inline placeholder on the same line', () => {
|
|
117
|
+
const result = markdownToHtml('Run `npm i` then ```\ncode here\n``` done')
|
|
118
|
+
|
|
119
|
+
expect(result).toContain('<code>npm i</code>')
|
|
120
|
+
expect(result).toContain('code here')
|
|
121
|
+
})
|
|
115
122
|
})
|
|
116
123
|
|
|
117
124
|
describe('stripMarkdown', () => {
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-to-html.d.ts","sourceRoot":"","sources":["../../../../src/platforms/webex/markdown-to-html.ts"],"names":[],"mappings":"AAGA,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgGvD;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgBtD;AAqED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOhD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-to-html.js","sourceRoot":"","sources":["../../../../src/platforms/webex/markdown-to-html.ts"],"names":[],"mappings":"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"}
|
|
File without changes
|