Last active
October 6, 2022 09:13
-
-
Save ShaunLawrie/de2815e17597ed27a2a93552e1f2cbf6 to your computer and use it in GitHub Desktop.
Quick script for extracting regex patterns from github codesearch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#requires -Version 7 | |
function Invoke-GithubCodeSearchLogin { | |
param ( | |
[Parameter(Mandatory)] | |
[string] $Username, | |
[Parameter(Mandatory)] | |
[securestring] $Password | |
) | |
Write-Verbose "This requires cookies across multiple domains so we're using a session like a web browser to keep state" | |
$session = [Microsoft.PowerShell.Commands.WebRequestSession]::new() | |
$response = Invoke-WebRequest -UseBasicParsing -Uri "https://cs.github.com/auth/login?redirect_url=%2F" -WebSession $session | |
Write-Verbose "Extract all the oauth state from the initial login page" | |
$authenticityToken = ($response.RawContent | Select-String 'authenticity_token" value="([^"]+)').Matches.Groups[1].Value | |
$returnTo = ($response.RawContent | Select-String 'return_to" value="([^"]+)').Matches.Groups[1].Value | |
$timestamp = ($response.RawContent | Select-String 'timestamp" value="([^"]+)').Matches.Groups[1].Value | |
$timestampSecret = ($response.RawContent | Select-String 'timestamp_secret" value="([^"]+)').Matches.Groups[1].Value | |
$requiredField = ($response.RawContent | Select-String 'required_field_([^"]+)').Matches.Groups[1].Value | |
Write-Verbose "Big login form let's go" | |
$sessionResponse = Invoke-WebRequest -UseBasicParsing -Uri "https://github.com/session" -WebSession $session -Method "POST" -Body @{ | |
"commit" = "Sign in" | |
"authenticity_token" = $authenticityToken | |
"login" = $Username | |
"password" = ($Password | ConvertFrom-SecureString -AsPlainText) | |
"webauthn-support" = "unsupported" | |
"webauthn-iuvpaa-support" = "unsupported" | |
"return_to" = $returnTo | |
"allow_signup" = "" | |
"client_id" = "" | |
"integration" = "" | |
"required_field_$requiredField" = "" | |
"timestamp" = $timestamp | |
"timestamp_secret" = $timestampSecret | |
} | |
Write-Verbose "Handle MFA for the basic MFA scenarios" | |
$redirectUrl = Get-GithubMfaRedirectUrl -Session $session -SessionResponse $sessionResponse | |
Write-Verbose "Complete login" | |
Invoke-WebRequest -UseBasicParsing -Uri $redirectUrl -WebSession $session -SkipHttpErrorCheck -ErrorAction "SilentlyContinue" | Out-Null | |
Write-Host "Logged in" | |
return $session | |
} | |
function Get-GithubMfaRedirectUrl { | |
param ( | |
[object] $Session, | |
[object] $SessionResponse | |
) | |
$sessionResponseContent = $SessionResponse.RawContent | |
if($sessionResponseContent.Contains("GitHub Mobile")) { | |
$mfaAuthenticityToken = ($sessionResponseContent | Select-String 'authenticity_token" value="([^"]+)').Matches.Groups[1].Value | |
$mfaCsrfToken = ($sessionResponseContent | Select-String 'value="(.+?)" data-csrf=').Matches.Groups[1].Value | |
$code = ($sessionResponseContent | Select-String '(?s)githubMobileChallengeValue.*?([0-9]+)').Matches.Groups[1].Value | |
if(!$code) { | |
Write-Error "Unable to extract the MFA prompt number from the page, try again" | |
exit 1 | |
} | |
Write-Host "Enter the code '$code' on your GitHub mobile app for MFA authentication" | |
$timeout = (Get-Date).AddSeconds(60) | |
$headers = @{ | |
"accept" = "application/json" | |
"scoped-csrf-token" = $mfaCsrfToken | |
"x-requested-with" = "XMLHttpRequest" | |
"accept-encoding" = "gzip, deflate, br" | |
"accept-language" = "en-US,en;q=0.9" | |
"origin" = "https://github.com" | |
"referer" = "https://github.com/sessions/two-factor/mobile?auto=true" | |
} | |
while($true) { | |
if((Get-Date) -gt $timeout) { | |
Write-Error "Timed out waiting for the mobile app to report MFA status" | |
exit 2 | |
} | |
try { | |
$mobileResponse = Invoke-RestMethod -UseBasicParsing -Uri "https://github.com/sessions/two-factor/mobile_poll" -WebSession $Session -Headers $headers -Method "POST" -Body @{ | |
"authenticity_token" = $mfaAuthenticityToken | |
} | |
if($mobileResponse.status -eq "STATUS_APPROVED") { | |
break | |
} | |
Write-Verbose "Mobile prompt status is '$($mobileResponse.status)'" | |
} catch { | |
Write-Warning "Failed to check mobile MFA status: $_" | |
} | |
Start-Sleep -Seconds 1 | |
} | |
$mfaResponse = Invoke-WebRequest "https://github.com/sessions/two-factor/mobile" -WebSession $Session -Headers @{ "accept" = "text/html" } -Body @{ | |
"auto" = "true" | |
"redirect" = "true" | |
} | |
$redirectResponse = $mfaResponse.RawContent | |
} elseif ($sessionResponseContent.Contains("Two-factor")) { | |
$mfaAuthenticityToken = ($sessionResponseContent | Select-String 'authenticity_token" value="([^"]+)').Matches.Groups[1].Value | |
$mfaCode = Read-Host "Enter your TOTP MFA token" | |
$mfaResponse = Invoke-WebRequest -UseBasicParsing -Uri "https://github.com/sessions/two-factor" -WebSession $Session -Method "POST" -Body @{ | |
"authenticity_token" = $mfaAuthenticityToken | |
"otp" = $mfaCode | |
} | |
$redirectResponse = $mfaResponse.RawContent | |
} else { | |
$redirectResponse = $sessionResponseContent | |
} | |
return ($redirectResponse | Select-String 'redirect" href="([^"]+)').Matches.Groups[1].Value -replace '&', '&' | |
} | |
function Get-GithubCodeSearch { | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory)] | |
[Microsoft.PowerShell.Commands.WebRequestSession] $Session, | |
[string] $SearchRegex, | |
[switch] $Pretty | |
) | |
if(!$SearchRegex) { | |
$SearchRegex = Read-Host "Enter a search term for GitHub CodeSearch" | |
} | |
$RawSearch = $SearchRegex | |
if($RawSearch.StartsWith("/") -and $RawSearch.EndsWith("/")) { | |
$SearchRegex = $RawSearch -replace '^\/', '' -replace '\/$', '' | |
} else { | |
$RawSearch = "/$RawSearch/" | |
} | |
if($Pretty) { | |
Write-Host -ForegroundColor "Green" -NoNewline "Searching for regex '" | |
Write-host -NoNewline "$RawSearch" | |
Write-Host -ForegroundColor "Green" "'`n" | |
} | |
$searchResponse = Invoke-RestMethod -UseBasicParsing -Uri "https://cs.github.com/api/search" -WebSession $Session -Body @{ q = $RawSearch } | |
for($p = 1; $p -le $searchResponse.total_pages; $p++) { | |
foreach($result in $searchResponse.results) { | |
$url = "https://github.com/$($result.repo_name)/tree/$($result.ref_name -replace 'refs/heads/', '')/$($result.path)" | |
if($Pretty) { | |
Write-Host -ForegroundColor DarkGreen "Location '$url'" | |
} | |
foreach($snippet in $result.snippets) { | |
$lines = $snippet.lines -join "`n" | |
try { | |
$marks = ($lines | Select-String -AllMatches $SearchRegex).Matches | Foreach-Object { $_.Value } | |
Write-Output ($marks -join "`n") | |
} catch { | |
Write-Warning $lines | |
} | |
} | |
} | |
$searchResponse = Invoke-RestMethod -UseBasicParsing -Uri "https://cs.github.com/api/search" -WebSession $Session -Body @{ q = $RawSearch; p = ($p + 1); page_token = $searchResponse.page_token } | |
} | |
} | |
<# | |
EXAMPLE | |
Import-Module .\GitHubCodeSearch.psm1 | |
$session = Invoke-GithubCodeSearchLogin | |
Get-GithubCodeSearch -Session $session -SearchRegex "https:\/\/example.+" | |
# To see the repos where the patterns are found use -Pretty | |
Get-GithubCodeSearch -Session $session -SearchRegex "firstname.+lastname" -Pretty | |
This script is super hacky I just wanted to find some stuff in cs.github.com with regex, 90% of it is dealing with the github login dance with MFA | |
#> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment