From 7f1d9016d62779c677510a2510db3cf06246e17b Mon Sep 17 00:00:00 2001 From: papanda925 <78643310+papanda925@users.noreply.github.com> Date: Thu, 9 Sep 2021 23:08:33 +0900 Subject: [PATCH 1/3] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a6e6ae5..724f5d3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # MarkdownLocalWiki MarkDown記法が使える、ディスクトップ上で動くwiki -個人ブログで管理していたものをGitHubに展開 +個人ブログで管理していたものを今更ながらGitHubに展開したもの +今更ながらGitHubにも挑戦中 +ベース: http://kazu-s-diary-2.cocolog-nifty.com/blog/2016/07/htajquerymark-1.html ・サーバ不要、WindowsOSであれば利用可能 From 3c3fec4b6475eb662c82d3283ed49a25add409ad Mon Sep 17 00:00:00 2001 From: papanda925 <78643310+papanda925@users.noreply.github.com> Date: Thu, 9 Sep 2021 23:13:07 +0900 Subject: [PATCH 2/3] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 724f5d3..f77f1d4 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,12 @@ http://kazu-s-diary-2.cocolog-nifty.com/blog/2016/07/htajquerymark-1.html ・Markdown記法が使える。 - いまさらかもしれないが、以前から興味があったMarkdown記法の変換ルールを採用した。 - そのために以下のjsファイル+cssを移植。 + 使っているライブラリ + ・marked.js(一部改造) https://github.com/markedjs/marked - ・marked.js(一部改造) - ・github-markdown.css - ・jquery-1.12.0.js +・github-markdown.css  https://github.com/sindresorhus/github-markdown-css + +・jquery-1.12.0.js https://code.jquery.com/jquery/ ・テキストファイルの保存機能 From 52cc546c4d63324ec7f2cf834e5c695c6df81524 Mon Sep 17 00:00:00 2001 From: papanda925 Date: Wed, 2 Sep 2026 23:29:37 +0900 Subject: [PATCH 3/3] Document HTA risks and harden local page handling Expand the Japanese README, document the legacy HTA security model, validate page names, and improve save, rename, and delete behavior. GitHub Actions validation completed successfully. --- .github/workflows/validate.yml | 22 ++++++ README.md | 130 +++++++++++++++++++++++++++------ Tests/validate.py | 54 ++++++++++++++ index.hta | 56 +++++++++++--- 4 files changed, 228 insertions(+), 34 deletions(-) create mode 100644 .github/workflows/validate.yml create mode 100644 Tests/validate.py diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..fd4282a --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,22 @@ +name: Validate legacy HTA wiki + +on: + push: + pull_request: + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.13' + - uses: actions/setup-node@v4 + with: + node-version: '24' + - name: Validate HTA JavaScript and documentation + run: python Tests/validate.py diff --git a/README.md b/README.md index f77f1d4..11024e2 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,121 @@ # MarkdownLocalWiki -MarkDown記法が使える、ディスクトップ上で動くwiki -個人ブログで管理していたものを今更ながらGitHubに展開したもの -今更ながらGitHubにも挑戦中 -ベース: -http://kazu-s-diary-2.cocolog-nifty.com/blog/2016/07/htajquerymark-1.html +WindowsのHTML Application(HTA)として動作し、MarkdownファイルをローカルPC上で閲覧・編集する個人用Wikiです。Webサーバーやデータベースは不要で、Wikiページはリポジトリ直下の`.md`ファイルとして保存されます。 -・サーバ不要、WindowsOSであれば利用可能 +> [!CAUTION] +> このリポジトリはHTA/`mshta.exe`を使うレガシー版です。HTAは通常のWebページより強い権限でActiveXやローカルファイルを操作できます。組織のセキュリティ設定で実行を禁止されている場合があり、新規利用にはPowerShellローカルサーバー版の[`MarkDownPSLocalServerWiki`](https://github.com/papanda925/MarkDownPSLocalServerWiki)を推奨します。 -・Markdown記法が使える。 +## 主な機能 - 使っているライブラリ - ・marked.js(一部改造) https://github.com/markedjs/marked +- Markdownページの表示と編集 +- 新規ページ作成、名前変更、削除 +- ページ一覧 +- ファイル名と本文の文字列検索 +- 表示履歴 +- 文字サイズの拡大・縮小 +- HTTP/HTTPSリンクを外部ブラウザーで開く機能 +- WindowsのファイルパスやUNCパスを関連付けられたアプリで開く機能 +- ページを同じフォルダーの`.md`ファイルとして保存 -・github-markdown.css  https://github.com/sindresorhus/github-markdown-css +## 動作環境 -・jquery-1.12.0.js https://code.jquery.com/jquery/ - -・テキストファイルの保存機能 +- Windows +- `mshta.exe`/HTAの実行が許可されている環境 +- リポジトリのフォルダーへ書き込めること - marked.jsを修正し、リンクのルール「( aaaa )[ aaaa ] 」の中でURL等以外は、テキストファイル(aaaa.md)保存するように改造している。 +Edge、Chrome、Firefoxの拡張機能として動くものではありません。`index.hta`はWindowsのHTML Application Hostで実行されます。 -テキストファイル(aaaa.md)は、リンクのルール「( aaaa )[ aaaa ] 」によりファイルリンクされるようにしている。 +## 起動方法 -・テキストファイル以外は、ブラウザ等の外部アプリを利用するように修正。 +1. リポジトリをダウンロードまたはクローンします。 +2. `index.hta`、`core`フォルダー、`.md`ファイルを同じ構成のまま置きます。 +3. `index.hta`をダブルクリックします。 +4. 「トップページ」が表示されることを確認します。 -・いわゆるファイルサーバ(\\コンピュータ名)でもリンクされるように機能追加。 +実行前に`index.hta`の入手元と内容を確認してください。メールや不明なWebサイトから取得したHTAファイルは実行しないでください。 -追伸: -まだまだ改良の余地あるツールで、作者も勉強途中ですが。 -このツール(手法)に興味を持っていただき、さらに改良にチャレンジされた方がいらっしゃり、とてもうれしい。 -こちらにリンクを張っておきます。 +## ページデータ -https://github.com/ma34s/MarkdownLocalWiki +| 場所 | 内容 | +| --- | --- | +| `トップページ.md` | 最初に表示するページ | +| `*.md` | 作成したWikiページ | +| `index.hta` | 画面、Markdown処理、ファイル操作 | +| `core\marked.js` | MarkdownからHTMLへの変換 | +| `core\github-markdown.css` | Markdown表示用CSS | +| `core\jquery-1.12.4.js` | 同梱されている旧版jQuery | -https://qiita.com/hachimitu22/items/5bba11ab89b556b810b4 +バックアップする場合は、リポジトリ直下の`.md`ファイルをすべてコピーしてください。更新、名前変更、削除の前には別フォルダーへバックアップすることを推奨します。 + +## ページ名の扱い + +ページ名はWindowsのファイル名として使われます。現在のコードは次を拒否します。 + +- 空のページ名 +- `.`または`..` +- `\ / : * ? " < > |`など、Windowsのファイル名に使用できない文字 +- 制御文字 + +すべてのページパスは`index.hta`があるフォルダーを基準に組み立てます。これにより、新規作成、保存、名前変更、削除でWikiフォルダー外を直接指定できないようにしています。 + +## セキュリティ上の注意 + +- HTAは現在のブラウザーのサンドボックス内では動作しません。 +- `Scripting.FileSystemObject`でファイルを作成・変更・削除します。 +- `Shell.Application`でローカルファイルやフォルダーを開きます。 +- 信頼できないMarkdownには、HTMLや不正なリンクが含まれる可能性があります。 +- このWikiフォルダーへ、信頼できない`.hta`、JavaScript、Markdownを追加しないでください。 +- 組織で`mshta.exe`が禁止されている場合、設定を回避せずPowerShellローカルサーバー版を使用してください。 + +`mshta.exe`は正規のWindows機能ですが、任意のスクリプト実行に悪用される例があるため、セキュリティ製品や組織ポリシーで制限されることがあります。参考:[MITRE ATT&CK - Mshta](https://attack.mitre.org/techniques/T1218/005/) + +## PowerShellローカルサーバー版への移行 + +1. [`MarkDownPSLocalServerWiki`](https://github.com/papanda925/MarkDownPSLocalServerWiki)を別フォルダーへ準備します。 +2. HTA版を終了します。 +3. このリポジトリ直下の`.md`ファイルを、移行先の`doc`フォルダーへコピーします。 +4. `トップページ.md`が`doc`フォルダーにあることを確認します。 +5. 移行先の`startWiki.bat`または`StartWiki.ps1`を起動します。 +6. 表示、編集、検索、リンクを確認してから旧フォルダーを保管します。 + +最初は移動ではなくコピーにし、元データを残してください。 + +## 制限事項 + +- HTA/Internet Explorer系のMSHTML実行環境に依存 +- 現在のWeb標準やブラウザー機能を前提にしたコードではない +- 複数利用者、ログイン、権限管理、同時編集に非対応 +- 自動バックアップ、世代管理、ごみ箱はなし +- 同梱JavaScriptライブラリは旧版であり、無条件の最新版置換は互換性を壊す可能性あり +- Markdown内の信頼できないHTMLを安全に無害化する機能はなし + +## トラブルシューティング + +### `index.hta`を開けない + +組織ポリシー、EDR、ウイルス対策ソフト、ファイルの関連付けでHTAが禁止されている可能性があります。設定の回避は行わず、PowerShellローカルサーバー版への移行を検討してください。 + +### トップページが見つからない + +`index.hta`と同じフォルダーに`トップページ.md`があるか確認してください。 + +### 保存できない + +Wikiフォルダーへの書き込み権限を確認してください。読み取り専用の場所、ZIPファイル内、保護されたフォルダーでは保存できません。 + +### 文字化けする + +`index.hta`はShift_JISを前提としています。ソースやMarkdownの文字コードを変更する場合は、必ずコピーで試し、既存ページの表示と再保存を確認してください。 + +## 由来と関連プロジェクト + +このリポジトリは、個人ブログで公開していたHTA/jQuery/marked.jsによるMarkdown WikiをGitHubへ移したものです。 + +- [元になったブログ記事](http://kazu-s-diary-2.cocolog-nifty.com/blog/2016/07/htajquerymark-1.html) +- [ma34s/MarkdownLocalWiki](https://github.com/ma34s/MarkdownLocalWiki) +- [Qiitaでの関連紹介](https://qiita.com/hachimitu22/items/5bba11ab89b556b810b4) +- [後継:MarkDownPSLocalServerWiki](https://github.com/papanda925/MarkDownPSLocalServerWiki) + +## English summary + +Legacy Windows HTA-based personal Markdown wiki. It can read and write local files with the current user's permissions. New users should prefer the localhost-only PowerShell server successor. diff --git a/Tests/validate.py b/Tests/validate.py new file mode 100644 index 0000000..2f9f585 --- /dev/null +++ b/Tests/validate.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +import re +import subprocess +import tempfile +from pathlib import Path + + +repository_root = Path(__file__).resolve().parent.parent +hta_path = repository_root / "index.hta" +readme_path = repository_root / "README.md" + +hta = hta_path.read_text(encoding="cp932") +readme = readme_path.read_text(encoding="utf-8") + +inline_scripts = re.findall( + r"]*)?>(.*?)", + hta, + flags=re.IGNORECASE | re.DOTALL, +) +javascript = "\n".join(script for script in inline_scripts if script.strip()) + +with tempfile.NamedTemporaryFile("w", suffix=".js", encoding="utf-8", delete=False) as temp_file: + temp_file.write(javascript) + temp_path = Path(temp_file.name) + +try: + subprocess.run(["node", "--check", str(temp_path)], check=True) +finally: + temp_path.unlink(missing_ok=True) + +required_source_patterns = { + "page-name validation": r"function\s+validatePageName\s*\(", + "HTA-relative base folder": r"window\.location\.pathname", + "textarea value saving": r"var\s+content\s*=\s*id\('textarea'\)\.value", + "scoped current page": r"var\s+pagename\s*=\s*id\('headerH1'\)\.innerText", + "protected top page": r"pagename\s*===\s*'トップページ'", +} + +for label, pattern in required_source_patterns.items(): + if not re.search(pattern, hta): + raise AssertionError(f"Missing {label}: {pattern}") + +for heading in ( + "## 起動方法", + "## セキュリティ上の注意", + "## PowerShellローカルサーバー版への移行", + "## 制限事項", + "## トラブルシューティング", +): + if heading not in readme: + raise AssertionError(f"README is missing {heading}") + +print("HTA JavaScript and repository conventions validated.") diff --git a/index.hta b/index.hta index 3f99da5..0f93362 100644 --- a/index.hta +++ b/index.hta @@ -23,7 +23,24 @@ function smaller() { x /= 1.2; document.getElementById('content').style.fontSize //HTMLt@CuĂtH_̃pX擾 function getBaseFolder(){ - return fso.GetFolder("."); + var currentPath = decodeURI(window.location.pathname).replace(/\//g, "\\"); + if (/^\\[A-Za-z]:\\/.test(currentPath)) { + currentPath = currentPath.substring(1); + } + return fso.GetFolder(fso.GetParentFolderName(currentPath)); +} + +//y[Wt@CƂĈSɎg邩mFB +//WikitH_[Owu..vAWindowsŎgpłȂۂB +function validatePageName(pagename) { + var normalizedPageName = String(pagename || "").replace(/^\s+|\s+$/g, ""); + var invalidCharacters = /[\\\/:*?"<>|\x00-\x1F]/; + + if (!normalizedPageName || normalizedPageName === "." || normalizedPageName === ".." || invalidCharacters.test(normalizedPageName)) { + throw new Error("y[WɎgpłȂ܂܂Ă܂B"); + } + + return normalizedPageName; } @@ -188,16 +205,14 @@ function edit(pagename){ //܌Ăy[WҏW function editCurrentPage(){ - pagename = id('headerH1').innerText; + var pagename = id('headerH1').innerText; edit(pagename); } //tH[̃eLXgGA̒gAw肵y[W̃RecƂĕۑ function save(pagename){ -//ҏW@̏C textareaւ̊i[̓tH[textareaփeLXg -// var content = id('textarea').value; - var content = id('textarea').innerText; + var content = id('textarea').value; var filepath = getFilePath(pagename); var file = fso.createTextFile(filepath); @@ -258,7 +273,7 @@ function openIndexPage(){ //y[Ww肵āAY}[N_Ei.md)̃pX擾 function getFilePath(pagename){ - var filename = pagename + ".md"; + var filename = validatePageName(pagename) + ".md"; return oBaseFolder.Path + "\\" + filename; } @@ -386,18 +401,28 @@ function openAndSerch(pagename) { //VK쐬 function NewPage() { - var pagename = Text2.value; -// window.confirm(pagename); - if (pagename) { + try { + var pagename = validatePageName(Text2.value); open(pagename); } + catch (error) { + window.alert(error.message); + } Text2.value = ''; } //l[ function ReNamePage() { var srcPagename = getPageName(); - var dstPagename = Text2.value; + var dstPagename; + + try { + dstPagename = validatePageName(Text2.value); + } + catch (error) { + window.alert(error.message); + return; + } // uOKv̏Jn { mF_CAO̕\ if (window.confirm(dstPagename + ' Ƀl[܂H')) @@ -405,6 +430,11 @@ function ReNamePage() { var srcFilepath = getFilePath(srcPagename); var dstFilepath = getFilePath(dstPagename); + + if (fso.FileExists(dstFilepath)) { + window.alert(dstPagename + ' ͊ɑ݂܂B'); + return; + } fso.MoveFile(srcFilepath, dstFilepath); window.alert(srcPagename + ' ' + dstPagename + ' Ƀl[܂B'); @@ -421,10 +451,14 @@ function DeletePage() { var pagename = getPageName(); + if (pagename === 'gbvy[W') { + window.alert('gbvy[W͍폜ł܂B'); + return; + } + // uOKv̏Jn { mF_CAO̕\ if (window.confirm(pagename + ' 폜܂H')) { - var pagename = getPageName(); var filepath = getFilePath(pagename); fso.DeleteFile(filepath);