Using regex to validate email address in powershell - regex

So i made a script that parses a .msg file in outlook and ommits the result. The whole scripts works except for when I receive an email from inside the network (we use Active Directory) is when i get a result similar to this: /O=Business/OU=FIRST ADMINISTRATIVE GROUP/CN=RECIPIENTS/CN=MIKEF
otherwise for emails outside the network I get email#email.com. I would like to to validate this this with regex that way it would take the name in the CN=" " and adds it to my #email.com
$MSGFILEPATH = '\\srv01\FTP\EmailtoSupportPortal\Testing'
$MSGCOMPLETED= '\\srv01\FTP\EmailtoSupportPortal\Testing\Completed'
Function MSGFiles {
Get-ChildItem $MSGFILEPATH -Filter *.msg|`
ForEach-Object{
$outlook = New-Object -comobject outlook.application
$msg = $outlook.CreateItemFromTemplate($_.FullName)
$body = $msg.Body
$SEM = $msg.SenderEmailAddress
$Subject = $msg.Subject
$SEM
}
}
MSGFiles

Check the MailItem.SenderEmailType property. If it is "EX", use MailItem.Sender.GetExchangeUser.PrimarySmtpAddress (be prepared to handle nulls/errors).
Otherwise use the SenderEmailAddress property the way you do that now.

Related

How to remove specific text from extensionattribute

Task is to remove |text from an extension attribute but, but leave the rest of the text there.
I’ve tried the below but it does not remove it.
Set-ADUser -identity user -Clear #{extensionAttribute1="|text"}

Set-ADUser -identity user -Remove #{extensionAttribute1="|text"}

Set-ADUser -identity user -Replace #{extensionAttribute1='|text',''}
Current string have multiple | characters that needs to remain.
Example. Some|unrelated|text&whatever
Need to remove just |text
Remain: Some|unrelated&whatever
You need to get the old value first, manipulate it however you want, and then set it back on the object. Something like this should work:
$oldValue = (Get-ADUser -Identity user -Properties extensionAttribute1).extensionAttribute1
$newValue = $oldValue.Replace('|text', [string]::Empty)
Set-ADUser -Identity user -Replace #{extensionAttribute1=$newValue}

Mock Get-ADUser with and without ParameterFilter

I'm pretty new to Pester so please bear with me. We're trying to make tests for a very large script that does some active directory queries and validates data. I've simplified it a bit to the following example:
Dummy.ps1
Param (
[String]$OU,
[String[]]$Groups
)
$AllOUusers = Get-ADUser -Filter * -SearchBase $OU
$GroupMemberUsers = foreach ($G in $Groups) {
Get-ADGroupMember $G -Recursive | Get-ADUser -Properties whenCreated
}
Dummy.Tests.ps1
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
$Params = #{
OU = 'OU=users,DC=contoso,DC=com'
Groups = 'Group1', 'Group2'
}
Describe 'test' {
Mock Get-ADGroupMember {
[PSCustomObject]#{SamAccountName = 'Member1'}
[PSCustomObject]#{SamAccountName = 'Member2'}
}
Mock Get-ADUser {
[PSCustomObject]#{SamAccountName = 'User1'}
[PSCustomObject]#{SamAccountName = 'User2'}
[PSCustomObject]#{SamAccountName = 'User3'}
}
Mock Get-ADUser {
[PSCustomObject]#{SamAccountName = 'User4'}
[PSCustomObject]#{SamAccountName = 'User5'}
} -ParameterFilter {$identity -eq 'User1'}
."$here\$sut" #Params
it 'test 1' {
($AllOUusers | Measure-Object).Count | Should -BeExactly 3
}
it 'test 2' {
($GroupMemberUsers | Measure-Object).Count | Should -BeExactly 2
'User4', 'User5' | Should -BeIn $GroupMemberUsers.SamAccountName
}
}
Error:
Cannot validate argument on parameter 'Identity'.
In the case above we're trying to collect in the first place all user accounts in a specific OU and afterwards all the members of a specific security group. When we have the members we use the Get-ADUser CmdLet again to get more details for these specific members.
The end goal is to have a Mock for the first Get-ADUser that returns all the user accounts, and a second Mock of Get-ADUser that only returns a specific set of user accounts based on group membership (or simulated group membership).
System details
PowerShell 5.1
Pester 4.1.1.
Windows server 2012 R1
AD Module 1.0.0.0.
It seems like the error you're seeing is occurring because the users you create in your Mock of Get-ADGroupMember aren't being mapped to/accepted by the Identity parameter because it accepts pipeline input ByValue and expects an ADUser type object.
You can work around that by using New-MockObject to create the specific object type you need. Here's an alternative option for your Mock of Get-ADGroupMember:
Mock Get-ADGroupMember {
1..2 | ForEach-Object {
$User = New-MockObject -Type Microsoft.ActiveDirectory.Management.ADUser
$User.SamAccountName = "Member$_"
$User
}
}
I've used a ForEach as a quick way to return the 2 users you were returning before, with the value you were setting but as actual ADUser objects.
This doesn't actually make your -ParameterFilter on the second Get-ADUser work however. That seems to be again because the User is an object but you're comparing it to a string, it doesn't evaluate as true.
One possible workaround for this (that I think works somewhat for your specific use case) is not to check for a specific value of $Identity but just to check if it has any value:
Mock Get-ADUser {
[PSCustomObject]#{SamAccountName = 'User4'}
[PSCustomObject]#{SamAccountName = 'User5'}
} -ParameterFilter { $identity }
This causes your 2nd Mock to always be called for the 2nd Get-ADUser in your script as that's the only one that gets piped an Identity value. This isn't exactly what you were trying to achieve, but I figure might help a little.

Create custom POST-request body in PowerShell

I am running into a bit of trouble. I am trying to do a POST-request with PowerShell. The problem is that the request-body uses the same key (you can upload multiple images), multiple times, so I can't build a hashtable to send the request. So the requestbody looks like this:
name value
image 1.jpg
image 2.jpg
subject this is the subject
message this is a message
A user with a similar problem (but not the same context) asked this before, and got as a response to use a List with KeyValuePair class. See https://stackoverflow.com/a/5308691/4225082
I cannot seem to create this. I found this https://bensonxion.wordpress.com/2012/04/27/using-key-value-pairs-in-powershell-2/
They use $testDictionary=New-Object “System.Collections.Generic.Dictionary[[System.String],[System.String]]”
to make the dictionary, but this doesn't translate to a list.
I managed to create (what I think is needed) by using $r = New-Object "System.Collections.Generic.List[System.Collections.Generic.KeyvaluePair[string,string]]"
and created a key by using $s = New-Object “System.Collections.Generic.KeyvaluePair[string,string]", but I can't set the values of that key.
I also tried creating a FormObject, but you also can't use the same key multiple times.
What is the best and/or easiest way to do this?
I am going to answer my own question. Because of the research, I managed to use better search terms, and found someone with exactly the same problem:
Does Invoke-WebRequest support arrays as POST form parameters?
I got rid of a bug (?) by changing [HttpWebResponse] to [System.Net.HttpWebResponse] and added the -WebSession parameter. I only needed it for the cookie, so I implemented that and didn't bother about the other stuff, it might need some tweaking for someone else!
This seemed to work at first glance, BUT for elements with the same key, it created an array, which messed up the order of the requestbody. Without the right order, the website won't accept it.
I messed around a bit more, and now I edited it to make use of multidimensional arrays.
So I ended up with this (all credits to the original writer!):
function Invoke-WebRequestEdit
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true)][System.Uri] $Uri,
[Parameter(Mandatory=$false)][System.Object] $Body,
[Parameter(Mandatory=$false)][Microsoft.PowerShell.Commands.WebRequestMethod] $Method,
[Parameter(Mandatory=$false)][Microsoft.PowerShell.Commands.WebRequestSession] $WebSession
# Extend as necessary to match the signature of Invoke-WebRequest to fit your needs.
)
Process
{
# If not posting a NameValueCollection, just call the native Invoke-WebRequest.
if ($Body -eq $null -or $body.GetType().BaseType -ne [Array]) {
Invoke-WebRequest #PsBoundParameters
return;
}
$params = "";
$i = 0;
$j = $body.Count;
$first = $true;
foreach ($array in $body){
if (!$first) {
$params += "&";
} else {
$first = $false;
}
$params += [System.Web.HttpUtility]::UrlEncode($array[0]) + "=" + [System.Web.HttpUtility]::UrlEncode($array[1]);
}
$b = [System.Text.Encoding]::UTF8.GetBytes($params);
# Use HttpWebRequest instead of Invoke-WebRequest, because the latter doesn't support arrays in POST params.
$req = [System.Net.HttpWebRequest]::Create($Uri);
$req.Method = "POST";
$req.ContentLength = $params.Length;
$req.ContentType = "application/x-www-form-urlencoded";
$req.CookieContainer = $WebSession.Cookies
$str = $req.GetRequestStream();
$str.Write($b, 0, $b.Length);
$str.Close();
$str.Dispose();
[System.Net.HttpWebResponse] $res = $req.GetResponse();
$str = $res.GetResponseStream();
$rdr = New-Object -TypeName "System.IO.StreamReader" -ArgumentList ($str);
$content = $rdr.ReadToEnd();
$str.Close();
$str.Dispose();
$rdr.Dispose();
# Build a return object that's similar to a Microsoft.PowerShell.Commands.HtmlWebResponseObject
$ret = New-Object -TypeName "System.Object";
$ret | Add-Member -Type NoteProperty -Name "BaseResponse" -Value $res;
$ret | Add-Member -Type NoteProperty -Name "Content" -Value $content;
$ret | Add-Member -Type NoteProperty -Name "StatusCode" -Value ([int] $res.StatusCode);
$ret | Add-Member -Type NoteProperty -Name "StatusDescription" -Value $res.StatusDescription;
return $ret;
}
}
The $body parameter is made like this:
$form=#()
$form+= ,#("value1",'somevalue')
$form+=,#("value2", 'somevalue')
$form+=,#("value2",'somevalue')
$form+=,#("value3",'somevalue')
Everything looks good now. It still doesn't work, but my original version with unique keys also doesn't work anymore, so there's probably something else going wrong.

Powershell -replace regex not working on connection strings

I'm attempting to use the Powershell -replace command to update the data source in my config file. However, the -replace regex below will not remove the $oldServer value.
I've place a string directly in to the $_.connectionString variable in the loop and it saved properly, so I know that is not the issue. Seems to just be the regex.
#environment variables
$env = "DEV"
$oldServer = "quasq10"
$newValue = "$env-AR-SQL.CORP.COM"
$doc = [xml](Get-Content "D:\AMS\app.config")
$doc.configuration.connectionStrings.add|%{
$_.connectionString = $_.connectionString -replace $oldServer, $newValue;
}
$doc.Save($file.FullName)
EDIT
Per the comment below I added a Write-host $_.connectionString statement as the first line in the loop. Below is the console output
metadata=res:///MonetDb.csdl|res:///MonetDb.ssdl|res://*/MonetDb.msl;provider=System.Data.SqlClient;provider connection string="data source=quasq10\sql08a;initial catalog=MyDB
;integrated security=True;multipleactiveresultsets=True;App=EntityFramework"
I just put this right into ISE, I copied your connection string into a variable and was able to do this replace as a one off.
$connectionString = 'metadata=res:///MonetDb.csdl|res:///MonetDb.ssdl|res://*/MonetDb.msl;provider=System.Data.SqlClient;provider connection string="data source=quasq10\sql08a;initial catalog=MyDB ;integrated security=True;multipleactiveresultsets=True;App=EntityFramework"'
$env = "DEV"
$oldServer = "quasq10"
$newValue = "$env-AR-SQL.CORP.COM"
$connectionString -replace $oldServer, $newValue
res:///MonetDb.csdl|res:///MonetDb.ssdl|res://*/MonetDb.msl;provider=System.Data.SqlClient;provider connection string="data source=DEV-AR-SQL.CORP.COM\sql08a;initial catalog=MyDB ;integrated security=True;multipleactiveresultsets=True;App=EntityFramework"
I think your foreach loop might not be getting the info you want, because it looks like your replace is fine.
$doc.configuration.connectionStrings.add
I haven't done much with XML, does the XML data type have an ADD member function? You aren't really adding anything, right?
As a test, what do you get from this:
$doc.configuration.connectionStrings | % {
$_.connectionString -replace $oldServer, $newValue;
}
Run that against a dummy file and see what happens.
For a sanity check on the replace operator:
$string = "The quick brown fox jumped over the lazy dog"
$oldColor = "brown"
$newColor = "orange"
$string -replace $oldColor, $newColor
To avoid digging through comments, this method worked
$string.Replace($oldColor,$newColor)

Perl taint mode with domain name input for CGI resulting in “Insecure dependency in eval”

Given the following in a CGI script with Perl and taint mode I have not been able to get past the following.
tail /etc/httpd/logs/error_log
/usr/local/share/perl5/Net/DNS/Dig.pm line 906 (#1)
(F) You tried to do something that the tainting mechanism didn't like.
The tainting mechanism is turned on when you're running setuid or
setgid, or when you specify -T to turn it on explicitly. The
tainting mechanism labels all data that's derived directly or indirectly
from the user, who is considered to be unworthy of your trust. If any
such data is used in a "dangerous" operation, you get this error. See
perlsec for more information.
[Mon Jan 6 16:24:21 2014] dig.cgi: Insecure dependency in eval while running with -T switch at /usr/local/share/perl5/Net/DNS/Dig.pm line 906.
Code:
#!/usr/bin/perl -wT
use warnings;
use strict;
use IO::Socket::INET;
use Net::DNS::Dig;
use CGI;
$ENV{"PATH"} = ""; # Latest attempted fix
my $q = CGI->new;
my $domain = $q->param('domain');
if ( $domain =~ /(^\w+)\.(\w+\.?\w+\.?\w+)$/ ) {
$domain = "$1\.$2";
}
else {
warn("TAINTED DATA SENT BY $ENV{'REMOTE_ADDR'}: $domain: $!");
$domain = ""; # successful match did not occur
}
my $dig = new Net::DNS::Dig(
Timeout => 15, # default
Class => 'IN', # default
PeerAddr => $domain,
PeerPort => 53, # default
Proto => 'UDP', # default
Recursion => 1, # default
);
my #result = $dig->for( $domain, 'NS' )->to_text->rdata();
#result = sort #result;
print #result;
I normally use Data::Validate::Domain to do checking for a “valid” domain name, but could not deploy it in a way in which the tainted variable error would not occur.
I read that in order to untaint a variable you have to pass it through a regex with capture groups and then join the capture groups to sanitize it. So I deployed $domain =~ /(^\w+)\.(\w+\.?\w+\.?\w+)$/. As shown here it is not the best regex for the purpose of untainting a domain name and covering all possible domains but it meets my needs. Unfortunately my script is still producing tainted failures and I can not figure out how.
Regexp-Common does not provide a domain regex and modules don’t seem to work with untainting variable so I am at a loss now.
How to get this thing to pass taint checking?
$domain is not tainted
I verified that your $domain is not tainted. This is the only variable you use that could be tainted, in my opinion.
perl -T <(cat <<'EOF'
use Scalar::Util qw(tainted);
sub p_t($) {
if (tainted $_[0]) {
print "Tainted\n";
} else {
print "Not tainted\n";
}
}
my $domain = shift;
p_t($domain);
if ($domain =~ /(^\w+)\.(\w+\.?\w+\.?\w+)$/) {
$domain = "$1\.$2";
} else {
warn("$domain\n");
$domain = "";
}
p_t($domain);
EOF
) abc.def
It prints
Tainted
Not tainted
What Net::DNS::Dig does
See Net::DNS::Dig line 906. It is the beginning of to_text method.
sub to_text {
my $self = shift;
my $d = Data::Dumper->new([$self],['tobj']);
$d->Purity(1)->Deepcopy(1)->Indent(1);
my $tobj;
eval $d->Dump; # line 906
…
From new definition I know that $self is just hashref containing values from new parameters and several other filled in the constructor. The evaled code produced by $d->Dump is setting $tobj to a deep copy of $self (Deepcopy(1)), with correctly set self-references (Purity(1)) and basic pretty-printing (Indent(1)).
Where is the problem, how to debug
From what I found out about &Net::DNS::Dig::to_text, it is clear that the problem is at least one tainted item inside $self. So you have a straightforward way to debug your problem further: after constructing the $dig object in your script, check which of its items is tainted. You can dump the whole structure to stdout using print Data::Dumper::Dump($dig);, which is roughly the same as the evaled code, and check suspicious items using &Scalar::Util::tainted.
I have no idea how far this is from making Net::DNS::Dig work in taint mode. I do not use it, I was just curious and wanted to find out, where the problem is. As you managed to solve your problem otherwise, I leave it at this stage, allowing others to continue debugging the issue.
As resolution to this question if anyone comes across it in the future it was indeed the module I was using which caused the taint checks to fail. Teaching me an important lesson on trusting modules in a CGI environment. I switched to Net::DNS as I figured it would not encounter this issue and sure enough it does not. My code is provided below for reference in case anyone wants to accomplish the same thing I set out to do which is: locate the nameservers defined for a domain within its own zone file.
#!/usr/bin/perl -wT
use warnings;
use strict;
use IO::Socket::INET;
use Net::DNS;
use CGI;
$ENV{"PATH"} = ""; // Latest attempted fix
my $q = CGI->new;
my $domain = $q->param('domain');
my #result;
if ( $domain =~ /(^\w+)\.(\w+\.?\w+\.?\w+)$/ ) {
$domain = "$1\.$2";
}
else {
warn("TAINTED DATA SENT BY $ENV{'REMOTE_ADDR'}: $domain: $!");
$domain = ""; # successful match did not occur
}
my $ip = inet_ntoa(inet_aton($domain));
my $res = Net::DNS::Resolver->new(
nameservers => [($ip)],
);
my $query = $res->query($domain, "NS");
if ($query) {
foreach my $rr (grep { $_->type eq 'NS' } $query->answer) {
push(#result, $rr->nsdname);
}
}
else {
warn "query failed: ", $res->errorstring, "\n";
}
#result = sort #result;
print #result;
Thanks for the comments assisting me in this matter, and SO for teaching more then any other resource I have come across.