Skip to content

Commit

Permalink
Validate and escape all inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
z38 committed Mar 25, 2018
1 parent bf7da28 commit 0956b42
Show file tree
Hide file tree
Showing 13 changed files with 219 additions and 60 deletions.
6 changes: 4 additions & 2 deletions src/Z38/SwissPayment/FinancialInstitutionAddress.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ class FinancialInstitutionAddress implements FinancialInstitutionInterface
*
* @param string $name Name of the FI
* @param PostalAddressInterface Address of the FI
*
* @throws \InvalidArgumentException When the name is invalid.
*/
public function __construct($name, PostalAddressInterface $address)
{
$this->name = (string) $name;
$this->name = Text::assert($name, 70);
$this->address = $address;
}

Expand All @@ -35,7 +37,7 @@ public function __construct($name, PostalAddressInterface $address)
public function asDom(\DOMDocument $doc)
{
$xml = $doc->createElement('FinInstnId');
$xml->appendChild($doc->createElement('Nm', $this->name));
$xml->appendChild(Text::xml($doc, 'Nm', $this->name));
$xml->appendChild($this->address->asDom($doc));

return $xml;
Expand Down
11 changes: 2 additions & 9 deletions src/Z38/SwissPayment/GeneralAccount.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
*/
class GeneralAccount implements AccountInterface
{
const MAX_LENGTH = 34;

/**
* @var string
*/
Expand All @@ -26,12 +24,7 @@ class GeneralAccount implements AccountInterface
*/
public function __construct($id)
{
$stringId = (string) $id;
if (strlen($stringId) > self::MAX_LENGTH) {
throw new InvalidArgumentException('The account identifcation is too long.');
}

$this->id = $stringId;
$this->id = Text::assert($id, 34);
}

/**
Expand All @@ -49,7 +42,7 @@ public function asDom(DOMDocument $doc)
{
$root = $doc->createElement('Id');
$other = $doc->createElement('Othr');
$other->appendChild($doc->createElement('Id', $this->format()));
$other->appendChild(Text::xml($doc, 'Id', $this->format()));
$root->appendChild($other);

return $root;
Expand Down
6 changes: 4 additions & 2 deletions src/Z38/SwissPayment/Message/AbstractMessage.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Z38\SwissPayment\Message;

use Z38\SwissPayment\Text;

/**
* AbstractMessages eases message creation using DOM
*/
Expand Down Expand Up @@ -85,8 +87,8 @@ protected function buildContactDetails(\DOMDocument $doc)
{
$root = $doc->createElement('CtctDtls');

$root->appendChild($doc->createElement('Nm', $this->getSoftwareName()));
$root->appendChild($doc->createElement('Othr', $this->getSoftwareVersion()));
$root->appendChild(Text::xml($doc, 'Nm', $this->getSoftwareName()));
$root->appendChild(Text::xml($doc, 'Othr', $this->getSoftwareVersion()));

return $root;
}
Expand Down
17 changes: 10 additions & 7 deletions src/Z38/SwissPayment/Message/CustomerCreditTransfer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Z38\SwissPayment\Money;
use Z38\SwissPayment\PaymentInformation\PaymentInformation;
use Z38\SwissPayment\Text;

/**
* CustomerCreditTransfer represents a Customer Credit Transfer Initiation (pain.001) message
Expand Down Expand Up @@ -35,11 +36,13 @@ class CustomerCreditTransfer extends AbstractMessage
*
* @param string $id Identifier of the message (should usually be unique over a period of at least 90 days)
* @param string $initiatingParty Name of the initiating party
*
* @throws \InvalidArgumentException When any of the inputs contain invalid characters or are too long.
*/
public function __construct($id, $initiatingParty)
{
$this->id = (string) $id;
$this->initiatingParty = (string) $initiatingParty;
$this->id = Text::assertIdentifier($id);
$this->initiatingParty = Text::assert($initiatingParty, 70);
$this->payments = [];
$this->creationTime = new \DateTime();
}
Expand Down Expand Up @@ -104,12 +107,12 @@ protected function buildDom(\DOMDocument $doc)

$root = $doc->createElement('CstmrCdtTrfInitn');
$header = $doc->createElement('GrpHdr');
$header->appendChild($doc->createElement('MsgId', $this->id));
$header->appendChild($doc->createElement('CreDtTm', $this->creationTime->format('Y-m-d\TH:i:sP')));
$header->appendChild($doc->createElement('NbOfTxs', $transactionCount));
$header->appendChild($doc->createElement('CtrlSum', $transactionSum->format()));
$header->appendChild(Text::xml($doc, 'MsgId', $this->id));
$header->appendChild(Text::xml($doc, 'CreDtTm', $this->creationTime->format('Y-m-d\TH:i:sP')));
$header->appendChild(Text::xml($doc, 'NbOfTxs', $transactionCount));
$header->appendChild(Text::xml($doc, 'CtrlSum', $transactionSum->format()));
$initgParty = $doc->createElement('InitgPty');
$initgParty->appendChild($doc->createElement('Nm', $this->initiatingParty));
$initgParty->appendChild(Text::xml($doc, 'Nm', $this->initiatingParty));
$initgParty->appendChild($this->buildContactDetails($doc));
$header->appendChild($initgParty);
$root->appendChild($header);
Expand Down
11 changes: 7 additions & 4 deletions src/Z38/SwissPayment/PaymentInformation/PaymentInformation.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Z38\SwissPayment\IBAN;
use Z38\SwissPayment\IID;
use Z38\SwissPayment\Money;
use Z38\SwissPayment\Text;
use Z38\SwissPayment\TransactionInformation\CreditTransfer;

/**
Expand Down Expand Up @@ -71,18 +72,20 @@ class PaymentInformation
* @param string $debtorName Name of the debtor
* @param BIC|IID $debtorAgent BIC or IID of the debtor's financial institution
* @param IBAN $debtorIBAN IBAN of the debtor's account
*
* @throws \InvalidArgumentException When any of the inputs contain invalid characters or are too long.
*/
public function __construct($id, $debtorName, FinancialInstitutionInterface $debtorAgent, IBAN $debtorIBAN)
{
if (!$debtorAgent instanceof BIC && !$debtorAgent instanceof IID) {
throw new \InvalidArgumentException('The debtor agent must be an instance of BIC or IID.');
}

$this->id = (string) $id;
$this->id = Text::assertIdentifier($id);
$this->transactions = [];
$this->batchBooking = true;
$this->executionDate = new \DateTime();
$this->debtorName = (string) $debtorName;
$this->debtorName = Text::assert($debtorName, 70);
$this->debtorAgent = $debtorAgent;
$this->debtorIBAN = $debtorIBAN;
}
Expand Down Expand Up @@ -212,7 +215,7 @@ public function asDom(\DOMDocument $doc)
{
$root = $doc->createElement('PmtInf');

$root->appendChild($doc->createElement('PmtInfId', $this->id));
$root->appendChild(Text::xml($doc, 'PmtInfId', $this->id));
$root->appendChild($doc->createElement('PmtMtd', 'TRF'));
$root->appendChild($doc->createElement('BtchBookg', ($this->batchBooking ? 'true' : 'false')));

Expand Down Expand Up @@ -241,7 +244,7 @@ public function asDom(\DOMDocument $doc)
$root->appendChild($doc->createElement('ReqdExctnDt', $this->executionDate->format('Y-m-d')));

$debtor = $doc->createElement('Dbtr');
$debtor->appendChild($doc->createElement('Nm', $this->debtorName));
$debtor->appendChild(Text::xml($doc, 'Nm', $this->debtorName));
$root->appendChild($debtor);

$debtorAccount = $doc->createElement('DbtrAcct');
Expand Down
30 changes: 16 additions & 14 deletions src/Z38/SwissPayment/StructuredPostalAddress.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
class StructuredPostalAddress implements PostalAddressInterface
{
/**
* @var string
* @var string|null
*/
protected $street;

/**
* @var string
* @var string|null
*/
protected $buildingNo;

Expand All @@ -40,14 +40,16 @@ class StructuredPostalAddress implements PostalAddressInterface
* @param string $postCode Postal code
* @param string $town Town name
* @param string $country Country code (ISO 3166-1 alpha-2)
*
* @throws \InvalidArgumentException When the address contains invalid characters or is too long.
*/
public function __construct($street, $buildingNo, $postCode, $town, $country = 'CH')
{
$this->street = (string) $street;
$this->buildingNo = (string) $buildingNo;
$this->postCode = (string) $postCode;
$this->town = (string) $town;
$this->country = (string) $country;
$this->street = Text::assertOptional($street, 70);
$this->buildingNo = Text::assertOptional($buildingNo, 16);
$this->postCode = Text::assert($postCode, 16);
$this->town = Text::assert($town, 35);
$this->country = Text::assertCountryCode($country);
}

/**
Expand All @@ -57,15 +59,15 @@ public function asDom(\DOMDocument $doc)
{
$root = $doc->createElement('PstlAdr');

if (strlen($this->street)) {
$root->appendChild($doc->createElement('StrtNm', $this->street));
if ($this->street !== null) {
$root->appendChild(Text::xml($doc, 'StrtNm', $this->street));
}
if (strlen($this->buildingNo)) {
$root->appendChild($doc->createElement('BldgNb', $this->buildingNo));
if ($this->buildingNo !== null) {
$root->appendChild(Text::xml($doc, 'BldgNb', $this->buildingNo));
}
$root->appendChild($doc->createElement('PstCd', $this->postCode));
$root->appendChild($doc->createElement('TwnNm', $this->town));
$root->appendChild($doc->createElement('Ctry', $this->country));
$root->appendChild(Text::xml($doc, 'PstCd', $this->postCode));
$root->appendChild(Text::xml($doc, 'TwnNm', $this->town));
$root->appendChild(Text::xml($doc, 'Ctry', $this->country));

return $root;
}
Expand Down
69 changes: 69 additions & 0 deletions src/Z38/SwissPayment/Text.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

namespace Z38\SwissPayment;

use DOMDocument;
use InvalidArgumentException;

/**
* @internal
*/
class Text
{
const TEXT_CH = '/^[A-Za-z0-9 .,:\'\/()?+\-!"#%&*;<>÷=@_$£[\]{}\` ́~àáâäçèéêëìíîïñòóôöùúûüýßÀÁÂÄÇÈÉÊËÌÍÎÏÒÓÔÖÙÚÛÜÑ]*$/u';
const TEXT_SWIFT = '/^[A-Za-z0-9 .,:\'\/()?+\-]*$/';

public static function assertOptional($input, $maxLength)
{
if ($input === null) {
return null;
}

return self::assert($input, $maxLength);
}

public static function assert($input, $maxLength)
{
return self::assertPattern($input, $maxLength, self::TEXT_CH);
}

public static function assertIdentifier($input)
{
$input = self::assertPattern($input, 35, self::TEXT_SWIFT);
if ($input[0] === '/' || strpos($input, '//') !== false) {
throw new InvalidArgumentException('The identifier contains unallowed slashes.');
}

return $input;
}

public static function assertCountryCode($input)
{
if (!preg_match('/^[A-Z]{2}$/', $input)) {
throw new InvalidArgumentException('The country code is invalid.');
}

return $input;
}

protected static function assertPattern($input, $maxLength, $pattern)
{
$length = function_exists('mb_strlen') ? mb_strlen($input, 'UTF-8') : strlen($input);
if (!is_string($input) || $length === 0 || $length > $maxLength) {
throw new InvalidArgumentException(sprintf('The string can not be empty or longer than %d characters.', $maxLength));
}
if (!preg_match($pattern, $input)) {
throw new InvalidArgumentException('The string contains invalid characters.');
}

return $input;
}

public static function xml(DOMDocument $doc, $tag, $content)
{
$element = $doc->createElement($tag);
$element->appendChild($doc->createTextNode($content));

return $element;
}
}
21 changes: 13 additions & 8 deletions src/Z38/SwissPayment/TransactionInformation/CreditTransfer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Z38\SwissPayment\Money\Money;
use Z38\SwissPayment\PaymentInformation\PaymentInformation;
use Z38\SwissPayment\PostalAddressInterface;
use Z38\SwissPayment\Text;

/**
* CreditTransfer contains all the information about the beneficiary and further information about the transaction.
Expand Down Expand Up @@ -64,13 +65,15 @@ abstract class CreditTransfer
* @param Money $amount Amount of money to be transferred
* @param string $creditorName Name of the creditor
* @param PostalAddressInterface $creditorAddress Address of the creditor
*
* @throws \InvalidArgumentException When any of the inputs contain invalid characters or are too long.
*/
public function __construct($instructionId, $endToEndId, Money $amount, $creditorName, PostalAddressInterface $creditorAddress)
{
$this->instructionId = (string) $instructionId;
$this->endToEndId = (string) $endToEndId;
$this->instructionId = Text::assertIdentifier($instructionId);
$this->endToEndId = Text::assertIdentifier($endToEndId);
$this->amount = $amount;
$this->creditorName = (string) $creditorName;
$this->creditorName = Text::assert($creditorName, 70);
$this->creditorAddress = $creditorAddress;
}

Expand Down Expand Up @@ -114,10 +117,12 @@ public function setPurpose(PurposeCode $purpose)
* @param string|null $remittanceInformation
*
* @return CreditTransfer This credit transfer
*
* @throws \InvalidArgumentException When the information contains invalid characters or is too long.
*/
public function setRemittanceInformation($remittanceInformation)
{
$this->remittanceInformation = $remittanceInformation;
$this->remittanceInformation = Text::assertOptional($remittanceInformation, 140);

return $this;
}
Expand Down Expand Up @@ -155,8 +160,8 @@ protected function buildHeader(\DOMDocument $doc, PaymentInformation $paymentInf
$root = $doc->createElement('CdtTrfTxInf');

$id = $doc->createElement('PmtId');
$id->appendChild($doc->createElement('InstrId', $this->instructionId));
$id->appendChild($doc->createElement('EndToEndId', $this->endToEndId));
$id->appendChild(Text::xml($doc, 'InstrId', $this->instructionId));
$id->appendChild(Text::xml($doc, 'EndToEndId', $this->endToEndId));
$root->appendChild($id);

if (!$paymentInformation->hasPaymentTypeInformation() && ($this->localInstrument !== null || $this->serviceLevel !== null)) {
Expand Down Expand Up @@ -193,7 +198,7 @@ protected function buildHeader(\DOMDocument $doc, PaymentInformation $paymentInf
protected function buildCreditor(\DOMDocument $doc)
{
$creditor = $doc->createElement('Cdtr');
$creditor->appendChild($doc->createElement('Nm', $this->creditorName));
$creditor->appendChild(Text::xml($doc, 'Nm', $this->creditorName));
$creditor->appendChild($this->creditorAddress->asDom($doc));

return $creditor;
Expand Down Expand Up @@ -224,7 +229,7 @@ protected function appendRemittanceInformation(\DOMDocument $doc, \DOMElement $t
{
if (!empty($this->remittanceInformation)) {
$remittanceNode = $doc->createElement('RmtInf');
$remittanceNode->appendChild($doc->createElement('Ustrd', $this->remittanceInformation));
$remittanceNode->appendChild(Text::xml($doc, 'Ustrd', $this->remittanceInformation));
$transaction->appendChild($remittanceNode);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Z38\SwissPayment\PaymentInformation\PaymentInformation;
use Z38\SwissPayment\PostalAccount;
use Z38\SwissPayment\PostalAddressInterface;
use Z38\SwissPayment\Text;

/**
* IS2CreditTransfer contains all the information about a IS 2-stage (type 2.2) transaction.
Expand Down Expand Up @@ -51,7 +52,7 @@ public function __construct($instructionId, $endToEndId, Money\Money $amount, $c
parent::__construct($instructionId, $endToEndId, $amount, $creditorName, $creditorAddress);

$this->creditorIBAN = $creditorIBAN;
$this->creditorAgentName = (string) $creditorAgentName;
$this->creditorAgentName = Text::assert($creditorAgentName, 70);
$this->creditorAgentPostal = $creditorAgentPostal;
$this->localInstrument = 'CH03';
}
Expand All @@ -65,7 +66,7 @@ public function asDom(DOMDocument $doc, PaymentInformation $paymentInformation)

$creditorAgent = $doc->createElement('CdtrAgt');
$creditorAgentId = $doc->createElement('FinInstnId');
$creditorAgentId->appendChild($doc->createElement('Nm', $this->creditorAgentName));
$creditorAgentId->appendChild(Text::xml($doc, 'Nm', $this->creditorAgentName));
$creditorAgentIdOther = $doc->createElement('Othr');
$creditorAgentIdOther->appendChild($doc->createElement('Id', $this->creditorAgentPostal->format()));
$creditorAgentId->appendChild($creditorAgentIdOther);
Expand Down
Loading

0 comments on commit 0956b42

Please sign in to comment.