Skip to content

Instantly share code, notes, and snippets.

@kmpoppe
Last active March 19, 2023 07:35
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kmpoppe/52ff04186f52e20078f66da7e4d9ff6b to your computer and use it in GitHub Desktop.
Save kmpoppe/52ff04186f52e20078f66da7e4d9ff6b to your computer and use it in GitHub Desktop.
putty.vbs with 1Password support
' The aim of this script is to execute putty with parameters like ssh://login@host directly using an URL in a browser
'
' Update 2019-02-02, Kai Michael Poppe (twitter @kmpoppe):
' This script now also accepts item and vault data from 1password so that you can log in to putty without having
' username and password in the URL but getting it from your 1password vault. The script now makes use of a Json lib
' by "demon" (http://demon.tw/my-work/vbs-json.html)
'
' Installation:
' - Launch putty.reg to associate ssh:// and telnet:// to this script
' - Edit the putty path in parameter below like puttyPath = "C:\Program Files (x86)\putty.exe"
' - Save this .vbs on your PC like c:\putty.vbs (or change the path in putty.reg)
' If you want to use the script with 1password:
' - Get 1password CLI-tool from https://app-updates.agilebits.com/product_history/CLI
' - Read how to get a signin session (variable opSession below): https://support.1password.com/command-line/
' Uninstallation:
' - Run unputty.reg
' Version 2014-04-23'
' Script Created by Sebastien Biffi
'
' Source Original: https://gist.github.com/sbiffi
puttyPath = "C:\Program Files\putty\putty.exe"
opPath = "<PATH TO OP-CLI>\op.exe"
opSession = "<SESSION FROM OP SIGNIN>"
On Error Resume Next
' initialisation of variables
logResult = "" ' Used for logging purpose during debug or in case of error. Log is a msgBox
logIcon = 0 ' Icon of the MsgBox. By default: none
logTitle = "" ' Title of the MsgBox. By default: none
Set objShell = CreateObject("WScript.Shell")
Set re = New RegExp
' The pattern that matches the URL. Used to check the URL format provided and find fields in it.
re.Pattern = "^([^:]+)://(([^:]+)(:(.*))?@)?(([^/@:]+)(:([0-9]+))?)/?(\?onepasswdfill=(.*)\&onepasswdvault=(.*))?$"
' Check if only 1 parameter is passed to the script, else throw an error
if Wscript.Arguments.Count = 1 then
url = Wscript.Arguments(0)
else
exitWithError("Please pass only 1 parameter to this script like:" & vbCrLf & " - ssh://login:password@host:port " & vbCrLf & " - telnet://login@host:port" & vbCrLf & "login, password & port are optional")
end if
' Check if the URL is valid, else throw an error
If re.Test(url) Then
log("URL is valid: " & url)
Else
exitWithError(url & " is NOT a valid URL" & vbCrLf & "Please pass only 1 parameter to this script like:" & vbCrLf & " - ssh://login:password@host:port " & vbCrLf & " - telnet://login@host:port" & vbCrLf & "login, password & port are optional")
End If
' Find the putty parameters in the URL
Set Matches = re.Execute(url)
protocol = Matches.Item(0).Submatches(0)
login = Matches.Item(0).Submatches(2)
pwd = Matches.Item(0).Submatches(4)
host = Matches.Item(0).Submatches(6)
port = Matches.Item(0).Submatches(8)
' See if 1Password url parts are provided and replace user/password from the item
opwditem = Matches.Item(0).Submatches(10)
opwdvault = Matches.Item(0).Submatches(11)
If opwditem <> "" And opwdvault <> "" Then
Dim opExecStatement, strTempFileName, opJson
Set fso = CreateObject("Scripting.FileSystemObject")
strTempFileName = fso.GetSpecialFolder(2) & "\" & fso.GetTempName
opExecStatement = opPath & " get item " & opwditem & " --vault=" & opwdvault & " --session=" & opSession & " > " & strTempFileName
Set returnValue = objShell.Run("cmd /c " & opExecStatement, 0, true)
Set jsonFile = fso.OpenTextFile(strTempFileName, 1)
opJson = jsonFile.ReadAll
jsonFile.Close
fso.DeleteFile strTempFileName
Dim json, o, i
Set json = New VbsJson
Set o = json.Decode(opJson)
For Each i In o("details")("fields")
If i("designation") = "username" Then login = i("value")
If i("designation") = "password" Then pwd = i("value")
Next
End If
log(" host: " & host)
log(" protocol: " & protocol)
log(" port: " & port)
log(" login: " & login)
log(" pwd: " & pwd)
if protocol = "ssh" then
paramProtocol = " -ssh"
elseif protocol = "telnet" then
paramProtocol = " -telnet"
else
exitWithError("Protocol: " & protocol & " is not a valid protocol (ssh or telnet).")
end if
if host <> "" then
paramHost = " " & host
else
exitWithError("Host cannot bu null.")
end if
if login <> "" then
paramLogin = " -l " & login
end if
if (pwd <> "") and (protocol <> "telnet") then
paramPwd = " -pw " & pwd
end if
if port <> "" then
paramPort = " -P " & port
end if
' build the putty command like:
' putty.exe -ssh host -l login -pw password -P port
' putty.exe -telnet host -l login -P port
puttyCommand = chr(34) & puttyPath & chr(34) & paramProtocol & paramHost & paramLogin & paramPwd & paramPort
log (puttyCommand)
' For debugging purpose, uncomment following line:
'outputLog()
' Execute putty
objShell.Run puttyCommand,1,False
' ****************
' Error Output
' ****************
' Outputs a msgBox including the log and the error message
function exitWithError(str)
log(str)
logIcon = vbCritical
logTitle = "Error"
outputLog()
wscript.Quit
end function
' ***********
' Logging
' ***********
' Adds a message to the log String
function log(str)
if logResult = "" then
logResult = str
else
logResult = logResult & vbCrLf & str
end if
end function
' Outputs a msgBox including the log String
function outputLog()
if logResult <> "" then
MsgBox logResult, logIcon, logTitle
end if
end function
''' This class is imported here unchanged from the website mentioned below
Class VbsJson
'Author: Demon
'Date: 2012/5/3
'Website: http://demon.tw
Private Whitespace, NumberRegex, StringChunk
Private b, f, r, n, t
Private Sub Class_Initialize
Whitespace = " " & vbTab & vbCr & vbLf
b = ChrW(8)
f = vbFormFeed
r = vbCr
n = vbLf
t = vbTab
Set NumberRegex = New RegExp
NumberRegex.Pattern = "(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?"
NumberRegex.Global = False
NumberRegex.MultiLine = True
NumberRegex.IgnoreCase = True
Set StringChunk = New RegExp
StringChunk.Pattern = "([\s\S]*?)([""\\\x00-\x1f])"
StringChunk.Global = False
StringChunk.MultiLine = True
StringChunk.IgnoreCase = True
End Sub
'Return a JSON string representation of a VBScript data structure
'Supports the following objects and types
'+-------------------+---------------+
'| VBScript | JSON |
'+===================+===============+
'| Dictionary | object |
'+-------------------+---------------+
'| Array | array |
'+-------------------+---------------+
'| String | string |
'+-------------------+---------------+
'| Number | number |
'+-------------------+---------------+
'| True | true |
'+-------------------+---------------+
'| False | false |
'+-------------------+---------------+
'| Null | null |
'+-------------------+---------------+
Public Function Encode(ByRef obj)
Dim buf, i, c, g
Set buf = CreateObject("Scripting.Dictionary")
Select Case VarType(obj)
Case vbNull
buf.Add buf.Count, "null"
Case vbBoolean
If obj Then
buf.Add buf.Count, "true"
Else
buf.Add buf.Count, "false"
End If
Case vbInteger, vbLong, vbSingle, vbDouble
buf.Add buf.Count, obj
Case vbString
buf.Add buf.Count, """"
For i = 1 To Len(obj)
c = Mid(obj, i, 1)
Select Case c
Case """" buf.Add buf.Count, "\"""
Case "\" buf.Add buf.Count, "\\"
Case "/" buf.Add buf.Count, "/"
Case b buf.Add buf.Count, "\b"
Case f buf.Add buf.Count, "\f"
Case r buf.Add buf.Count, "\r"
Case n buf.Add buf.Count, "\n"
Case t buf.Add buf.Count, "\t"
Case Else
If AscW(c) >= 0 And AscW(c) <= 31 Then
c = Right("0" & Hex(AscW(c)), 2)
buf.Add buf.Count, "\u00" & c
Else
buf.Add buf.Count, c
End If
End Select
Next
buf.Add buf.Count, """"
Case vbArray + vbVariant
g = True
buf.Add buf.Count, "["
For Each i In obj
If g Then g = False Else buf.Add buf.Count, ","
buf.Add buf.Count, Encode(i)
Next
buf.Add buf.Count, "]"
Case vbObject
If TypeName(obj) = "Dictionary" Then
g = True
buf.Add buf.Count, "{"
For Each i In obj
If g Then g = False Else buf.Add buf.Count, ","
buf.Add buf.Count, """" & i & """" & ":" & Encode(obj(i))
Next
buf.Add buf.Count, "}"
Else
Err.Raise 8732,,"None dictionary object"
End If
Case Else
buf.Add buf.Count, """" & CStr(obj) & """"
End Select
Encode = Join(buf.Items, "")
End Function
'Return the VBScript representation of ``str(``
'Performs the following translations in decoding
'+---------------+-------------------+
'| JSON | VBScript |
'+===============+===================+
'| object | Dictionary |
'+---------------+-------------------+
'| array | Array |
'+---------------+-------------------+
'| string | String |
'+---------------+-------------------+
'| number | Double |
'+---------------+-------------------+
'| true | True |
'+---------------+-------------------+
'| false | False |
'+---------------+-------------------+
'| null | Null |
'+---------------+-------------------+
Public Function Decode(ByRef str)
Dim idx
idx = SkipWhitespace(str, 1)
If Mid(str, idx, 1) = "{" Then
Set Decode = ScanOnce(str, 1)
Else
Decode = ScanOnce(str, 1)
End If
End Function
Private Function ScanOnce(ByRef str, ByRef idx)
Dim c, ms
idx = SkipWhitespace(str, idx)
c = Mid(str, idx, 1)
If c = "{" Then
idx = idx + 1
Set ScanOnce = ParseObject(str, idx)
Exit Function
ElseIf c = "[" Then
idx = idx + 1
ScanOnce = ParseArray(str, idx)
Exit Function
ElseIf c = """" Then
idx = idx + 1
ScanOnce = ParseString(str, idx)
Exit Function
ElseIf c = "n" And StrComp("null", Mid(str, idx, 4)) = 0 Then
idx = idx + 4
ScanOnce = Null
Exit Function
ElseIf c = "t" And StrComp("true", Mid(str, idx, 4)) = 0 Then
idx = idx + 4
ScanOnce = True
Exit Function
ElseIf c = "f" And StrComp("false", Mid(str, idx, 5)) = 0 Then
idx = idx + 5
ScanOnce = False
Exit Function
End If
Set ms = NumberRegex.Execute(Mid(str, idx))
If ms.Count = 1 Then
idx = idx + ms(0).Length
ScanOnce = CDbl(ms(0))
Exit Function
End If
Err.Raise 8732,,"No JSON object could be ScanOnced"
End Function
Private Function ParseObject(ByRef str, ByRef idx)
Dim c, key, value
Set ParseObject = CreateObject("Scripting.Dictionary")
idx = SkipWhitespace(str, idx)
c = Mid(str, idx, 1)
If c = "}" Then
Exit Function
ElseIf c <> """" Then
Err.Raise 8732,,"Expecting property name"
End If
idx = idx + 1
Do
key = ParseString(str, idx)
idx = SkipWhitespace(str, idx)
If Mid(str, idx, 1) <> ":" Then
Err.Raise 8732,,"Expecting : delimiter"
End If
idx = SkipWhitespace(str, idx + 1)
If Mid(str, idx, 1) = "{" Then
Set value = ScanOnce(str, idx)
Else
value = ScanOnce(str, idx)
End If
ParseObject.Add key, value
idx = SkipWhitespace(str, idx)
c = Mid(str, idx, 1)
If c = "}" Then
Exit Do
ElseIf c <> "," Then
Err.Raise 8732,,"Expecting , delimiter"
End If
idx = SkipWhitespace(str, idx + 1)
c = Mid(str, idx, 1)
If c <> """" Then
Err.Raise 8732,,"Expecting property name"
End If
idx = idx + 1
Loop
idx = idx + 1
End Function
Private Function ParseArray(ByRef str, ByRef idx)
Dim c, values, value
Set values = CreateObject("Scripting.Dictionary")
idx = SkipWhitespace(str, idx)
c = Mid(str, idx, 1)
If c = "]" Then
ParseArray = values.Items
Exit Function
End If
Do
idx = SkipWhitespace(str, idx)
If Mid(str, idx, 1) = "{" Then
Set value = ScanOnce(str, idx)
Else
value = ScanOnce(str, idx)
End If
values.Add values.Count, value
idx = SkipWhitespace(str, idx)
c = Mid(str, idx, 1)
If c = "]" Then
Exit Do
ElseIf c <> "," Then
Err.Raise 8732,,"Expecting , delimiter"
End If
idx = idx + 1
Loop
idx = idx + 1
ParseArray = values.Items
End Function
Private Function ParseString(ByRef str, ByRef idx)
Dim chunks, content, terminator, ms, esc, char
Set chunks = CreateObject("Scripting.Dictionary")
Do
Set ms = StringChunk.Execute(Mid(str, idx))
If ms.Count = 0 Then
Err.Raise 8732,,"Unterminated string starting"
End If
content = ms(0).Submatches(0)
terminator = ms(0).Submatches(1)
If Len(content) > 0 Then
chunks.Add chunks.Count, content
End If
idx = idx + ms(0).Length
If terminator = """" Then
Exit Do
ElseIf terminator <> "\" Then
Err.Raise 8732,,"Invalid control character"
End If
esc = Mid(str, idx, 1)
If esc <> "u" Then
Select Case esc
Case """" char = """"
Case "\" char = "\"
Case "/" char = "/"
Case "b" char = b
Case "f" char = f
Case "n" char = n
Case "r" char = r
Case "t" char = t
Case Else Err.Raise 8732,,"Invalid escape"
End Select
idx = idx + 1
Else
char = ChrW("&H" & Mid(str, idx + 1, 4))
idx = idx + 5
End If
chunks.Add chunks.Count, char
Loop
ParseString = Join(chunks.Items, "")
End Function
Private Function SkipWhitespace(ByRef str, ByVal idx)
Do While idx <= Len(str) And _
InStr(Whitespace, Mid(str, idx, 1)) > 0
idx = idx + 1
Loop
SkipWhitespace = idx
End Function
End Class
@wqweto
Copy link

wqweto commented Nov 27, 2021

Might want to apply existing fixes to VbsJson from here.

@Lala1987guddu
Copy link

Hi experts nice to see , I am beginner , I want to use SSH protocol by application SecureCRT (which has more option compare to putty) , I badly needed this , Using windows 10 ... Many thanks in advanced

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